+
+ selectedDate={this.state.date}
+ theme={this.props.theme} />
-
- );
+
+ );
+ }
}
-}
-export default CalendarDialog;
+ return CalendarDialog;
+};
+
+export default factory;
diff --git a/components/date_picker/index.js b/components/date_picker/index.js
index 02d322e5e..53d6a11d0 100644
--- a/components/date_picker/index.js
+++ b/components/date_picker/index.js
@@ -1 +1,20 @@
export default from './DatePicker';
+
+import { themr } from 'react-css-themr';
+import { DATE_PICKER } from '../identifiers.js';
+import { datePickerFactory } from './DatePicker.js';
+import datePickerDialogFactory from './DatePickerDialog.js';
+import calendarFactory from './Calendar.js';
+
+import { IconButton } from '../button';
+import Input from '../input';
+import Dialog from '../dialog';
+import theme from './theme.scss';
+
+const Calendar = calendarFactory(IconButton);
+const DatePickerDialog = datePickerDialogFactory(Dialog, Calendar);
+const DatePicker = datePickerFactory(Input, DatePickerDialog);
+
+const ThemedDatePicker = themr(DATE_PICKER, theme)(DatePicker);
+export default ThemedDatePicker;
+export { ThemedDatePicker as DatePicker };
diff --git a/components/date_picker/readme.md b/components/date_picker/readme.md
index 96fb65eaa..3f84dc938 100644
--- a/components/date_picker/readme.md
+++ b/components/date_picker/readme.md
@@ -30,6 +30,8 @@ class DatePickerTest extends React.Component {
}
```
+If you want to provide a theme via context, the component key is `RTDatePicker`.
+
## Properties
| Name | Type | Default | Description|
@@ -43,3 +45,28 @@ class DatePickerTest extends React.Component {
| `minDate` | `Date` | | Date object with the minimum selectable date. |
| `onChange` | `Function` | | Callback called when the picker value is changed.|
| `value` | `Date` | | Date object with the currently selected date. |
+
+## Theme
+
+| Name | Description|
+|:---------|:-----------|
+| `active` | Used for the active day and year.|
+| `button` | Used for the buttons in the dialog.|
+| `calendar` | Used for the calendar root element.|
+| `calendarWrapper` | Used as wrapper for the calendar component inside dialog.|
+| `date` | Used for the date element inside header.|
+| `day` | Used for the day element inside the calendar.|
+| `days` | Used for the list of days inside a month.|
+| `dialog` | Used for the dialog component.|
+| `disabled` | Added to day element when day is disabled.|
+| `header` | Used for the dialog header,.|
+| `input` | Used for Input element that opens the picker.|
+| `month` | Used for the month root element.|
+| `monthsDisplay` | Added to the root dialog when months are displayed.|
+| `next` | Used for the next month icon.|
+| `prev` | Used for the prev month icon.|
+| `title` | Used for the month title element.|
+| `week` | Used for the weekdays wrapper.|
+| `year` | Used for the year element inside header.|
+| `years` | Used for the years list in years view.|
+| `yearsDisplay` | Added to the root dialog when years are displayed.|
diff --git a/components/date_picker/style.scss b/components/date_picker/style.scss
deleted file mode 100644
index e3fcb3461..000000000
--- a/components/date_picker/style.scss
+++ /dev/null
@@ -1,61 +0,0 @@
-@import "../base";
-@import "./config";
-
-.input > [role="input"] {
- cursor: pointer;
-}
-
-.header {
- padding: 1.6 * $unit 2 * $unit;
- color: $datepicker-primary-contrast-color;
- cursor: pointer;
- background-color: $datepicker-primary-color;
-}
-
-.year {
- display: inline-block;
- font-size: $datepicker-year-font-size;
- transition: opacity, font-size $animation-duration $animation-curve-default;
-}
-
-.date {
- display: block;
- font-weight: $font-weight-semi-bold;
- text-transform: capitalize;
- transition: opacity $animation-duration $animation-curve-default;
-}
-
-.wrapper {
- padding: $unit .5 * $unit 0;
-}
-
-.display-years {
- .date {
- opacity: $datepicker-inactive-opacity;
- }
- .year {
- font-size: $font-size-normal;
- }
-}
-
-.display-months {
- .year {
- opacity: $datepicker-inactive-opacity;
- }
-}
-
-.dialog {
- width: $datepicker-dialog-width;
- > [role="body"] {
- padding: 0;
- }
- > [role="navigation"] > .button {
- color: $datepicker-primary-color;
- &:hover {
- background: $datepicker-primary-hover-color;
- }
- &:focus:not(:active) {
- background: $datepicker-primary-hover-color;
- }
- }
-}
diff --git a/components/date_picker/style.calendar.scss b/components/date_picker/theme.scss
similarity index 59%
rename from components/date_picker/style.calendar.scss
rename to components/date_picker/theme.scss
index dfcea1dd6..e4dbbac76 100644
--- a/components/date_picker/style.calendar.scss
+++ b/components/date_picker/theme.scss
@@ -1,7 +1,68 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
-.root {
+.input > [role="input"] {
+ cursor: pointer;
+}
+
+.header {
+ padding: 1.6 * $unit 2 * $unit;
+ color: $datepicker-primary-contrast-color;
+ cursor: pointer;
+ background-color: $datepicker-primary-color;
+}
+
+.year {
+ display: inline-block;
+ font-size: $datepicker-year-font-size;
+ transition: opacity, font-size $animation-duration $animation-curve-default;
+}
+
+.date {
+ display: block;
+ font-weight: $font-weight-semi-bold;
+ text-transform: capitalize;
+ transition: opacity $animation-duration $animation-curve-default;
+}
+
+.calendarWrapper {
+ padding: $unit .5 * $unit 0;
+}
+
+.yearsDisplay {
+ .date {
+ opacity: $datepicker-inactive-opacity;
+ }
+ .year {
+ font-size: $font-size-normal;
+ }
+}
+
+.monthsDisplay {
+ .year {
+ opacity: $datepicker-inactive-opacity;
+ }
+}
+
+.dialog {
+ width: $datepicker-dialog-width;
+ > [role="body"] {
+ padding: 0;
+ }
+ > [role="navigation"] > .button {
+ color: $datepicker-primary-color;
+ &:hover {
+ background: $datepicker-primary-hover-color;
+ }
+ &:focus:not(:active) {
+ background: $datepicker-primary-hover-color;
+ }
+ }
+}
+
+.calendar {
position: relative;
height: $calendar-total-height;
overflow: hidden;
diff --git a/components/dialog/Dialog.js b/components/dialog/Dialog.js
index e92f828fd..449d9cff4 100644
--- a/components/dialog/Dialog.js
+++ b/components/dialog/Dialog.js
@@ -1,63 +1,79 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import ActivableRenderer from '../hoc/ActivableRenderer';
-import Button from '../button';
-import Overlay from '../overlay';
-import style from './style';
+import React, { PropTypes } from 'react';
+import { themr } from 'react-css-themr';
+import classnames from 'classnames';
+import { DIALOG } from '../identifiers.js';
+import ActivableRenderer from '../hoc/ActivableRenderer.js';
+import InjectButton from '../button/Button.js';
+import InjectOverlay from '../overlay/Overlay.js';
-const Dialog = (props) => {
- const actions = props.actions.map((action, idx) => {
- const className = ClassNames(style.button, {[action.className]: action.className});
- return
;
- });
+const factory = (Overlay, Button) => {
+ const Dialog = (props) => {
+ const actions = props.actions.map((action, idx) => {
+ const className = classnames(props.theme.button, {[action.className]: action.className});
+ return
;
+ });
- const className = ClassNames([ style.root, style[props.type] ], {
- [style.active]: props.active
- }, props.className);
+ const className = classnames([props.theme.dialog, props.theme[props.type]], {
+ [props.theme.active]: props.active
+ }, props.className);
- return (
-
-
-
- {props.title ? {props.title} : null}
- {props.children}
-
- {actions.length
- ?
- {actions}
-
- : null
- }
-
-
- );
-};
+ return (
+
+
+
+ {props.title ? {props.title} : null}
+ {props.children}
+
+ {actions.length
+ ?
+ {actions}
+
+ : null
+ }
+
+
+ );
+ };
-Dialog.propTypes = {
- actions: React.PropTypes.array,
- active: React.PropTypes.bool,
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- onEscKeyDown: React.PropTypes.func,
- onOverlayClick: React.PropTypes.func,
- onOverlayMouseDown: React.PropTypes.func,
- onOverlayMouseMove: React.PropTypes.func,
- onOverlayMouseUp: React.PropTypes.func,
- title: React.PropTypes.string,
- type: React.PropTypes.string
-};
+ Dialog.propTypes = {
+ actions: PropTypes.array,
+ active: PropTypes.bool,
+ children: PropTypes.node,
+ className: PropTypes.string,
+ onEscKeyDown: PropTypes.func,
+ onOverlayClick: PropTypes.func,
+ onOverlayMouseDown: PropTypes.func,
+ onOverlayMouseMove: PropTypes.func,
+ onOverlayMouseUp: PropTypes.func,
+ theme: PropTypes.shape({
+ active: PropTypes.string,
+ body: PropTypes.string,
+ button: PropTypes.string,
+ dialog: PropTypes.string,
+ navigation: PropTypes.string,
+ title: PropTypes.string
+ }),
+ title: PropTypes.string,
+ type: PropTypes.string
+ };
+
+ Dialog.defaultProps = {
+ actions: [],
+ active: false,
+ type: 'normal'
+ };
-Dialog.defaultProps = {
- actions: [],
- active: false,
- type: 'normal'
+ return ActivableRenderer()(Dialog);
};
-export default ActivableRenderer()(Dialog);
+const Dialog = factory(InjectOverlay, InjectButton);
+export default themr(DIALOG)(Dialog);
+export { Dialog };
+export { factory as dialogFactory };
diff --git a/components/dialog/index.js b/components/dialog/index.js
index 7faed16fa..1236aebb7 100644
--- a/components/dialog/index.js
+++ b/components/dialog/index.js
@@ -1 +1,12 @@
-export default from './Dialog';
+import { themr } from 'react-css-themr';
+import { DIALOG } from '../identifiers.js';
+import { dialogFactory } from './Dialog.js';
+import Overlay from '../overlay';
+import Button from '../button';
+import theme from './theme.scss';
+
+const Dialog = dialogFactory(Overlay, Button);
+const ThemedDialog = themr(DIALOG, theme)(Dialog);
+
+export default ThemedDialog;
+export { ThemedDialog as Dialog };
diff --git a/components/dialog/readme.md b/components/dialog/readme.md
index ebf3299b7..e7169935f 100644
--- a/components/dialog/readme.md
+++ b/components/dialog/readme.md
@@ -7,8 +7,8 @@
import Dialog from 'react-toolbox/lib/dialog';
class DialogTest extends React.Component {
- state = {
- active: false
+ state = {
+ active: false
};
handleToggle = () => {
@@ -39,6 +39,8 @@ class DialogTest extends React.Component {
}
```
+If you want to provide a theme via context, the component key is `RTDialog`.
+
## Properties
| Name | Type | Default | Description|
@@ -53,3 +55,14 @@ class DialogTest extends React.Component {
| `onOverlayMouseUp` | `Function` | | Callback called when the mouse button is released over the overlay. |
| `title` | `String` | | The text string to use as standar title of the dialog.|
| `type` | `String` | `normal` | Used to determine the size of the dialog. It can be `small`, `normal` or `large`. |
+
+## Theme
+
+| Name | Description|
+|:---------|:-----------|
+| `active` | Used for the root when the dialog is active.|
+| `body` | Used to wrap the dialog body.|
+| `button` | Used in buttons when the dialog implements actions.|
+| `dialog` | Used for the root element.|
+| `navigation` | Used for the navigation element when it implements actions.|
+| `title` | Used for the title element of the dialog.|
diff --git a/components/dialog/style.scss b/components/dialog/theme.scss
similarity index 90%
rename from components/dialog/style.scss
rename to components/dialog/theme.scss
index 194ee7714..62e7388db 100644
--- a/components/dialog/style.scss
+++ b/components/dialog/theme.scss
@@ -1,7 +1,9 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
-.root {
+.dialog {
display: flex;
max-width: 96vw;
max-height: 96vh;
@@ -23,21 +25,21 @@
.small {
width: 30vw;
-
+
@media screen and (max-width: $layout-breakpoint-sm-tablet) {
- width: 50vw;
+ width: 50vw;
}
-
+
@media screen and (max-width: $layout-breakpoint-xs) {
- width: 75vw;
+ width: 75vw;
}
}
.normal {
width: 50vw;
-
+
@media screen and (max-width: $layout-breakpoint-xs) {
- width: 96vw;
+ width: 96vw;
}
}
diff --git a/components/drawer/Drawer.js b/components/drawer/Drawer.js
index 76ddaecad..c2b3ae4a9 100644
--- a/components/drawer/Drawer.js
+++ b/components/drawer/Drawer.js
@@ -1,37 +1,52 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import ActivableRenderer from '../hoc/ActivableRenderer';
-import Overlay from '../overlay';
-import style from './style';
+import React, { PropTypes } from 'react';
+import { themr } from 'react-css-themr';
+import classnames from 'classnames';
+import { DRAWER } from '../identifiers.js';
+import ActivableRenderer from '../hoc/ActivableRenderer.js';
+import InjectOverlay from '../overlay/Overlay.js';
-const Drawer = (props) => {
- const className = ClassNames([style.root, style[props.type]], {
- [style.active]: props.active
- }, props.className);
+const factory = (Overlay) => {
+ const Drawer = ({ active, children, className, onOverlayClick, theme, type }) => {
+ const _className = classnames([theme.drawer, theme[type]], {
+ [theme.active]: active
+ }, className);
- return (
-
-
-
- );
-};
+ return (
+
+
+
+ );
+ };
-Drawer.propTypes = {
- active: React.PropTypes.bool,
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- onOverlayClick: React.PropTypes.func,
- type: React.PropTypes.oneOf(['left', 'right'])
-};
+ Drawer.propTypes = {
+ active: PropTypes.bool,
+ children: PropTypes.node,
+ className: PropTypes.string,
+ onOverlayClick: PropTypes.func,
+ theme: PropTypes.shape({
+ active: PropTypes.string,
+ content: PropTypes.string,
+ drawer: PropTypes.string,
+ left: PropTypes.string,
+ right: PropTypes.string
+ }),
+ type: PropTypes.oneOf(['left', 'right'])
+ };
+
+ Drawer.defaultProps = {
+ active: false,
+ className: '',
+ type: 'left'
+ };
-Drawer.defaultProps = {
- active: false,
- className: '',
- type: 'left'
+ return ActivableRenderer()(Drawer);
};
-export default ActivableRenderer()(Drawer);
+const Drawer = factory(InjectOverlay);
+export default themr(DRAWER)(Drawer);
+export { factory as drawerFactory };
+export { Drawer };
diff --git a/components/drawer/index.js b/components/drawer/index.js
index 717461790..8196b783c 100644
--- a/components/drawer/index.js
+++ b/components/drawer/index.js
@@ -1 +1,11 @@
-export default from './Drawer';
+import { themr } from 'react-css-themr';
+import { DRAWER } from '../identifiers.js';
+import { Overlay } from '../overlay';
+import { drawerFactory } from './Drawer.js';
+import theme from './theme.scss';
+
+const Drawer = drawerFactory(Overlay);
+const ThemedDrawer = themr(DRAWER, theme)(Drawer);
+
+export default ThemedDrawer;
+export { ThemedDrawer as Drawer };
diff --git a/components/drawer/readme.md b/components/drawer/readme.md
index 13dfd6e6f..fe28bb9f7 100644
--- a/components/drawer/readme.md
+++ b/components/drawer/readme.md
@@ -29,6 +29,8 @@ class DrawerTest extends React.Component {
}
```
+If you want to provide a theme via context, the component key is `RTDrawer`.
+
## Properties
| Name | Type | Default | Description|
@@ -37,3 +39,13 @@ class DrawerTest extends React.Component {
| `className` | `String` | `''` | Sets a class to give customized styles to the drawer.|
| `onOverlayClick` | `Function` | | Callback function to be invoked when the overlay is clicked.|
| `type` | `String` | `left` | Type of drawer. It can be `left` or `right` to display the drawer on the left or right side of the screen.|
+
+## Theme
+
+| Name | Description|
+|:---------|:-----------|
+| `active` | Used for the root class when the drawer is active.|
+| `content` | Used for the drawer content.|
+| `drawer` | Root class.|
+| `left` | Added to the root class when drawer is to the left.|
+| `right` | Added to the root class when drawer is to the right.|
diff --git a/components/drawer/style.scss b/components/drawer/theme.scss
similarity index 92%
rename from components/drawer/style.scss
rename to components/drawer/theme.scss
index e58ddff0f..a089640a3 100644
--- a/components/drawer/style.scss
+++ b/components/drawer/theme.scss
@@ -1,7 +1,9 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
-.root {
+.drawer {
@include shadow-2dp();
position: absolute;
top: 0;
diff --git a/components/dropdown/Dropdown.js b/components/dropdown/Dropdown.js
index c6cce59c7..086ffc541 100644
--- a/components/dropdown/Dropdown.js
+++ b/components/dropdown/Dropdown.js
@@ -1,149 +1,173 @@
-import React from 'react';
+import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
-import ClassNames from 'classnames';
-import Input from '../input';
-import events from '../utils/events';
-import style from './style';
-
-class Dropdown extends React.Component {
- static propTypes = {
- allowBlank: React.PropTypes.bool,
- auto: React.PropTypes.bool,
- className: React.PropTypes.string,
- disabled: React.PropTypes.bool,
- error: React.PropTypes.string,
- label: React.PropTypes.string,
- onBlur: React.PropTypes.func,
- onChange: React.PropTypes.func,
- onFocus: React.PropTypes.func,
- source: React.PropTypes.array.isRequired,
- template: React.PropTypes.func,
- value: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.number
- ])
- };
-
- static defaultProps = {
- auto: true,
- className: '',
- allowBlank: true,
- disabled: false
- };
-
- state = {
- active: false,
- up: false
- };
-
- componentWillUpdate (nextProps, nextState) {
- if (!this.state.active && nextState.active) {
- events.addEventsToDocument({click: this.handleDocumentClick});
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { DROPDOWN } from '../identifiers.js';
+import InjectInput from '../input/Input.js';
+import events from '../utils/events.js';
+
+const factory = (Input) => {
+ class Dropdown extends Component {
+ static propTypes = {
+ allowBlank: PropTypes.bool,
+ auto: PropTypes.bool,
+ className: PropTypes.string,
+ disabled: PropTypes.bool,
+ error: PropTypes.string,
+ label: PropTypes.string,
+ onBlur: PropTypes.func,
+ onChange: PropTypes.func,
+ onFocus: PropTypes.func,
+ source: PropTypes.array.isRequired,
+ template: PropTypes.func,
+ theme: PropTypes.shape({
+ active: PropTypes.string,
+ disabled: PropTypes.string,
+ dropdown: PropTypes.string,
+ error: PropTypes.string,
+ errored: PropTypes.string,
+ field: PropTypes.string,
+ label: PropTypes.string,
+ selected: PropTypes.string,
+ templateValue: PropTypes.string,
+ up: PropTypes.string,
+ value: PropTypes.string,
+ values: PropTypes.string
+ }),
+ value: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number
+ ])
+ };
+
+ static defaultProps = {
+ auto: true,
+ className: '',
+ allowBlank: true,
+ disabled: false
+ };
+
+ state = {
+ active: false,
+ up: false
+ };
+
+ componentWillUpdate (nextProps, nextState) {
+ if (!this.state.active && nextState.active) {
+ events.addEventsToDocument({click: this.handleDocumentClick});
+ }
}
- }
- componentDidUpdate (prevProps, prevState) {
- if (prevState.active && !this.state.active) {
- events.removeEventsFromDocument({click: this.handleDocumentClick});
+ componentDidUpdate (prevProps, prevState) {
+ if (prevState.active && !this.state.active) {
+ events.removeEventsFromDocument({click: this.handleDocumentClick});
+ }
}
- }
- componentWillUnmount () {
- if (this.state.active) {
- events.removeEventsFromDocument({click: this.handleDocumentClick});
+ componentWillUnmount () {
+ if (this.state.active) {
+ events.removeEventsFromDocument({click: this.handleDocumentClick});
+ }
}
- }
- close = () => {
- if (this.state.active) {
- this.setState({active: false});
+ close = () => {
+ if (this.state.active) {
+ this.setState({active: false});
+ }
}
- }
- handleDocumentClick = (event) => {
- if (this.state.active && !events.targetIsDescendant(event, ReactDOM.findDOMNode(this))) {
- this.setState({active: false});
- }
- };
-
- handleMouseDown = (event) => {
- events.pauseEvent(event);
- 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;
- if (this.props.onFocus) this.props.onFocus(event);
- this.setState({active: true, up});
- };
-
- handleSelect = (item, event) => {
- if (this.props.onBlur) this.props.onBlur(event);
- if (!this.props.disabled && this.props.onChange) {
- this.props.onChange(item, event);
- this.setState({active: false});
+ handleDocumentClick = (event) => {
+ if (this.state.active && !events.targetIsDescendant(event, ReactDOM.findDOMNode(this))) {
+ this.setState({active: false});
+ }
+ };
+
+ handleMouseDown = (event) => {
+ events.pauseEvent(event);
+ 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;
+ if (this.props.onFocus) this.props.onFocus(event);
+ this.setState({active: true, up});
+ };
+
+ handleSelect = (item, event) => {
+ if (this.props.onBlur) this.props.onBlur(event);
+ if (!this.props.disabled && this.props.onChange) {
+ this.props.onChange(item, event);
+ this.setState({active: false});
+ }
+ };
+
+ getSelectedItem = () => {
+ for (const item of this.props.source) {
+ if (item.value === this.props.value) return item;
+ }
+ if (!this.props.allowBlank) {
+ return this.props.source[0];
+ }
+ };
+
+ renderTemplateValue (selected) {
+ const { theme } = this.props;
+ const className = classnames(theme.field, {
+ [theme.errored]: this.props.error,
+ [theme.disabled]: this.props.disabled
+ });
+
+ return (
+
+
+ {this.props.template(selected)}
+
+ {this.props.label ?
{this.props.label} : null}
+ {this.props.error ?
{this.props.error} : null}
+
+ );
}
- };
- getSelectedItem = () => {
- for (const item of this.props.source) {
- if (item.value === this.props.value) return item;
+ renderValue (item, idx) {
+ const { theme } = this.props;
+ const className = item.value === this.props.value ? theme.selected : null;
+ return (
+
+ {this.props.template ? this.props.template(item) : item.label}
+
+ );
}
- if (!this.props.allowBlank) {
- return this.props.source[0];
- }
- };
-
- renderTemplateValue (selected) {
- const className = ClassNames(style.field, {
- [style.errored]: this.props.error,
- [style.disabled]: this.props.disabled
- });
-
- return (
-
-
- {this.props.template(selected)}
-
- {this.props.label ?
{this.props.label} : null}
- {this.props.error ?
{this.props.error} : null}
-
- );
- }
- renderValue (item, idx) {
- const className = item.value === this.props.value ? style.selected : null;
- return (
-
- {this.props.template ? this.props.template(item) : item.label}
-
- );
+ render () {
+ const {template, theme, source, ...others} = this.props;
+ const selected = this.getSelectedItem();
+ const className = classnames(theme.dropdown, {
+ [theme.up]: this.state.up,
+ [theme.active]: this.state.active,
+ [theme.disabled]: this.props.disabled
+ }, this.props.className);
+
+ return (
+
+
+ {template && selected ? this.renderTemplateValue(selected) : null}
+
+ {source.map(this.renderValue.bind(this))}
+
+
+ );
+ }
}
- render () {
- const {template, source, ...others} = this.props;
- const selected = this.getSelectedItem();
- const className = ClassNames(style.root, {
- [style.up]: this.state.up,
- [style.active]: this.state.active,
- [style.disabled]: this.props.disabled
- }, this.props.className);
-
- return (
-
-
- {template && selected ? this.renderTemplateValue(selected) : null}
-
- {source.map(this.renderValue.bind(this))}
-
-
- );
- }
-}
+ return Dropdown;
+};
-export default Dropdown;
+const Dropdown = factory(InjectInput);
+export default themr(DROPDOWN)(Dropdown);
+export { factory as dropdownFactory };
+export { Dropdown };
diff --git a/components/dropdown/index.js b/components/dropdown/index.js
index 2640e262c..056478aa5 100644
--- a/components/dropdown/index.js
+++ b/components/dropdown/index.js
@@ -1 +1,11 @@
-export default from './Dropdown';
+import { themr } from 'react-css-themr';
+import { DROPDOWN } from '../identifiers.js';
+import { dropdownFactory } from './Dropdown.js';
+import { Input } from '../input';
+import theme from './theme.scss';
+
+const Dropdown = dropdownFactory(Input);
+const ThemedDropdown = themr(DROPDOWN, theme)(Dropdown);
+
+export default ThemedDropdown;
+export { ThemedDropdown as Dropdown };
diff --git a/components/dropdown/readme.md b/components/dropdown/readme.md
index 64e3b2ee0..cb177a40c 100644
--- a/components/dropdown/readme.md
+++ b/components/dropdown/readme.md
@@ -14,9 +14,7 @@ const countries = [
];
class DropdownTest extends React.Component {
- state = {
- value: 'ES-es',
- };
+ state = { value: 'ES-es' };
handleChange = (value) => {
this.setState({value: value});
@@ -35,19 +33,38 @@ class DropdownTest extends React.Component {
}
```
+If you want to provide a theme via context, the component key is `RTDropdown`.
+
## Properties
-| Name | Type | Default | Description |
-|:-----|:-----|:-----|:-----|
-| `allowBlank` | `Boolean` | `true` | If true the dropdown will preselect the first item if the supplied value matches none of the options' values.|
-| `auto` | `Boolean` | `true` | If true, the dropdown will open up or down depending on the position in the screen.|
-| `className` | `String` | `''` | Set the class to give custom styles to the dropdown.|
-| `disabled` | `Boolean` | `false` | Set the component as disabled.|
-| `error` | `String` | | Give an error string to display under the field.|
-| `label` | `String` | | The text string to use for the floating label element.|
-| `onBlur` | `Function` | | Callback function that is fired when the component is blurred.|
-| `onChange` | `Function` | | Callback function that is fired when the component's value changes.|
-| `onFocus` | `Function` | | Callback function that is fired when the component is focused.|
-| `source` | `Array` | | Array of data objects with the data to represent in the dropdown.|
-| `template` | `Function` | | Callback function that returns a JSX template to represent the element.|
-| `value` | `String` | | Default value using JSON data.|
+| Name | Type | Default | Description |
+|:-------------|:-----------|:--------|:------------|
+| `allowBlank` | `Boolean` | `true` | If true the dropdown will preselect the first item if the supplied value matches none of the options' values.|
+| `auto` | `Boolean` | `true` | If true, the dropdown will open up or down depending on the position in the screen.|
+| `className` | `String` | `''` | Set the class to give custom styles to the dropdown.|
+| `disabled` | `Boolean` | `false` | Set the component as disabled.|
+| `error` | `String` | | Give an error string to display under the field.|
+| `label` | `String` | | The text string to use for the floating label element.|
+| `onBlur` | `Function` | | Callback function that is fired when the component is blurred.|
+| `onChange` | `Function` | | Callback function that is fired when the component's value changes.|
+| `onFocus` | `Function` | | Callback function that is fired when the component is focused.|
+| `source` | `Array` | | Array of data objects with the data to represent in the dropdown.|
+| `template` | `Function` | | Callback function that returns a JSX template to represent the element.|
+| `value` | `String` | | Default value using JSON data.|
+
+## Theming
+
+| Name | Description|
+|:----------------|:-----------|
+| `active` | Added to the root element when the dropdown is active.|
+| `disabled` | Added to the root element when it's disabled.|
+| `dropdown` | Root element class.|
+| `error` | Used for the error element.|
+| `errored` | Added to the inner wrapper if it's errored.|
+| `field` | Used for the inner wrapper of the component.|
+| `label` | Used for the the label element.|
+| `selected` | Used to highlight the selected value.|
+| `templateValue` | Used as a wrapper for the given template value.|
+| `up` | Added to the root element when it's opening up.|
+| `value` | Used for each value in the dropdown component.|
+| `values` | Used for the list of values.|
diff --git a/components/dropdown/style.scss b/components/dropdown/theme.scss
similarity index 97%
rename from components/dropdown/style.scss
rename to components/dropdown/theme.scss
index 6f0be32f6..a128b2cdc 100644
--- a/components/dropdown/style.scss
+++ b/components/dropdown/theme.scss
@@ -1,8 +1,10 @@
-@import "../base";
-@import "./config";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "../input/config";
+@import "./config";
-.root {
+.dropdown {
position: relative;
&:not(.active) {
> .values {
diff --git a/components/font_icon/FontIcon.js b/components/font_icon/FontIcon.js
index b68dc7ebb..8354ebb40 100644
--- a/components/font_icon/FontIcon.js
+++ b/components/font_icon/FontIcon.js
@@ -1,25 +1,23 @@
-import React from 'react';
-import ClassNames from 'classnames';
+import React, { PropTypes } from 'react';
+import classnames from 'classnames';
-const FontIcon = ({ children, className, value, ...other}) => {
- const classes = ClassNames(
- {'material-icons': typeof value === 'string'},
- className
- );
- return (
-
- {value}
- {children}
-
- );
-};
+const FontIcon = ({ children, className, value, ...other}) => (
+
+ {value}
+ {children}
+
+);
FontIcon.propTypes = {
- children: React.PropTypes.any,
- className: React.PropTypes.string,
- value: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.element
+ children: PropTypes.any,
+ className: PropTypes.string,
+ value: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.element
])
};
@@ -28,3 +26,4 @@ FontIcon.defaultProps = {
};
export default FontIcon;
+export { FontIcon };
diff --git a/components/font_icon/index.js b/components/font_icon/index.js
index 29e818f60..726aec89e 100644
--- a/components/font_icon/index.js
+++ b/components/font_icon/index.js
@@ -1 +1,4 @@
-export default from './FontIcon';
+import FontIcon from './FontIcon.js';
+
+export default FontIcon;
+export { FontIcon };
diff --git a/components/form/Form.js b/components/form/Form.js
index b7afcd83a..3f313399c 100644
--- a/components/form/Form.js
+++ b/components/form/Form.js
@@ -1,74 +1,86 @@
-import React from 'react';
-import Autocomplete from '../autocomplete';
-import Button from '../button';
-import Checkbox from '../checkbox';
-import DatePicker from '../date_picker';
-import Dropdown from '../dropdown';
-import Input from '../input';
-import RadioGroup from '../radio/RadioGroup';
-import Slider from '../slider';
-import Switch from '../switch';
-import TimePicker from '../time_picker';
-import style from './style';
+import React, { Component, PropTypes } from 'react';
+import InjectAutocomplete from '../autocomplete/Autocomplete.js';
+import InjectButton from '../button/Button.js';
+import InjectCheckbox from '../checkbox/Checkbox.js';
+import InjectDatePicker from '../date_picker/DatePicker.js';
+import InjectDropdown from '../dropdown/Dropdown.js';
+import InjectInput from '../input/Input.js';
+import InjectRadioGroup from '../radio/RadioGroup.js';
+import InjectSlider from '../slider/Slider.js';
+import InjectSwitch from '../switch/Switch.js';
+import InjectTimePicker from '../time_picker/TimePicker.js';
-const Component = {
- 'autocomplete': Autocomplete,
- 'button': Button,
- 'checkbox': Checkbox,
- 'datepicker': DatePicker,
- 'dropdown': Dropdown,
- 'input': Input,
- 'radioGroup': RadioGroup,
- 'slider': Slider,
- 'switch': Switch,
- 'timepicker': TimePicker
-};
+const factory = (
+ Autocomplete, Button, Checkbox, DatePicker, Dropdown,
+ Input, RadioGroup, Slider, Switch, TimePicker
+ ) => {
-class Form extends React.Component {
- static propTypes = {
- attributes: React.PropTypes.array,
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- model: React.PropTypes.object,
- onChange: React.PropTypes.func,
- onError: React.PropTypes.func,
- onSubmit: React.PropTypes.func,
- onValid: React.PropTypes.func,
- storage: React.PropTypes.string
+ const COMPONENTS = {
+ 'autocomplete': Autocomplete,
+ 'button': Button,
+ 'checkbox': Checkbox,
+ 'datepicker': DatePicker,
+ 'dropdown': Dropdown,
+ 'input': Input,
+ 'radioGroup': RadioGroup,
+ 'slider': Slider,
+ 'switch': Switch,
+ 'timepicker': TimePicker
};
- static defaultProps = {
- attributes: [],
- className: ''
- };
+ class Form extends Component {
+ static propTypes = {
+ attributes: PropTypes.array,
+ children: PropTypes.node,
+ className: PropTypes.string,
+ model: PropTypes.object,
+ onChange: PropTypes.func,
+ onError: PropTypes.func,
+ onSubmit: PropTypes.func,
+ onValid: PropTypes.func,
+ storage: PropTypes.string
+ };
- onSubmit = (event) => {
- event.preventDefault();
- if (this.props.onSubmit) this.props.onSubmit(event);
- };
+ static defaultProps = {
+ attributes: [],
+ className: ''
+ };
- onChange = (field, value, event) => {
- if (this.props.onChange) this.props.onChange(field, value, event);
- };
+ onSubmit = (event) => {
+ event.preventDefault();
+ if (this.props.onSubmit) this.props.onSubmit(event);
+ };
- renderFields () {
- return Object.keys(this.props.model).map((field, index) => {
- const properties = this.props.model[field];
- const Field = Component[properties.kind.toLowerCase()];
- return
;
- });
- }
+ onChange = (field, value, event) => {
+ if (this.props.onChange) this.props.onChange(field, value, event);
+ };
- render () {
- const className = `${style.root} ${this.props.className}`;
+ renderFields () {
+ return Object.keys(this.props.model).map((field, index) => {
+ const properties = this.props.model[field];
+ const Field = COMPONENTS[properties.kind.toLowerCase()];
+ return
;
+ });
+ }
- return (
-
- );
+ render () {
+ return (
+
+ );
+ }
}
-}
+
+ return Form;
+};
+
+const Form = factory(
+ InjectAutocomplete, InjectButton, InjectCheckbox, InjectDatePicker, InjectDropdown,
+ InjectInput, InjectRadioGroup, InjectSlider, InjectSwitch, InjectTimePicker
+);
export default Form;
+export { factory as formFactory };
+export { Form };
diff --git a/components/form/index.js b/components/form/index.js
index 4516c6297..7ae8c7f46 100644
--- a/components/form/index.js
+++ b/components/form/index.js
@@ -1 +1,19 @@
-export default from './Form';
+import { formFactory } from './Form.js';
+import Autocomplete from '../autocomplete';
+import Button from '../button';
+import Checkbox from '../checkbox';
+import DatePicker from '../date_picker';
+import Dropdown from '../dropdown';
+import Input from '../input';
+import RadioGroup from '../radio';
+import Slider from '../slider';
+import Switch from '../switch';
+import TimePicker from '../time_picker';
+
+const ThemedForm = formFactory(
+ Autocomplete, Button, Checkbox, DatePicker, Dropdown,
+ Input, RadioGroup, Slider, Switch, TimePicker
+);
+
+export default ThemedForm;
+export { ThemedForm as Form };
diff --git a/components/form/readme.md b/components/form/readme.md
index e23ab646e..a26b12389 100644
--- a/components/form/readme.md
+++ b/components/form/readme.md
@@ -22,19 +22,3 @@ var fields : [
| **onSubmit** | Function | | Dispatch callback when user clicks on submit
|
| **onValid** | Function | | Dispatch callback when all required fields are full-filled.|
| **Storage** | String | | Sets a localStorage key for save all current field values.|
-
-## Methods
-
-#### getValue
-Returns the value of the form.
-
-```
-form_instance.getValue();
-```
-
-#### setValue
-Sets the value of the form component.
-
-```
-form_instance.setValue(newValue);
-```
diff --git a/components/form/style.scss b/components/form/style.scss
deleted file mode 100644
index 86ed935cc..000000000
--- a/components/form/style.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.root > :last-child {
- margin-bottom: 0;
-}
diff --git a/components/identifiers.js b/components/identifiers.js
new file mode 100644
index 000000000..bf567bf14
--- /dev/null
+++ b/components/identifiers.js
@@ -0,0 +1,25 @@
+export const APP_BAR = 'RTAppBar';
+export const AUTOCOMPLETE = 'RTAutocomplete';
+export const AVATAR = 'RTAvatar';
+export const BUTTON = 'RTButton';
+export const CARD = 'RTCard';
+export const CHIP = 'RTChip';
+export const DATE_PICKER = 'RTDatePicker';
+export const DIALOG = 'RTDialog';
+export const DROPDOWN = 'RTDropdown';
+export const INPUT = 'RTInput';
+export const LAYOUT = 'RTLayout';
+export const LINK = 'RTLink';
+export const LIST = 'RTList';
+export const NAVIGATION = 'RTNavigation';
+export const OVERLAY = 'RTOverlay';
+export const PROGRESS_BAR = 'RTProgressBar';
+export const RADIO = 'RTRadio';
+export const RIPPLE = 'RTRipple';
+export const SLIDER = 'RTSlider';
+export const SNACKBAR = 'RTSnackbar';
+export const SWITCH = 'RTSwitch';
+export const TABLE = 'RTTable';
+export const TABS = 'RTTabs';
+export const TOOLTIP = 'RTTooltip';
+export const TIMEPICKER = 'RTTimePicker';
diff --git a/components/index.js b/components/index.js
index 51516acd3..4d83a958a 100644
--- a/components/index.js
+++ b/components/index.js
@@ -2,8 +2,7 @@ import './utils/polyfills'; // Import polyfills for IE11
export AppBar from './app_bar';
export Autocomplete from './autocomplete';
export Avatar from './avatar';
-export Button from './button/Button';
-export IconButton from './button/IconButton';
+export * from './button';
export * from './card';
export Chip from './chip';
export Checkbox from './checkbox';
@@ -14,27 +13,18 @@ export Dropdown from './dropdown';
export FontIcon from './font_icon';
export Form from './form';
export Input from './input';
-export { Layout, NavDrawer, Panel, Sidebar } from './layout';
+export * from './layout';
export Link from './link';
-export List from './list/List';
-export ListItem from './list/ListItem';
-export ListDivider from './list/ListDivider';
-export ListCheckbox from './list/ListCheckbox';
-export ListSubHeader from './list/ListSubHeader';
-export Menu from './menu/Menu';
-export MenuItem from './menu/MenuItem';
-export MenuDivider from './menu/MenuDivider';
-export IconMenu from './menu/IconMenu';
+export * from './list';
+export * from './menu';
export Navigation from './navigation';
export ProgressBar from './progress_bar';
-export RadioGroup from './radio/RadioGroup';
-export RadioButton from './radio/RadioButton';
+export * from './radio';
export Ripple from './ripple';
export Slider from './slider';
export Snackbar from './snackbar';
export Switch from './switch';
export Table from './table';
-export Tabs from './tabs/Tabs';
-export Tab from './tabs/Tab';
+export * from './tabs';
export Tooltip from './tooltip';
export TimePicker from './time_picker';
diff --git a/components/input/Input.js b/components/input/Input.js
index 13f5d646d..54b14a836 100644
--- a/components/input/Input.js
+++ b/components/input/Input.js
@@ -1,101 +1,123 @@
import React from 'react';
-import ClassNames from 'classnames';
-import FontIcon from '../font_icon';
-import style from './style';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { INPUT } from '../identifiers.js';
+import InjectedFontIcon from '../font_icon/FontIcon.js';
-class Input extends React.Component {
- static propTypes = {
- children: React.PropTypes.any,
- className: React.PropTypes.string,
- disabled: React.PropTypes.bool,
- error: React.PropTypes.string,
- floating: React.PropTypes.bool,
- hint: React.PropTypes.string,
- icon: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.element
- ]),
- label: React.PropTypes.string,
- maxLength: React.PropTypes.number,
- multiline: React.PropTypes.bool,
- onBlur: React.PropTypes.func,
- onChange: React.PropTypes.func,
- onFocus: React.PropTypes.func,
- onKeyPress: React.PropTypes.func,
- required: React.PropTypes.bool,
- type: React.PropTypes.string,
- value: React.PropTypes.any
- };
+const factory = (FontIcon) => {
+ class Input extends React.Component {
+ static propTypes = {
+ children: React.PropTypes.any,
+ className: React.PropTypes.string,
+ disabled: React.PropTypes.bool,
+ error: React.PropTypes.string,
+ floating: React.PropTypes.bool,
+ hint: React.PropTypes.string,
+ icon: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.element
+ ]),
+ label: React.PropTypes.string,
+ maxLength: React.PropTypes.number,
+ multiline: React.PropTypes.bool,
+ onBlur: React.PropTypes.func,
+ onChange: React.PropTypes.func,
+ onFocus: React.PropTypes.func,
+ onKeyPress: React.PropTypes.func,
+ required: React.PropTypes.bool,
+ theme: React.PropTypes.shape({
+ bar: React.PropTypes.string,
+ counter: React.PropTypes.string,
+ disabled: React.PropTypes.string,
+ error: React.PropTypes.string,
+ errored: React.PropTypes.string,
+ hidden: React.PropTypes.string,
+ hint: React.PropTypes.string,
+ icon: React.PropTypes.string,
+ input: React.PropTypes.string,
+ inputElement: React.PropTypes.string,
+ required: React.PropTypes.string,
+ withIcon: React.PropTypes.string
+ }),
+ type: React.PropTypes.string,
+ value: React.PropTypes.any
+ };
- static defaultProps = {
- className: '',
- hint: '',
- disabled: false,
- floating: true,
- multiline: false,
- required: false,
- type: 'text'
- };
+ static defaultProps = {
+ className: '',
+ hint: '',
+ disabled: false,
+ floating: true,
+ multiline: false,
+ required: false,
+ type: 'text'
+ };
- handleChange = (event) => {
- if (this.props.onChange) this.props.onChange(event.target.value, event);
- };
+ handleChange = (event) => {
+ if (this.props.onChange) this.props.onChange(event.target.value, event);
+ };
- blur () {
- this.refs.input.blur();
- }
+ blur () {
+ this.refs.input.blur();
+ }
- focus () {
- this.refs.input.focus();
- }
+ focus () {
+ this.refs.input.focus();
+ }
- render () {
- const { children, disabled, error, floating, hint, icon,
- label: labelText, maxLength, multiline, required,
- type, value, ...others} = this.props;
- const length = maxLength && value ? value.length : 0;
- const labelClassName = ClassNames(style.label, {[style.fixed]: !floating});
+ render () {
+ const { children, disabled, error, floating, hint, icon,
+ label: labelText, maxLength, multiline, required,
+ theme, type, value, ...others} = this.props;
+ const length = maxLength && value ? value.length : 0;
+ const labelClassName = classnames(theme.label, {[theme.fixed]: !floating});
- const className = ClassNames(style.root, {
- [style.disabled]: disabled,
- [style.errored]: error,
- [style.hidden]: type === 'hidden',
- [style.withIcon]: icon
- }, this.props.className);
+ const className = classnames(theme.input, {
+ [theme.disabled]: disabled,
+ [theme.errored]: error,
+ [theme.hidden]: type === 'hidden',
+ [theme.withIcon]: icon
+ }, this.props.className);
- const valuePresent = value !== null && value !== undefined && value !== '' && !Number.isNaN(value);
+ const valuePresent = value !== null && value !== undefined && value !== '' && !Number.isNaN(value);
- const InputElement = React.createElement(multiline ? 'textarea' : 'input', {
- ...others,
- className: ClassNames(style.input, {[style.filled]: valuePresent}),
- onChange: this.handleChange,
- ref: 'input',
- role: 'input',
- disabled,
- required,
- type,
- value,
- maxLength
- });
+ const InputElement = React.createElement(multiline ? 'textarea' : 'input', {
+ ...others,
+ className: classnames(theme.inputElement, {[theme.filled]: valuePresent}),
+ onChange: this.handleChange,
+ ref: 'input',
+ role: 'input',
+ disabled,
+ required,
+ type,
+ value,
+ maxLength
+ });
- return (
-
- {InputElement}
- {icon ? : null}
-
- {labelText
- ?
- {labelText}
- {required ? * : null}
-
- : null}
- {hint ? {hint} : null}
- {error ? {error} : null}
- {maxLength ? {length}/{maxLength} : null}
- {children}
-
- );
+ return (
+
+ {InputElement}
+ {icon ? : null}
+
+ {labelText
+ ?
+ {labelText}
+ {required ? * : null}
+
+ : null}
+ {hint ? {hint} : null}
+ {error ? {error} : null}
+ {maxLength ? {length}/{maxLength} : null}
+ {children}
+
+ );
+ }
}
-}
-export default Input;
+ return Input;
+};
+
+const Input = factory(InjectedFontIcon);
+export default themr(INPUT)(Input);
+export { factory as inputFactory };
+export { Input };
diff --git a/components/input/index.js b/components/input/index.js
index 3d7b4980e..2f4fe1d95 100644
--- a/components/input/index.js
+++ b/components/input/index.js
@@ -1 +1,11 @@
-export default from './Input';
+import { INPUT } from '../identifiers.js';
+import { themr } from 'react-css-themr';
+import { inputFactory } from './Input.js';
+import FontIcon from '../font_icon/FontIcon.js';
+import theme from './theme.scss';
+
+const Input = inputFactory(FontIcon);
+const ThemedInput = themr(INPUT, theme)(Input);
+
+export default ThemedInput;
+export { ThemedInput as Input };
diff --git a/components/input/readme.md b/components/input/readme.md
index 630c2ebca..1189bc843 100644
--- a/components/input/readme.md
+++ b/components/input/readme.md
@@ -27,6 +27,8 @@ class InputTest extends React.Component {
}
```
+If you want to provide a theme via context, the component key is `RTInput`.
+
## Properties
| Name | Type | Default | Description|
@@ -48,9 +50,19 @@ class InputTest extends React.Component {
| `type` | `String` | `text` | Type of the input element. It can be a valid HTML5 input type|
| `value` | `Any` | | Current value of the input element.|
-## Methods
-
-The input is stateless but it includes two methods to be able to communicate with the DOM input node:
+## Theming
-- `blur` to blur the input field.
-- `focus` to focus the input field.
+| Name | Description|
+|:-----------|:-----------|
+| `bar` | Used for the bar under the input.|
+| `counter` | Used for the counter element.|
+| `disabled` | Added to the root class when input is disabled.|
+| `error` | Used for the text error.|
+| `errored` | Added to the root class when input is errored.|
+| `hidden` | Used when the input is hidden.|
+| `hint` | Used for the hint text.|
+| `icon` | Used for the icon in case the input has icon.|
+| `input` | Used as root class for the component.|
+| `inputElement` | Used for the HTML input element.|
+| `required` | Used in case the input is required.|
+| `withIcon` | Added to the root class if the input has icon.|
diff --git a/components/input/style.scss b/components/input/theme.scss
similarity index 96%
rename from components/input/style.scss
rename to components/input/theme.scss
index 0e5f9b641..739de903e 100644
--- a/components/input/style.scss
+++ b/components/input/theme.scss
@@ -1,7 +1,9 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
-.root {
+.input {
position: relative;
padding: $input-padding 0;
&.withIcon {
@@ -23,7 +25,7 @@
transition: color $animation-duration $animation-curve-default;
}
-.input {
+.inputElement {
display: block;
width: 100%;
padding: $input-field-padding 0;
@@ -126,14 +128,14 @@
color: $input-text-label-color;
}
-.disabled > .input {
+.disabled > .inputElement {
color: $input-text-disabled-text-color;
border-bottom-style: dotted;
}
.errored {
padding-bottom: 0;
- > .input {
+ > .inputElement {
margin-top: 1px;
border-bottom-color: $input-text-error-color;
}
diff --git a/components/layout/Layout.js b/components/layout/Layout.js
index 9b83d3f3d..2a2a08e27 100644
--- a/components/layout/Layout.js
+++ b/components/layout/Layout.js
@@ -1,23 +1,24 @@
-import React from 'react';
+import React, { PropTypes } from 'react';
+import { themr } from 'react-css-themr';
import classnames from 'classnames';
-import style from './style';
+import { LAYOUT } from '../identifiers.js';
-const Layout = ({ className, children }) => (
-
+const Layout = ({ className, children, theme }) => (
+
{children}
);
-const ALLOWED = [
- 'Panel',
- 'NavDrawer|Panel',
- 'NavDrawer|Panel|Sidebar',
- 'Panel|Sidebar'
+const ALLOWED_THEMED = [
+ 'Themed Panel',
+ 'Themed NavDrawer|Themed Panel',
+ 'Themed NavDrawer|Themed Panel|Themed Sidebar',
+ 'Themed Panel|Themed Sidebar'
];
function getChildName (child) {
if (child.type) {
- return child.type.name || child.type;
+ return child.type.displayName || child.type.name || child.type;
}
if (!child.constructor || !child.constructor.name) {
return 'UNKNOWN';
@@ -37,18 +38,22 @@ Layout.propTypes = {
}
const names = React.Children.map(children, getChildName).join('|');
- if (!(~ALLOWED.indexOf(names))) {
+ if (!(~ALLOWED_THEMED.indexOf(names))) {
return new Error(
'`' + componentName + '` '
+ 'should have a Panel for a child, optionally preceded by a NavDrawer and/or followed by a Sidebar.'
);
}
},
- className: React.PropTypes.string
+ className: PropTypes.string,
+ theme: PropTypes.shape({
+ layout: PropTypes.string
+ })
};
Layout.defaultProps = {
className: ''
};
-export default Layout;
+export default themr(LAYOUT)(Layout);
+export { Layout };
diff --git a/components/layout/NavDrawer.js b/components/layout/NavDrawer.js
index 0504cfb24..2abba2c8a 100644
--- a/components/layout/NavDrawer.js
+++ b/components/layout/NavDrawer.js
@@ -1,24 +1,25 @@
-import React from 'react';
+import React, { PropTypes } from 'react';
import classnames from 'classnames';
-import style from './style';
+import { themr } from 'react-css-themr';
+import { LAYOUT } from '../identifiers.js';
-const NavDrawer = (props) => {
- const rootClasses = classnames([style.navDrawer], {
- [style['permanent-' + props.permanentAt]]: props.permanentAt,
- [style.wide]: (props.width === 'wide'),
- [style.active]: props.active,
- [style.pinned]: props.pinned
- }, props.className);
+const NavDrawer = ({ active, children, className, onOverlayClick, permanentAt, pinned, scrollY, theme, width }) => {
+ const rootClasses = classnames([theme.navDrawer], {
+ [theme[permanentAt + 'Permanent']]: permanentAt,
+ [theme.wide]: (width === 'wide'),
+ [theme.active]: active,
+ [theme.pinned]: pinned
+ }, className);
- const drawerClasses = classnames(style.drawerContent, {
- [style.scrollY]: props.scrollY
+ const drawerClasses = classnames(theme.drawerContent, {
+ [theme.scrollY]: scrollY
});
return (
-
-
+
+
- {props.children}
+ {children}
@@ -26,14 +27,29 @@ const NavDrawer = (props) => {
};
NavDrawer.propTypes = {
- active: React.PropTypes.bool,
- children: React.PropTypes.any,
- className: React.PropTypes.string,
- onOverlayClick: React.PropTypes.func,
- permanentAt: React.PropTypes.oneOf(['sm', 'md', 'lg', 'xl', 'xxl', 'xxxl']),
- pinned: React.PropTypes.bool,
- scrollY: React.PropTypes.bool,
- width: React.PropTypes.oneOf(['normal', 'wide'])
+ active: PropTypes.bool,
+ children: PropTypes.any,
+ className: PropTypes.string,
+ onOverlayClick: PropTypes.func,
+ permanentAt: PropTypes.oneOf(['sm', 'md', 'lg', 'xl', 'xxl', 'xxxl']),
+ pinned: PropTypes.bool,
+ scrollY: PropTypes.bool,
+ theme: PropTypes.shape({
+ active: PropTypes.string,
+ drawerContent: PropTypes.string,
+ lgPermanent: PropTypes.string,
+ mdPermanent: PropTypes.string,
+ navDrawer: PropTypes.string,
+ pinned: PropTypes.string,
+ scrim: PropTypes.string,
+ scrollY: PropTypes.string,
+ smPermanent: PropTypes.string,
+ wide: PropTypes.string,
+ xlPermanent: PropTypes.string,
+ xxlPermanent: PropTypes.string,
+ xxxlPermanent: PropTypes.string
+ }),
+ width: PropTypes.oneOf(['normal', 'wide'])
};
NavDrawer.defaultProps = {
@@ -43,4 +59,5 @@ NavDrawer.defaultProps = {
width: 'normal'
};
-export default NavDrawer;
+export default themr(LAYOUT)(NavDrawer);
+export { NavDrawer };
diff --git a/components/layout/Panel.js b/components/layout/Panel.js
index a65005842..ffc1e6191 100644
--- a/components/layout/Panel.js
+++ b/components/layout/Panel.js
@@ -1,10 +1,11 @@
-import React from 'react';
+import React, { PropTypes } from 'react';
import classnames from 'classnames';
-import style from './style';
+import { themr } from 'react-css-themr';
+import { LAYOUT } from '../identifiers.js';
-const Panel = ({ children, className, scrollY }) => {
- const _className = classnames(style.panel, {
- [style.scrollY]: scrollY
+const Panel = ({ children, className, scrollY, theme }) => {
+ const _className = classnames(theme.panel, {
+ [theme.scrollY]: scrollY
}, className);
return (
@@ -15,9 +16,13 @@ const Panel = ({ children, className, scrollY }) => {
};
Panel.propTypes = {
- children: React.PropTypes.any,
- className: React.PropTypes.string,
- scrollY: React.PropTypes.bool
+ children: PropTypes.any,
+ className: PropTypes.string,
+ scrollY: PropTypes.bool,
+ theme: PropTypes.shape({
+ panel: PropTypes.string,
+ scrollY: PropTypes.string
+ })
};
Panel.defaultProps = {
@@ -25,4 +30,5 @@ Panel.defaultProps = {
scrollY: false
};
-export default Panel;
+export default themr(LAYOUT)(Panel);
+export { Panel };
diff --git a/components/layout/Sidebar.js b/components/layout/Sidebar.js
index 5b3567304..7b8923a26 100644
--- a/components/layout/Sidebar.js
+++ b/components/layout/Sidebar.js
@@ -1,31 +1,38 @@
-import React from 'react';
+import React, { PropTypes } from 'react';
import classnames from 'classnames';
-import style from './style';
+import { themr } from 'react-css-themr';
+import { LAYOUT } from '../identifiers.js';
-const Sidebar = (props) => {
- const wrapperClasses = classnames(style.sidebar, style[`width-${props.width}`], {
- [style.pinned]: props.pinned
- }, props.className);
+const Sidebar = ({ children, className, pinned, scrollY, theme, width }) => {
+ const wrapperClasses = classnames(theme.sidebar, theme[`width-${width}`], {
+ [theme.pinned]: pinned
+ }, className);
- const innerClasses = classnames(style.sidebarContent, {
- [style.scrollY]: props.scrollY
+ const innerClasses = classnames(theme.sidebarContent, {
+ [theme.scrollY]: scrollY
});
return (
- {props.children}
+ {children}
);
};
Sidebar.propTypes = {
- children: React.PropTypes.any,
- className: React.PropTypes.string,
- pinned: React.PropTypes.bool,
- scrollY: React.PropTypes.bool,
- width: React.PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 25, 33, 50, 66, 75, 100])
+ children: PropTypes.any,
+ className: PropTypes.string,
+ pinned: PropTypes.bool,
+ scrollY: PropTypes.bool,
+ theme: PropTypes.shape({
+ pinned: PropTypes.string,
+ scrollY: PropTypes.string,
+ sidebar: PropTypes.string,
+ sidebarContent: PropTypes.string
+ }),
+ width: PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 25, 33, 50, 66, 75, 100])
};
Sidebar.defaultProps = {
@@ -35,4 +42,5 @@ Sidebar.defaultProps = {
width: 5
};
-export default Sidebar;
+export default themr(LAYOUT)(Sidebar);
+export { Sidebar };
diff --git a/components/layout/index.js b/components/layout/index.js
index 88fc67a6c..d44ec77d3 100644
--- a/components/layout/index.js
+++ b/components/layout/index.js
@@ -1,4 +1,17 @@
-export { default as Layout } from './Layout';
-export { default as Panel } from './Panel';
-export { default as NavDrawer } from './NavDrawer';
-export { default as Sidebar } from './Sidebar';
+import { themr } from 'react-css-themr';
+import { LAYOUT } from '../identifiers.js';
+import { Layout } from './Layout.js';
+import { Panel } from './Panel.js';
+import { NavDrawer } from './NavDrawer.js';
+import { Sidebar } from './Sidebar.js';
+import theme from './theme.scss';
+
+const ThemedLayout = themr(LAYOUT, theme)(Layout);
+const ThemedPanel = themr(LAYOUT, theme)(Panel);
+const ThemedNavDrawer = themr(LAYOUT, theme)(NavDrawer);
+const ThemedSidebar = themr(LAYOUT, theme)(Sidebar);
+
+export { ThemedLayout as Layout };
+export { ThemedPanel as Panel };
+export { ThemedNavDrawer as NavDrawer };
+export { ThemedSidebar as Sidebar };
diff --git a/components/layout/readme.md b/components/layout/readme.md
index 2b74ed2e8..28593ffa2 100644
--- a/components/layout/readme.md
+++ b/components/layout/readme.md
@@ -1,10 +1,6 @@
# Layout
-A Layout is a container that can hold a main content area with an optional
-navigation drawer (on the left) and/or sidebar (on the right). According to
-the [material design spec](https://www.google.com/design/spec/layout/structure.html#structure-side-nav),
-the left drawer is typically used for navigation or identity-based content,
-while the right sidebar is secondary content related to the main content.
+A Layout is a container that can hold a main content area with an optional navigation drawer (on the left) and/or sidebar (on the right). According to the [material design spec](https://www.google.com/design/spec/layout/structure.html#structure-side-nav), the left drawer is typically used for navigation or identity-based content, while the right sidebar is secondary content related to the main content.
```jsx
@@ -61,20 +57,15 @@ class LayoutTest extends React.Component {
}
```
-
+If you want to provide a theme for Layout subcomponents the key to use is `RTLayout`;
## Layout
-The primary layout component. This acts as the main container
-that all subcomponents are placed within. The layout is typically placed
-so as to fill the entire screen, although it does not have to be.
+The primary layout component. This acts as the main container that all subcomponents are placed within. The layout is typically placed so as to fill the entire screen, although it does not have to be.
### Breakpoints and Increments
-The Layout's subcomponents can alter their appearance and behavior based
-on the current screen size. The layout uses the screen breakpoints described
-in the [material design spec](https://www.google.com/design/spec/layout/responsive-ui.html#responsive-ui-breakpoints),
-namely:
+The Layout's subcomponents can alter their appearance and behavior based on the current screen size. The layout uses the screen breakpoints described in the [material design spec](https://www.google.com/design/spec/layout/responsive-ui.html#responsive-ui-breakpoints), namely:
| Width | Abreviation | Typical Device |
|:-----|:-----|:-----|
@@ -89,21 +80,13 @@ namely:
| 1600px | `xxl` | desktop |
| 1920px | `xxxl` | desktop |
-The components also make use of [standard increments](https://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-sizing-by-increments),
-which is a unit equal to the height of the action bar. At mobile sizes (< `xs`) the increment is
-56px. On larger screens, it is 64px.
+The components also make use of [standard increments](https://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-sizing-by-increments), which is a unit equal to the height of the action bar. At mobile sizes (< `xs`) the increment is 56px. On larger screens, it is 64px.
### Content Area Layout
-The content areas of all three of the subcomponents (`NavDrawer`, `Panel`, and `Sidebar`)
-use flexbox column layouts set to fill the entire height of the containing `Layout`.
-The column layout lends itself well to the fixed header/scrolling content that will frequently
-inhabit these components. By default, these components also do not scroll content vertically
-so that you can control where scrolling occurs. (For example, see the content of the `Panel`
-in the sample.)
+The content areas of all three of the subcomponents (`NavDrawer`, `Panel`, and `Sidebar`) use flexbox column layouts set to fill the entire height of the containing `Layout`. The column layout lends itself well to the fixed header/scrolling content that will frequently inhabit these components. By default, these components also do not scroll content vertically so that you can control where scrolling occurs. (For example, see the content of the `Panel` in the sample.)
-If the column layout does not suit your needs, simply fill the content area with an element
-with `flex` set to 1, and use whatever layout you like within it.
+If the column layout does not suit your needs, simply fill the content area with an element with `flex` set to 1, and use whatever layout you like within it.
### Properties
| Name | Type | Default | Description |
@@ -111,11 +94,16 @@ with `flex` set to 1, and use whatever layout you like within it.
| `children` | `Nodes` | | A `Panel`, optionally preceded by a `NavDrawer` and/or followed by a `Sidebar` |
| `className` | `string` | | Additional class(es) for custom styling. |
+### Theme
+The themed key the `Layout` in general is `ToolboxLayout`. For the `Layout` wrapper it should only provide one class interface:
+
+| Name | Description|
+|:---------|:-----------|
+| `layout` | Class used in the container to position and align inner items.|
+
## NavDrawer
-The [navigation drawer](https://www.google.com/design/spec/patterns/navigation-drawer.html) slides
-in from the left and usually holds [the application's main navigation](https://www.google.com/design/spec/layout/structure.html#structure-side-nav).
-The drawer's width is based on the screen size:
+The [navigation drawer](https://www.google.com/design/spec/patterns/navigation-drawer.html) slides in from the left and usually holds [the application's main navigation](https://www.google.com/design/spec/layout/structure.html#structure-side-nav). The drawer's width is based on the screen size:
| Breakpoint | Drawer Width | Notes |
|:-----|:-----|:-----|
@@ -123,9 +111,7 @@ The drawer's width is based on the screen size:
| > `xs` | 320px | |
| > `xs` | 400px | If property `width` is set to `wide` |
-The drawer can be docked to the left side of the screen or can float temporarily
-as an overlay. You can control the drawer's display manually `active` and `pinned` properties,
-and can also specify a breakpoint at which the drawer automatically becomes permanently docked.
+The drawer can be docked to the left side of the screen or can float temporarily as an overlay. You can control the drawer's display manually `active` and `pinned` properties, and can also specify a breakpoint at which the drawer automatically becomes permanently docked.
### Properties
| Name | Type | Default | Description |
@@ -138,11 +124,26 @@ and can also specify a breakpoint at which the drawer automatically becomes perm
| `onOverlayClick` | `Function` | | Callback function to be invoked when the overlay is clicked.|
| `className` | `string` | | Additional class(es) for custom styling. |
+### Theme
+| Name | Description|
+|:---------|:-----------|
+| `active` | Used when the drawer is active.|
+| `drawerContent` | Used for the content of the drawer.|
+| `lgPermanent` | Added to the root class for large drawer.|
+| `mdPermanent` | Added to the root class for medium drawer.|
+| `navDrawer` | Root class for the drawer.|
+| `pinned` | Added to the root class if positioning is pinned.|
+| `scrim` | Used as a wrapper for the drawer content.|
+| `scrollY` | Added to the drawer content if its scrollable.|
+| `smPermanent` | Added to the root class for small drawer.|
+| `wide` | Added to the root class if width is wide.|
+| `xlPermanent` | Added to the root class for extra big drawer.|
+| `xxlPermanent` | Added to the root class for super big drawer.|
+| `xxxlPermanent` | Added to the root class for largest possible drawer.|
+
## Panel
-The `Panel` is the main content area within a `Layout`. It is a full-height
-flexbox column that takes up all remaining horizontal space after the `NavDrawer`
-and `Sidebar` are laid out.
+The `Panel` is the main content area within a `Layout`. It is a full-height flexbox column that takes up all remaining horizontal space after the `NavDrawer` and `Sidebar` are laid out.
### Properties
| Name | Type | Default | Description |
@@ -150,13 +151,15 @@ and `Sidebar` are laid out.
| `scrollY` | `bool` | `false` | If true, the panel will vertically scroll all content. |
| `className` | `string` | | Additional class(es) for custom styling. |
+### Theme
+| Name | Description|
+|:---------|:-----------|
+| `panel` | Used as the root class of the panel component.|
+| `scrollY` | Used in case the panel is scrollable.|
+
## Sidebar
-The `Sidebar` is an extra drawer that docks to the right side of the `Layout`.
-The sidebar's width can be set either to a multiple of the "standard increment"
-(1 - 12 increments) or as a percentage of the parent `Layout` width (25%, 33%, 50%, 66%, 75%, 100%).
-Regardless of the width set, at mobile screen sizes the sidebar acts like a full-screen dialog that
-covers the entire screen (see [examples](https://www.google.com/design/spec/layout/structure.html#structure-side-nav)).
+The `Sidebar` is an extra drawer that docks to the right side of the `Layout`. The sidebar's width can be set either to a multiple of the "standard increment" (1 - 12 increments) or as a percentage of the parent `Layout` width (25%, 33%, 50%, 66%, 75%, 100%). Regardless of the width set, at mobile screen sizes the sidebar acts like a full-screen dialog that covers the entire screen (see [examples](https://www.google.com/design/spec/layout/structure.html#structure-side-nav)).
### Properties
| Name | Type | Default | Description |
@@ -166,11 +169,17 @@ covers the entire screen (see [examples](https://www.google.com/design/spec/layo
| `scrollY` | `bool` | `false` | If true, the sidebar will vertically scroll all content. |
| `className` | `string` | | Additional class(es) for custom styling. |
+### Theme
+| Name | Description|
+|:---------|:-----------|
+| `pinned` | Added to the root class if sidebar is pinned.|
+| `scrollY` | Add to the content of sidebar if its scrollable.|
+| `sidebar` | Root class of the sidebar.|
+| `sidebarContent` | Used in for the content element of the sidebar.|
+
## Nesting Layouts
-The `Layout` is meant to be used near the top level of your application,
-so that it occupies the entire screen. However, it is possible to nest one
-layout inside another:
+The `Layout` is meant to be used near the top level of your application, so that it occupies the entire screen. However, it is possible to nest one layout inside another:
```jsx
@@ -188,6 +197,4 @@ layout inside another:
```
-The main reason you would want to do something like this would be so that
-the navigation could be rendered at a high level, while the contents of the
-inner `Layout` would be controlled by react-router or something like that.
+The main reason you would want to do something like this would be so that the navigation could be rendered at a high level, while the contents of the inner `Layout` would be controlled by react-router or something like that.
diff --git a/components/layout/style.scss b/components/layout/theme.scss
similarity index 96%
rename from components/layout/style.scss
rename to components/layout/theme.scss
index 16e16221b..a07c02924 100644
--- a/components/layout/style.scss
+++ b/components/layout/theme.scss
@@ -1,8 +1,10 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
@import "./mixins";
-.root {
+.layout {
position: relative;
display: flex;
width: 100%;
@@ -101,47 +103,47 @@
// Permanent screen, ignore .active and make part of layout
@media screen and (min-width: $layout-breakpoint-sm) {
- &.permanent-sm {
+ &.smPermanent {
@include permanent();
}
}
@media screen and (min-width: $layout-breakpoint-md) {
- &.permanent-md {
+ &.mdPermanent {
@include permanent();
}
}
@media screen and (min-width: $layout-breakpoint-lg) {
- &.permanent-lg {
+ &.lgPermanent {
@include permanent();
}
}
@media screen and (min-width: $layout-breakpoint-xl) {
- &.permanent-xl {
+ &.xlPermanent {
@include permanent();
}
}
@media screen and (min-width: $layout-breakpoint-xxl) {
- &.permanent-xxl {
+ &.xxlPermanent {
@include permanent();
}
}
@media screen and (min-width: $layout-breakpoint-xxxl) {
- &.permanent-xxxl {
+ &.xxxlPermanent {
@include permanent();
}
}
}
- & .root {
+ & .layout {
.scrim {
z-index: $z-index-highest - 1;
}
- & .root {
+ & .layout {
.scrim {
z-index: $z-index-highest - 2;
}
diff --git a/components/link/Link.js b/components/link/Link.js
index 8500bd1d0..f37bfc6eb 100644
--- a/components/link/Link.js
+++ b/components/link/Link.js
@@ -1,33 +1,39 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import style from './style';
-import FontIcon from '../font_icon';
+import React, { PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { LINK } from '../identifiers.js';
+import FontIcon from '../font_icon/FontIcon.js';
-const Link = ({children, ...props}) => {
- const className = ClassNames(style.root, {
- [style.active]: props.active
- }, props.className);
+const Link = ({active, children, className, count, icon, label, theme, ...others}) => {
+ const _className = classnames(theme.link, {
+ [theme.active]: active
+ }, className);
return (
-
- {props.icon ? : null}
- {props.label ? {props.label} : null}
- {props.count && parseInt(props.count) !== 0 ? {props.count} : null}
- {children ? children : null}
+
+ {icon ? : null}
+ {label ? {label} : null}
+ {count && parseInt(count) !== 0 ? {count} : null}
+ {children}
);
};
Link.propTypes = {
- active: React.PropTypes.bool,
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- count: React.PropTypes.number,
- icon: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.element
+ active: PropTypes.bool,
+ children: PropTypes.node,
+ className: PropTypes.string,
+ count: PropTypes.number,
+ icon: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.element
]),
- label: React.PropTypes.string
+ label: PropTypes.string,
+ theme: PropTypes.shape({
+ active: PropTypes.string,
+ icon: PropTypes.string,
+ link: PropTypes.string
+ })
};
Link.defaultProps = {
@@ -35,4 +41,5 @@ Link.defaultProps = {
className: ''
};
-export default Link;
+export default themr(LINK)(Link);
+export { Link };
diff --git a/components/link/index.js b/components/link/index.js
index 5bda4e744..3dcfcf1b5 100644
--- a/components/link/index.js
+++ b/components/link/index.js
@@ -1 +1,9 @@
-export default from './Link';
+import { themr } from 'react-css-themr';
+import { LINK } from '../identifiers.js';
+import { Link } from './Link.js';
+import theme from './theme.scss';
+
+const ThemedLink = themr(LINK, theme)(Link);
+
+export default ThemedLink;
+export { ThemedLink as Link };
diff --git a/components/link/readme.md b/components/link/readme.md
index fa8dff1b6..1de725eb1 100644
--- a/components/link/readme.md
+++ b/components/link/readme.md
@@ -15,14 +15,24 @@ const LinksTest = () => (
);
```
+If you want to provide a theme via context, the component key is `RTLink`.
+
## Properties
You can add as many properties as you want to be directly transferred to the output anchor element. Apart from them you have the following properties:
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
-| `active` | `Boolean` | `false` | If true, adds active style to link.|
-| `className` | `String` | `''` | Sets a custom class name to add styles to the link.|
-| `count` | `Number` | | Sets a count number.|
-| `icon` | `String` or `Element` | | An icon key string to include a `FontIcon` component in front of the text.|
-| `label` | `String` | | The text string used for the text content of the link.|
+| `active` | `Boolean` | `false` | If true, adds active style to link.|
+| `className` | `String` | `''` | Sets a custom class name to add styles to the link.|
+| `count` | `Number` | | Sets a count number.|
+| `icon` | `String` or `Element` | | An icon key string to include a `FontIcon` component in front of the text.|
+| `label` | `String` | | The text string used for the text content of the link.|
+
+## Theme
+
+| Name | Description|
+|:---------|:-----------|
+| `active` | Added to the root element if the Link is active.|
+| `icon` | Used for the icon element if it's present.|
+| `link` | Used for the root element.|
diff --git a/components/link/style.scss b/components/link/theme.scss
similarity index 88%
rename from components/link/style.scss
rename to components/link/theme.scss
index 4981c596d..11eb90844 100644
--- a/components/link/style.scss
+++ b/components/link/theme.scss
@@ -1,11 +1,13 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
.icon {
margin-right: $unit;
font-size: 1.8 * $unit;
}
-.root {
+.link {
position: relative;
display: flex;
flex-direction: row;
diff --git a/components/list/List.js b/components/list/List.js
index 43bc560f8..6091e496c 100644
--- a/components/list/List.js
+++ b/components/list/List.js
@@ -1,43 +1,53 @@
-import React from 'react';
-import ListItem from './ListItem';
-import style from './style';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { LIST } from '../identifiers.js';
+import InjectListItem from './ListItem.js';
-class List extends React.Component {
- static propTypes = {
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- ripple: React.PropTypes.bool,
- selectable: React.PropTypes.bool
- };
+const factory = (ListItem) => {
+ class List extends Component {
+ static propTypes = {
+ children: PropTypes.node,
+ className: PropTypes.string,
+ ripple: PropTypes.bool,
+ selectable: PropTypes.bool,
+ theme: PropTypes.shape({
+ list: PropTypes.string
+ })
+ };
- static defaultProps = {
- className: '',
- ripple: false,
- selectable: false
- };
+ static defaultProps = {
+ className: '',
+ ripple: false,
+ selectable: false
+ };
- renderItems () {
- return React.Children.map(this.props.children, (item) => {
- if (item.type === ListItem) {
- return React.cloneElement(item, {
- ripple: this.props.ripple,
- selectable: this.props.selectable
- });
- } else {
- return React.cloneElement(item);
- }
- });
- }
+ renderItems () {
+ return React.Children.map(this.props.children, (item) => {
+ if (item.type === ListItem) {
+ return React.cloneElement(item, {
+ ripple: this.props.ripple,
+ selectable: this.props.selectable
+ });
+ } else {
+ return React.cloneElement(item);
+ }
+ });
+ }
- render () {
- let className = style.list;
- if (this.props.className) className += ` ${this.props.className}`;
- return (
-
- );
+ render () {
+ return (
+
+ );
+ }
}
-}
-export default List;
+ return List;
+};
+
+const List = factory(InjectListItem);
+export default themr(LIST)(List);
+export { factory as listFactory };
+export { List };
diff --git a/components/list/ListCheckbox.js b/components/list/ListCheckbox.js
index 53ebb2841..c50379dde 100644
--- a/components/list/ListCheckbox.js
+++ b/components/list/ListCheckbox.js
@@ -1,46 +1,60 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import Checkbox from '../checkbox';
-import ListItemContent from './ListItemContent';
-import style from './style';
+import React, { PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { LIST } from '../identifiers.js';
+import InjectCheckbox from '../checkbox/Checkbox.js';
+import InjectListItemContent from './ListItemContent.js';
-const ListCheckbox = (props) => {
- const className = ClassNames([style.item, style.checkboxItem], {
- [style.withLegend]: props.legend,
- [style.disabled]: props.disabled
- }, props.className);
+const factory = (Checkbox, ListItemContent) => {
+ const ListCheckbox = ({ caption, checked, className, disabled, legend, name, onBlur, onChange, onFocus, theme }) => {
+ const _className = classnames(theme.item, theme.checkboxItem, {
+ [theme.disabled]: disabled
+ }, className);
- return (
-
- }
- name={props.name}
- onBlur={props.onBlur}
- onChange={props.onChange}
- onFocus={props.onFocus}
- />
-
- );
-};
+ return (
+
+ }
+ name={name}
+ onBlur={onBlur}
+ onChange={onChange}
+ onFocus={onFocus}
+ />
+
+ );
+ };
-ListCheckbox.propTypes = {
- caption: React.PropTypes.string.isRequired,
- checked: React.PropTypes.bool,
- className: React.PropTypes.string,
- disabled: React.PropTypes.bool,
- legend: React.PropTypes.string,
- name: React.PropTypes.string,
- onBlur: React.PropTypes.func,
- onChange: React.PropTypes.func,
- onFocus: React.PropTypes.func
-};
+ ListCheckbox.propTypes = {
+ caption: PropTypes.string,
+ checked: PropTypes.bool,
+ className: PropTypes.string,
+ disabled: PropTypes.bool,
+ legend: PropTypes.string,
+ name: PropTypes.string,
+ onBlur: PropTypes.func,
+ onChange: PropTypes.func,
+ onFocus: PropTypes.func,
+ theme: PropTypes.shape({
+ checkbox: PropTypes.string,
+ checkboxItem: PropTypes.string,
+ disabled: PropTypes.string,
+ item: PropTypes.string
+ })
+ };
-ListCheckbox.defaultProps = {
- checked: false,
- disabled: false
+ ListCheckbox.defaultProps = {
+ checked: false,
+ disabled: false
+ };
+
+ return ListCheckbox;
};
-export default ListCheckbox;
+const ListCheckbox = factory(InjectCheckbox, InjectListItemContent);
+
+export default themr(LIST)(ListCheckbox);
+export { factory as listCheckboxFactory };
+export { ListCheckbox };
diff --git a/components/list/ListDivider.js b/components/list/ListDivider.js
index a7b873e89..f1cc252f8 100644
--- a/components/list/ListDivider.js
+++ b/components/list/ListDivider.js
@@ -1,17 +1,22 @@
-import React from 'react';
-import style from './style';
+import React, { PropTypes } from 'react';
+import { themr } from 'react-css-themr';
+import { LIST } from '../identifiers.js';
-const ListDivider = ({inset}) => {
- const className = inset ? `${style.divider} ${style.inset}` : style.divider;
- return
;
-};
+const ListDivider = ({inset, theme}) => (
+
+);
ListDivider.propTypes = {
- inset: React.PropTypes.bool
+ inset: PropTypes.bool,
+ theme: PropTypes.shape({
+ divider: PropTypes.string,
+ inset: PropTypes.string
+ })
};
ListDivider.defaultProps = {
inset: false
};
-export default ListDivider;
+export default themr(LIST)(ListDivider);
+export { ListDivider };
diff --git a/components/list/ListItem.js b/components/list/ListItem.js
index ee6864406..2cea098d7 100644
--- a/components/list/ListItem.js
+++ b/components/list/ListItem.js
@@ -1,75 +1,81 @@
-import React from 'react';
-import ListItemContent from './ListItemContent';
-import ListItemLayout from './ListItemLayout';
-import Ripple from '../ripple';
-import style from './style';
+import React, { Component, PropTypes } from 'react';
+import { themr } from 'react-css-themr';
+import { LIST } from '../identifiers.js';
+import InjectListItemContent from './ListItemContent.js';
+import InjectListItemLayout from './ListItemLayout.js';
+import rippleFactory from '../ripple/Ripple.js';
-class ListItem extends React.Component {
- static propTypes = {
- children: React.PropTypes.any,
- className: React.PropTypes.string,
- disabled: React.PropTypes.bool,
- onClick: React.PropTypes.func,
- ripple: React.PropTypes.bool,
- to: React.PropTypes.string
- };
-
- static defaultProps = {
- disabled: false,
- ripple: false
- };
-
- handleClick = (event) => {
- if (this.props.onClick && !this.props.disabled) {
- this.props.onClick(event);
- }
- };
+const factory = (ripple, ListItemLayout, ListItemContent) => {
+ class ListItem extends Component {
+ static propTypes = {
+ children: PropTypes.any,
+ className: PropTypes.string,
+ disabled: PropTypes.bool,
+ onClick: PropTypes.func,
+ ripple: PropTypes.bool,
+ theme: PropTypes.shape({
+ listItem: PropTypes.string
+ }),
+ to: PropTypes.string
+ };
- groupChildren () {
- const children = {
- leftActions: [],
- rightActions: [],
- ignored: []
+ static defaultProps = {
+ className: '',
+ disabled: false,
+ ripple: false
};
- React.Children.forEach(this.props.children, (child, i) => {
- if (!React.isValidElement(child)) {
- return;
+ handleClick = (event) => {
+ if (this.props.onClick && !this.props.disabled) {
+ this.props.onClick(event);
}
- if (child.props.listItemIgnore) {
- children.ignored.push(child);
- return;
- }
- if (child.type === ListItemContent) {
- children.itemContent = child;
- return;
- }
- const bucket = children.itemContent ? 'rightActions' : 'leftActions';
- children[bucket].push({...child, key: i});
- });
+ };
- return children;
- }
+ groupChildren () {
+ const children = {
+ leftActions: [],
+ rightActions: [],
+ ignored: []
+ };
+
+ React.Children.forEach(this.props.children, (child, i) => {
+ if (!React.isValidElement(child)) {
+ return;
+ }
+ if (child.props.listItemIgnore) {
+ children.ignored.push(child);
+ return;
+ }
+ if (child.type === ListItemContent) {
+ children.itemContent = child;
+ return;
+ }
+ const bucket = children.itemContent ? 'rightActions' : 'leftActions';
+ children[bucket].push({...child, key: i});
+ });
- render () {
- const {className, onMouseDown, to, onClick, ripple, ...other} = this.props; //eslint-disable-line no-unused-vars
- const children = this.groupChildren();
- const content =
;
- let finalClassName = style.listItem;
- if (className) finalClassName += ` ${className}`;
+ return children;
+ }
- return (
-
- {to ? {content} : content}
- {children.ignored}
-
- );
+ render () {
+ const {className, onMouseDown, to, onClick, ripple: hasRipple, theme, ...other} = this.props; //eslint-disable-line no-unused-vars
+ const children = this.groupChildren();
+ const content =
;
+ return (
+
+ {to ? {content} : content}
+ {children.ignored}
+
+ );
+ }
}
-}
-export default Ripple({
- className: style.ripple,
- centered: false,
- listItemIgnore: true
-})(ListItem);
-export {ListItem as RawListItem};
+ return ripple(ListItem);
+};
+
+const ripple = rippleFactory({ centered: false, listItemIgnore: true });
+const ListItem = factory(ripple, InjectListItemLayout, InjectListItemContent);
+
+export default themr(LIST)(ListItem);
+export { factory as listItemFactory };
+export { ListItem };
diff --git a/components/list/ListItemAction.js b/components/list/ListItemAction.js
index 9ecdea068..f424cac50 100644
--- a/components/list/ListItemAction.js
+++ b/components/list/ListItemAction.js
@@ -1,22 +1,24 @@
import React, { PropTypes } from 'react';
-import style from './style';
+import { themr } from 'react-css-themr';
+import { LIST } from '../identifiers.js';
-const ListItemAction = ({action}) => {
+const ListItemAction = ({action, theme}) => {
const {onClick, onMouseDown} = action.props;
const stopRipple = onClick && !onMouseDown;
const stop = e => e.stopPropagation();
return (
-
+
{action}
);
};
ListItemAction.propTypes = {
- action: PropTypes.object
+ action: PropTypes.object,
+ theme: PropTypes.shape({
+ itemAction: PropTypes.string
+ })
};
-ListItemAction.defaultProps = {
-};
-
-export default ListItemAction;
+export default themr(LIST)(ListItemAction);
+export { ListItemAction };
diff --git a/components/list/ListItemActions.js b/components/list/ListItemActions.js
index d6df1aff8..da7313bb4 100644
--- a/components/list/ListItemActions.js
+++ b/components/list/ListItemActions.js
@@ -1,22 +1,34 @@
-import React from 'react';
-import style from './style';
-import ListItemAction from './ListItemAction';
+import React, { PropTypes } from 'react';
+import { themr } from 'react-css-themr';
+import { LIST } from '../identifiers.js';
+import InjectListItemAction from './ListItemAction.js';
-const ListItemActions = ({type, children}) => {
- const validChildren = React.Children.toArray(children).filter(c => (
- React.isValidElement(c)
- ));
+const factory = (ListItemAction) => {
+ const ListItemActions = ({type, children, theme}) => {
+ const validChildren = React.Children.toArray(children).filter(c => (
+ React.isValidElement(c)
+ ));
- return (
-
- {validChildren.map((action, i) => )}
-
- );
-};
+ return (
+
+ {validChildren.map((action, i) => )}
+
+ );
+ };
+
+ ListItemActions.propTypes = {
+ children: PropTypes.any,
+ theme: PropTypes.shape({
+ left: PropTypes.string,
+ right: PropTypes.string
+ }),
+ type: PropTypes.oneOf(['left', 'right'])
+ };
-ListItemActions.propTypes = {
- children: React.PropTypes.any,
- type: React.PropTypes.oneOf(['left', 'right'])
+ return ListItemActions;
};
-export default ListItemActions;
+const ListItemActions = factory(InjectListItemAction);
+export default themr(LIST)(ListItemActions);
+export { factory as listItemActionsFactory };
+export { ListItemActions };
diff --git a/components/list/ListItemContent.js b/components/list/ListItemContent.js
index f6a028b52..0c66d2d1b 100644
--- a/components/list/ListItemContent.js
+++ b/components/list/ListItemContent.js
@@ -1,41 +1,54 @@
-import React from 'react';
-import style from './style';
+import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
-import ListItemText from './ListItemText';
+import { themr } from 'react-css-themr';
+import { LIST } from '../identifiers.js';
+import InjectListItemText from './ListItemText.js';
const types = ['auto', 'normal', 'large'];
-class ListItemContent extends React.Component {
- static propTypes = {
- caption: React.PropTypes.string,
- children: React.PropTypes.any,
- legend: React.PropTypes.string,
- type: React.PropTypes.oneOf(types)
- };
+const factory = (ListItemText) => {
+ class ListItemContent extends Component {
+ static propTypes = {
+ caption: PropTypes.string,
+ children: PropTypes.any,
+ legend: PropTypes.string,
+ theme: PropTypes.shape({
+ itemContentRoot: PropTypes.string,
+ large: PropTypes.string
+ }),
+ type: PropTypes.oneOf(types)
+ };
- getType () {
- const {type, children, caption, legend} = this.props;
+ getType () {
+ const {type, children, caption, legend} = this.props;
- let count = React.Children.count(children);
- [caption, legend].forEach(s => { count += s ? 1 : 0; });
- const typeIndex = Math.min(count, types.length);
+ let count = React.Children.count(children);
+ [caption, legend].forEach(s => { count += s ? 1 : 0; });
+ const typeIndex = Math.min(count, types.length);
- return type || types[typeIndex];
- }
+ return type || types[typeIndex];
+ }
+
+ render () {
+ const {children, caption, legend, theme} = this.props;
+ const className = classnames(theme.itemContentRoot, {
+ [theme[this.getType()]]: theme[this.getType()]
+ });
- render () {
- const {children, caption, legend} = this.props;
- const className = classnames(style.itemContentRoot, {
- [style[this.getType()]]: style[this.getType()]
- });
-
- return (
-
- {caption && {caption} }
- {legend && {legend} }
- {children}
-
- );
+ return (
+
+ {caption && {caption} }
+ {legend && {legend} }
+ {children}
+
+ );
+ }
}
-}
+
+ return ListItemContent;
+};
+
+const ListItemContent = themr(LIST)(InjectListItemText);
export default ListItemContent;
+export { factory as listItemContentFactory };
+export { ListItemContent };
diff --git a/components/list/ListItemLayout.js b/components/list/ListItemLayout.js
index e1d12dfe1..2fcb95567 100644
--- a/components/list/ListItemLayout.js
+++ b/components/list/ListItemLayout.js
@@ -1,66 +1,79 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import FontIcon from '../font_icon';
-import Avatar from '../avatar';
-import ListItemContent from './ListItemContent';
-import ListItemActions from './ListItemActions';
-import style from './style';
+import React, { PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { LIST } from '../identifiers.js';
+import FontIcon from '../font_icon/FontIcon.js';
+import InjectAvatar from '../avatar/Avatar.js';
+import InjectListItemContent from './ListItemContent.js';
+import InjectListItemActions from './ListItemActions.js';
-const ListItemLayout = (props) => {
- const className = ClassNames(style.item, {
- [style.disabled]: props.disabled,
- [style.selectable]: props.selectable
- }, props.className);
+const factory = (Avatar, ListItemContent, ListItemActions) => {
+ const ListItemLayout = (props) => {
+ const className = classnames(props.theme.item, {
+ [props.theme.disabled]: props.disabled,
+ [props.theme.selectable]: props.selectable
+ }, props.className);
- const leftActions = [
- props.leftIcon && ,
- props.avatar && ,
- ...props.leftActions
- ];
- const rightActions = [
- props.rightIcon && ,
- ...props.rightActions
- ];
- const content = props.itemContent || ;
- const emptyActions = (item) => !item[0] && !item[1] && !item[2];
+ const leftActions = [
+ props.leftIcon && ,
+ props.avatar && ,
+ ...props.leftActions
+ ];
+ const rightActions = [
+ props.rightIcon && ,
+ ...props.rightActions
+ ];
+ const content = props.itemContent || ;
+ const emptyActions = (item) => !item[0] && !item[1] && !item[2];
- return (
-
- {!emptyActions(leftActions) > 0 && {leftActions} }
- {content}
- {!emptyActions(rightActions) > 0 && {rightActions} }
-
- );
-};
+ return (
+
+ {!emptyActions(leftActions) > 0 && {leftActions} }
+ {content}
+ {!emptyActions(rightActions) > 0 && {rightActions} }
+
+ );
+ };
-ListItemLayout.propTypes = {
- avatar: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.element
- ]),
- caption: React.PropTypes.string,
- children: React.PropTypes.any,
- className: React.PropTypes.string,
- disabled: React.PropTypes.bool,
- itemContent: React.PropTypes.element,
- leftActions: React.PropTypes.array,
- leftIcon: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.element
- ]),
- legend: React.PropTypes.string,
- rightActions: React.PropTypes.array,
- rightIcon: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.element
- ]),
- selectable: React.PropTypes.bool,
- to: React.PropTypes.string
-};
+ ListItemLayout.propTypes = {
+ avatar: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.element
+ ]),
+ caption: PropTypes.string,
+ children: PropTypes.any,
+ className: PropTypes.string,
+ disabled: PropTypes.bool,
+ itemContent: PropTypes.element,
+ leftActions: PropTypes.array,
+ leftIcon: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.element
+ ]),
+ legend: PropTypes.string,
+ rightActions: PropTypes.array,
+ rightIcon: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.element
+ ]),
+ selectable: PropTypes.bool,
+ theme: PropTypes.shape({
+ disabled: PropTypes.string,
+ item: PropTypes.string,
+ selectable: PropTypes.string
+ }),
+ to: PropTypes.string
+ };
+
+ ListItemLayout.defaultProps = {
+ disabled: false,
+ selectable: false
+ };
-ListItemLayout.defaultProps = {
- disabled: false,
- selectable: false
+ return ListItemLayout;
};
-export default ListItemLayout;
+const ListItemLayout = factory(InjectAvatar, InjectListItemContent, InjectListItemActions);
+export default themr(LIST)(ListItemLayout);
+export { factory as listItemLayoutFactory };
+export { ListItemLayout };
diff --git a/components/list/ListItemText.js b/components/list/ListItemText.js
index a8123c549..6d930c786 100644
--- a/components/list/ListItemText.js
+++ b/components/list/ListItemText.js
@@ -1,10 +1,10 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import style from './style';
-
-const ListItemText = ({className, primary, children, ...other}) => {
- const _className = ClassNames(style.itemText, {[style.primary]: primary}, className);
+import React, { PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { LIST } from '../identifiers.js';
+const ListItemText = ({className, primary, children, theme, ...other}) => {
+ const _className = classnames(theme.itemText, {[theme.primary]: primary}, className);
return (
{children}
@@ -13,13 +13,18 @@ const ListItemText = ({className, primary, children, ...other}) => {
};
ListItemText.propTypes = {
- children: React.PropTypes.any,
- className: React.PropTypes.string,
- primary: React.PropTypes.bool
+ children: PropTypes.any,
+ className: PropTypes.string,
+ primary: PropTypes.bool,
+ theme: PropTypes.shape({
+ itemText: PropTypes.string,
+ primary: PropTypes.string
+ })
};
ListItemText.defaultProps = {
primary: false
};
-export default ListItemText;
+export default themr(LIST)(ListItemText);
+export { ListItemText };
diff --git a/components/list/ListSubHeader.js b/components/list/ListSubHeader.js
index 690f337be..e4da57981 100644
--- a/components/list/ListSubHeader.js
+++ b/components/list/ListSubHeader.js
@@ -1,15 +1,21 @@
-import React from 'react';
-import style from './style';
+import React, { PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { LIST } from '../identifiers.js';
-const ListSubHeader = (props) => {
- let className = style.subheader;
- if (props.className) className += ` ${props.className}`;
- return {props.caption} ;
-};
+const ListSubHeader = ({ caption, className, theme }) => (
+ {caption}
+);
ListSubHeader.propTypes = {
- caption: React.PropTypes.string,
- className: React.PropTypes.string
+ caption: PropTypes.string,
+ className: PropTypes.string,
+ theme: PropTypes.object
+};
+
+ListSubHeader.defaultProps = {
+ className: ''
};
-export default ListSubHeader;
+export default themr(LIST)(ListSubHeader);
+export { ListSubHeader };
diff --git a/components/list/index.js b/components/list/index.js
index 38d0ddafb..06edbf81a 100644
--- a/components/list/index.js
+++ b/components/list/index.js
@@ -1,7 +1,39 @@
-export List from './List';
-export ListItem from './ListItem';
-export ListDivider from './ListDivider';
-export ListCheckbox from './ListCheckbox';
-export ListSubHeader from './ListSubHeader';
-export ListItemText from './ListItemText';
-export ListItemContent from './ListItemContent';
+import { themr } from 'react-css-themr';
+import { LIST } from '../identifiers.js';
+import { Avatar } from '../avatar';
+import { Checkbox } from '../checkbox';
+import { ListItemText } from './ListItemText.js';
+import { ListItemAction } from './ListItemAction.js';
+import { ListSubHeader } from './ListSubHeader.js';
+import { ListDivider } from './ListDivider.js';
+import { listFactory } from './List.js';
+import { listItemFactory } from './ListItem.js';
+import { listCheckboxFactory } from './ListCheckbox.js';
+import { listItemActionsFactory } from './ListItemActions.js';
+import { listItemContentFactory } from './ListItemContent.js';
+import { listItemLayoutFactory } from './ListItemLayout.js';
+import themedRippleFactory from '../ripple';
+import theme from './theme.scss';
+
+const applyTheme = (Component) => themr(LIST, theme)(Component);
+const ripple = themedRippleFactory({ centered: false, listItemIgnore: true });
+const ThemedListItemAction = applyTheme(ListItemAction);
+const ThemedListSubHeader = applyTheme(ListSubHeader);
+const ThemedListItemText = applyTheme(ListItemText);
+const ThemedListDivider = applyTheme(ListDivider);
+const ThemedListItemContent = applyTheme(listItemContentFactory(ThemedListItemText));
+const ThemedListItemActions = applyTheme(listItemActionsFactory(ThemedListItemAction));
+const ThemedListItemLayout = applyTheme(listItemLayoutFactory(Avatar, ThemedListItemContent, ThemedListItemActions));
+const ThemedListCheckbox = applyTheme(listCheckboxFactory(Checkbox, ThemedListItemContent));
+const ThemedListItem = applyTheme(listItemFactory(ripple, ThemedListItemLayout, ThemedListItemContent));
+const ThemedList = applyTheme(listFactory(ThemedListItem));
+
+export { ThemedListItemActions as ListItemActions };
+export { ThemedListItemContent as ListItemContent };
+export { ThemedListItemLayout as ListItemLayout };
+export { ThemedListSubHeader as ListSubHeader };
+export { ThemedListItemText as ListItemText };
+export { ThemedListCheckbox as ListCheckbox };
+export { ThemedListDivider as ListDivider };
+export { ThemedListItem as ListItem };
+export { ThemedList as List };
diff --git a/components/list/readme.md b/components/list/readme.md
index 7b85e4401..49021ccc4 100644
--- a/components/list/readme.md
+++ b/components/list/readme.md
@@ -38,20 +38,29 @@ const ListTest = () => (
);
```
+If you want to provide styles via context to this components, you should use the key `RTList`.
+
## List
Is used as a wrapper for the list. It can hold properties that affect to the whole list and get styles for the wrapper.
+### Properties
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `className` | `String` | `''` | Sets a class to give custom styles to the list wrapper.|
| `ripple` | `Boolean` | `false` | If true, each element in the list will have a ripple effect on click |
| `selectable` | `Boolean` | `false` | If true, the elements in the list will display a hover effect and a pointer cursor. |
+### Theme
+| Name | Description|
+|:---------|:-----------|
+| `list` | Used for the root element of the list.|
+
## List Item
Represents a list item that can have avatar, icons, title, subtitle, etc. Note: you have to set it as an inmediate child of `List` component.
+### Properties
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `avatar` | `String` or `Element` | | A string URL to specify an avatar in the left side of the item.|
@@ -69,10 +78,27 @@ Represents a list item that can have avatar, icons, title, subtitle, etc. Note:
| `selectable` | `Boolean` | `false` | If true, the elements in the list will display a hover effect and a pointer cursor. Inherited from the parent.|
| `to` | `String` | | In case you want to provide the item as a link, you can pass this property to specify the href. |
+### Theme
+| Name | Description|
+|:---------|:-----------|
+| `disabled` | Added to the inner content if its a disabled item.|
+| `item` | Used for the inner content of a list item.|
+| `itemAction` | Used for each action element (left/right).|
+| `itemContentRoot` | Used for the content wrapper element in list item.|
+| `itemText` | Added to the text inside of the list item.|
+| `large` | Added to the content wrapper element if size is large.|
+| `left` | Added for the element that wraps left actions.|
+| `listItem` | Used for the root element of the list.|
+| `primary` | Added to the text inside of the list item if its primary.|
+| `right` | Added for the element that wraps right actions.|
+| `selectable` | Added to the inner content if its a selectable item.|
+
+
## List Checkbox
A special type of item that has a checkbox control on the left side. It implements similar methods to the `ListItem` component and some additional to control the checkbox.
+### Properties
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `caption` | `String` | | Main text of the item. Required.|
@@ -85,16 +111,34 @@ A special type of item that has a checkbox control on the left side. It implemen
| `onChange` | `Function` | | Callback called when the input element is changed.|
| `onFocus` | `Function` | | Callback called when the input element is focused.|
+### Theme
+| Name | Description|
+|:---------|:-----------|
+| `checkbox` | Used as a wrapper class for the subheader element.|
+| `checkboxItem` | Added to the checkbox element.|
+| `disabled` | Added to the root element if the component is disabled.|
+| `item` | Used as a wrapper class root element element. Same as List.|
+| `itemContentRoot` | Used for the content wrapper element in list item.|
+| `itemText` | Added to the text inside of the list item.|
+| `large` | Added to the content wrapper element if size is large.|
+| `primary` | Added to the text inside of the list item if its primary.|
+
+
## List Subheader
Simple subcomponent used to give a title to a list area.
+### Properties
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `caption` | `String` | | Text that will be displayed.|
| `className` | `String` | `''` | Set a class to give custom styles to the list subheader.|
-## List Divider
+### Theme
+| Name | Description|
+|:---------|:-----------|
+| `subheader` | Used as a wrapper class for the subheader element.|
-Simple subcomponent used to separate list sections or items. It has only one property `inset` which is a `Boolean` that indicates if the divider should be full with or should leave an space to the left side.
+## List Divider
+Simple subcomponent used to separate list sections or items. It has only one property `inset` which is a `Boolean` that indicates if the divider should be full with or should leave an space to the left side. It has two theming keys: `inset` and `divider` that will be used depending on wether it should full width or leave space.
diff --git a/components/list/style.scss b/components/list/theme.scss
similarity index 96%
rename from components/list/style.scss
rename to components/list/theme.scss
index d1b6e8d64..e432fcccb 100644
--- a/components/list/style.scss
+++ b/components/list/theme.scss
@@ -1,4 +1,6 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
.list {
@@ -43,6 +45,10 @@
> [data-react-toolbox="ripple"] {
overflow: hidden;
}
+
+ .ripple {
+ color: $color-text-secondary;
+ }
}
.item {
@@ -136,10 +142,6 @@
}
}
-.ripple {
- color: $color-text-secondary;
-}
-
.itemText {
display: block;
diff --git a/components/menu/IconMenu.js b/components/menu/IconMenu.js
index 68c908c80..bd321f990 100644
--- a/components/menu/IconMenu.js
+++ b/components/menu/IconMenu.js
@@ -1,68 +1,88 @@
-import React from 'react';
-import { IconButton } from '../button';
-import Menu from './Menu';
-import style from './style.icon_menu';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { MENU } from '../identifiers.js';
+import InjectIconButton from '../button/IconButton.js';
+import InjectMenu from './Menu.js';
-class IconMenu extends React.Component {
- static propTypes = {
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- icon: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.element
- ]),
- iconRipple: React.PropTypes.bool,
- menuRipple: React.PropTypes.bool,
- onClick: React.PropTypes.func,
- onHide: React.PropTypes.func,
- onSelect: React.PropTypes.func,
- onShow: React.PropTypes.func,
- position: React.PropTypes.string,
- selectable: React.PropTypes.bool,
- selected: React.PropTypes.any
- };
+const factory = (IconButton, Menu) => {
+ class IconMenu extends Component {
+ static propTypes = {
+ children: PropTypes.node,
+ className: PropTypes.string,
+ icon: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.element
+ ]),
+ iconRipple: PropTypes.bool,
+ menuRipple: PropTypes.bool,
+ onClick: PropTypes.func,
+ onHide: PropTypes.func,
+ onSelect: PropTypes.func,
+ onShow: PropTypes.func,
+ position: PropTypes.string,
+ selectable: PropTypes.bool,
+ selected: PropTypes.any,
+ theme: PropTypes.shape({
+ icon: PropTypes.string,
+ iconMenu: PropTypes.string
+ })
+ };
- static defaultProps = {
- className: '',
- icon: 'more_vert',
- iconRipple: true,
- menuRipple: true,
- position: 'auto',
- selectable: false
- };
+ static defaultProps = {
+ className: '',
+ icon: 'more_vert',
+ iconRipple: true,
+ menuRipple: true,
+ position: 'auto',
+ selectable: false
+ };
- handleButtonClick = (event) => {
- this.refs.menu.show();
- if (this.props.onClick) this.props.onClick(event);
- };
+ state = {
+ active: false
+ }
- render () {
- let className = style.root;
- if (this.props.className) className += ` ${this.props.className}`;
+ handleButtonClick = (event) => {
+ this.setState({ active: !this.state.active });
+ if (this.props.onClick) this.props.onClick(event);
+ };
- return (
-
-
-
- {this.props.children}
-
-
- );
+ handleMenuHide = () => {
+ this.setState({ active: false });
+ if (this.props.onHide) this.props.onHide();
+ }
+
+ render () {
+ return (
+
+
+
+ {this.props.children}
+
+
+ );
+ }
}
-}
-export default IconMenu;
+ return IconMenu;
+};
+
+const IconMenu = factory(InjectIconButton, InjectMenu);
+export default themr(MENU)(IconMenu);
+export { factory as iconMenuFactory };
+export { IconMenu };
diff --git a/components/menu/Menu.js b/components/menu/Menu.js
index 3e0371d79..b3cdc5eef 100644
--- a/components/menu/Menu.js
+++ b/components/menu/Menu.js
@@ -1,185 +1,214 @@
-import React from 'react';
+import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
-import ClassNames from 'classnames';
-import MenuItem from './MenuItem';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { MENU } from '../identifiers.js';
import { events, utils } from '../utils';
-import style from './style.menu';
+import InjectMenuItem from './MenuItem.js';
const POSITION = {
AUTO: 'auto',
STATIC: 'static',
- TOP_LEFT: 'top-left',
- TOP_RIGHT: 'top-right',
- BOTTOM_LEFT: 'bottom-left',
- BOTTOM_RIGHT: 'bottom-right'
+ TOP_LEFT: 'topLeft',
+ TOP_RIGHT: 'topRight',
+ BOTTOM_LEFT: 'bottomLeft',
+ BOTTOM_RIGHT: 'bottomRight'
};
-class Menu extends React.Component {
- static propTypes = {
- active: React.PropTypes.bool,
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- onHide: React.PropTypes.func,
- onSelect: React.PropTypes.func,
- onShow: React.PropTypes.func,
- outline: React.PropTypes.bool,
- position: React.PropTypes.string,
- ripple: React.PropTypes.bool,
- selectable: React.PropTypes.bool,
- selected: React.PropTypes.any
- };
-
- static defaultProps = {
- active: false,
- outline: true,
- position: POSITION.STATIC,
- ripple: true,
- selectable: true
- };
-
- state = {
- active: this.props.active,
- rippled: false
- };
-
- componentDidMount () {
- this.positionTimeoutHandle = setTimeout(() => {
- const { width, height } = this.refs.menu.getBoundingClientRect();
- const position = this.props.position === POSITION.AUTO ? this.calculatePosition() : this.props.position;
- this.setState({ position, width, height });
- });
- }
-
- componentWillReceiveProps (nextProps) {
- if (this.props.position !== nextProps.position) {
- const position = nextProps.position === POSITION.AUTO ? this.calculatePosition() : nextProps.position;
- this.setState({ position });
+const factory = (MenuItem) => {
+ class Menu extends Component {
+ static propTypes = {
+ active: PropTypes.bool,
+ children: PropTypes.node,
+ className: PropTypes.string,
+ onHide: PropTypes.func,
+ onSelect: PropTypes.func,
+ onShow: PropTypes.func,
+ outline: PropTypes.bool,
+ position: PropTypes.string,
+ ripple: PropTypes.bool,
+ selectable: PropTypes.bool,
+ selected: PropTypes.any,
+ theme: PropTypes.shape({
+ active: PropTypes.string,
+ bottomLeft: PropTypes.string,
+ bottomRight: PropTypes.string,
+ menu: PropTypes.string,
+ menuInner: PropTypes.string,
+ outline: PropTypes.string,
+ rippled: PropTypes.string,
+ static: PropTypes.string,
+ topLeft: PropTypes.string,
+ topRight: PropTypes.string
+ })
+ };
+
+ static defaultProps = {
+ active: false,
+ outline: true,
+ position: POSITION.STATIC,
+ ripple: true,
+ selectable: true
+ };
+
+ state = {
+ active: this.props.active,
+ rippled: false
+ };
+
+ componentDidMount () {
+ this.positionTimeoutHandle = setTimeout(() => {
+ const { width, height } = this.refs.menu.getBoundingClientRect();
+ const position = this.props.position === POSITION.AUTO ? this.calculatePosition() : this.props.position;
+ this.setState({ position, width, height });
+ });
}
- }
- shouldComponentUpdate (nextProps, nextState) {
- if (!this.state.active && nextState.active && this.props.position === POSITION.AUTO) {
- const position = this.calculatePosition();
- if (this.state.position !== position) {
- this.setState({ position, active: false }, () => {
- this.activateTimeoutHandle = setTimeout(() => {this.setState({active: true}); }, 20);
- });
- return false;
+ componentWillReceiveProps (nextProps) {
+ if (this.props.position !== nextProps.position) {
+ const position = nextProps.position === POSITION.AUTO ? this.calculatePosition() : nextProps.position;
+ this.setState({ position });
+ }
+
+ if (!this.props.active && nextProps.active && !this.state.active) {
+ this.show();
}
- }
- return true;
- }
- componentWillUpdate (nextProps, nextState) {
- if (!this.state.active && nextState.active) {
- events.addEventsToDocument({click: this.handleDocumentClick});
+ if (this.props.active && !nextProps.active && this.state.active) {
+ this.hide();
+ }
}
- }
- componentDidUpdate (prevProps, prevState) {
- if (prevState.active && !this.state.active) {
- if (this.props.onHide) this.props.onHide();
- events.removeEventsFromDocument({click: this.handleDocumentClick});
- } else if (!prevState.active && this.state.active && this.props.onShow) {
- this.props.onShow();
+ shouldComponentUpdate (nextProps, nextState) {
+ if (!this.state.active && nextState.active && this.props.position === POSITION.AUTO) {
+ const position = this.calculatePosition();
+ if (this.state.position !== position) {
+ this.setState({ position, active: false }, () => {
+ this.activateTimeoutHandle = setTimeout(() => {this.setState({active: true}); }, 20);
+ });
+ return false;
+ }
+ }
+ return true;
}
- }
- componentWillUnmount () {
- if (this.state.active) {
- events.removeEventsFromDocument({click: this.handleDocumentClick});
+ componentWillUpdate (nextProps, nextState) {
+ if (!this.state.active && nextState.active) {
+ events.addEventsToDocument({click: this.handleDocumentClick});
+ }
}
- clearTimeout(this.positionTimeoutHandle);
- clearTimeout(this.activateTimeoutHandle);
- }
- handleDocumentClick = (event) => {
- if (this.state.active && !events.targetIsDescendant(event, ReactDOM.findDOMNode(this))) {
- this.setState({active: false, rippled: false});
+ componentDidUpdate (prevProps, prevState) {
+ if (prevState.active && !this.state.active) {
+ if (this.props.onHide) this.props.onHide();
+ events.removeEventsFromDocument({click: this.handleDocumentClick});
+ } else if (!prevState.active && this.state.active && this.props.onShow) {
+ this.props.onShow();
+ }
}
- };
-
- handleSelect = (item) => {
- const { value, onClick } = item.props;
- this.setState({ active: false, rippled: this.props.ripple }, () => {
- if (onClick) onClick();
- if (this.props.onSelect) this.props.onSelect(value);
- });
- };
-
- calculatePosition () {
- const parentNode = ReactDOM.findDOMNode(this).parentNode;
- if (!parentNode) return;
- const {top, left, height, width} = parentNode.getBoundingClientRect();
- const {height: wh, width: ww} = utils.getViewport();
- const toTop = top < ((wh / 2) - height / 2);
- const toLeft = left < ((ww / 2) - width / 2);
- return `${toTop ? 'top' : 'bottom'}-${toLeft ? 'left' : 'right'}`;
- }
- getMenuStyle () {
- const { width, height, position } = this.state;
- if (position !== POSITION.STATIC) {
+ componentWillUnmount () {
if (this.state.active) {
- return { clip: `rect(0 ${width}px ${height}px 0)` };
- } else if (position === POSITION.TOP_RIGHT) {
- return { clip: `rect(0 ${width}px 0 ${width}px)` };
- } else if (position === POSITION.BOTTOM_RIGHT) {
- return { clip: `rect(${height}px ${width}px ${height}px ${width}px)` };
- } else if (position === POSITION.BOTTOM_LEFT) {
- return { clip: `rect(${height}px 0 ${height}px 0)` };
- } else if (position === POSITION.TOP_LEFT) {
- return { clip: 'rect(0 0 0 0)' };
+ events.removeEventsFromDocument({click: this.handleDocumentClick});
}
+ clearTimeout(this.positionTimeoutHandle);
+ clearTimeout(this.activateTimeoutHandle);
}
- }
- getRootStyle () {
- if (this.state.position !== POSITION.STATIC) {
- return { width: this.state.width, height: this.state.height };
+ handleDocumentClick = (event) => {
+ if (this.state.active && !events.targetIsDescendant(event, ReactDOM.findDOMNode(this))) {
+ this.setState({active: false, rippled: false});
+ }
+ };
+
+ handleSelect = (item) => {
+ const { value, onClick } = item.props;
+ this.setState({ active: false, rippled: this.props.ripple }, () => {
+ if (onClick) onClick();
+ if (this.props.onSelect) this.props.onSelect(value);
+ });
+ };
+
+ calculatePosition () {
+ const parentNode = ReactDOM.findDOMNode(this).parentNode;
+ if (!parentNode) return;
+ const {top, left, height, width} = parentNode.getBoundingClientRect();
+ const {height: wh, width: ww} = utils.getViewport();
+ const toTop = top < ((wh / 2) - height / 2);
+ const toLeft = left < ((ww / 2) - width / 2);
+ return `${toTop ? 'top' : 'bottom'}${toLeft ? 'Left' : 'Right'}`;
}
- }
- renderItems () {
- return React.Children.map(this.props.children, (item) => {
- if (!item) return item;
- if (item.type === MenuItem) {
- return React.cloneElement(item, {
- ripple: item.props.ripple || this.props.ripple,
- selected: typeof item.props.value !== 'undefined' && this.props.selectable && item.props.value === this.props.selected,
- onClick: this.handleSelect.bind(this, item)
- });
- } else {
- return React.cloneElement(item);
+ getMenuStyle () {
+ const { width, height, position } = this.state;
+ if (position !== POSITION.STATIC) {
+ if (this.state.active) {
+ return { clip: `rect(0 ${width}px ${height}px 0)` };
+ } else if (position === POSITION.TOP_RIGHT) {
+ return { clip: `rect(0 ${width}px 0 ${width}px)` };
+ } else if (position === POSITION.BOTTOM_RIGHT) {
+ return { clip: `rect(${height}px ${width}px ${height}px ${width}px)` };
+ } else if (position === POSITION.BOTTOM_LEFT) {
+ return { clip: `rect(${height}px 0 ${height}px 0)` };
+ } else if (position === POSITION.TOP_LEFT) {
+ return { clip: 'rect(0 0 0 0)' };
+ }
}
- });
- }
+ }
- show () {
- const { width, height } = this.refs.menu.getBoundingClientRect();
- this.setState({active: true, width, height});
- }
+ getRootStyle () {
+ if (this.state.position !== POSITION.STATIC) {
+ return { width: this.state.width, height: this.state.height };
+ }
+ }
- hide () {
- this.setState({active: false});
- }
+ renderItems () {
+ return React.Children.map(this.props.children, (item) => {
+ if (!item) return item;
+ if (item.type === MenuItem) {
+ return React.cloneElement(item, {
+ ripple: item.props.ripple || this.props.ripple,
+ selected: typeof item.props.value !== 'undefined' && this.props.selectable && item.props.value === this.props.selected,
+ onClick: this.handleSelect.bind(this, item)
+ });
+ } else {
+ return React.cloneElement(item);
+ }
+ });
+ }
- render () {
- const outlineStyle = { width: this.state.width, height: this.state.height };
- const className = ClassNames([style.root, style[this.state.position]], {
- [style.active]: this.state.active,
- [style.rippled]: this.state.rippled
- }, this.props.className);
-
- return (
-
- {this.props.outline ?
: null}
-
-
- );
+ show () {
+ const { width, height } = this.refs.menu.getBoundingClientRect();
+ this.setState({active: true, width, height});
+ }
+
+ hide () {
+ this.setState({active: false});
+ }
+
+ render () {
+ const { theme } = this.props;
+ const outlineStyle = { width: this.state.width, height: this.state.height };
+ const className = classnames([theme.menu, theme[this.state.position]], {
+ [theme.active]: this.state.active,
+ [theme.rippled]: this.state.rippled
+ }, this.props.className);
+
+ return (
+
+ {this.props.outline ?
: null}
+
+
+ );
+ }
}
-}
-export default Menu;
+ return Menu;
+};
+
+const Menu = factory(InjectMenuItem);
+export default themr(MENU)(Menu);
+export { factory as menuFactory };
+export { Menu };
diff --git a/components/menu/MenuDivider.js b/components/menu/MenuDivider.js
index 8eac0f304..b99cf87c8 100644
--- a/components/menu/MenuDivider.js
+++ b/components/menu/MenuDivider.js
@@ -1,8 +1,16 @@
-import React from 'react';
-import style from './style.menu_divider';
+import React, { PropTypes } from 'react';
+import { themr } from 'react-css-themr';
+import { MENU } from '../identifiers.js';
-const MenuDivider = () => (
-
+const MenuDivider = ({ theme }) => (
+
);
-export default MenuDivider;
+MenuDivider.propTypes = {
+ theme: PropTypes.shape({
+ menuDivider: PropTypes.string
+ })
+};
+
+export default themr(MENU)(MenuDivider);
+export { MenuDivider };
diff --git a/components/menu/MenuItem.js b/components/menu/MenuItem.js
index 76a4d426a..4ce2dc185 100644
--- a/components/menu/MenuItem.js
+++ b/components/menu/MenuItem.js
@@ -1,55 +1,68 @@
-import React from 'react';
-import FontIcon from '../font_icon';
-import ClassNames from 'classnames';
-import Ripple from '../ripple';
-import style from './style.menu_item';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { MENU } from '../identifiers.js';
+import FontIcon from '../font_icon/FontIcon.js';
+import rippleFactory from '../ripple/Ripple.js';
-class MenuItem extends React.Component {
- static propTypes = {
- caption: React.PropTypes.string.isRequired,
- children: React.PropTypes.any,
- className: React.PropTypes.string,
- disabled: React.PropTypes.bool,
- icon: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.element
- ]),
- onClick: React.PropTypes.func,
- selected: React.PropTypes.bool,
- shortcut: React.PropTypes.string
- };
+const factory = (ripple) => {
+ class MenuItem extends Component {
+ static propTypes = {
+ caption: PropTypes.string,
+ children: PropTypes.any,
+ className: PropTypes.string,
+ disabled: PropTypes.bool,
+ icon: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.element
+ ]),
+ onClick: PropTypes.func,
+ selected: PropTypes.bool,
+ shortcut: PropTypes.string,
+ theme: PropTypes.shape({
+ caption: PropTypes.string,
+ disabled: PropTypes.string,
+ icon: PropTypes.string,
+ menuItem: PropTypes.string,
+ selected: PropTypes.string,
+ shortcut: PropTypes.string
+ })
+ };
- static defaultProps = {
- className: '',
- disabled: false,
- selected: false
- };
+ static defaultProps = {
+ className: '',
+ disabled: false,
+ selected: false
+ };
- handleClick = (event) => {
- if (this.props.onClick && !this.props.disabled) {
- this.props.onClick(event, this);
- }
- };
+ handleClick = (event) => {
+ if (this.props.onClick && !this.props.disabled) {
+ this.props.onClick(event, this);
+ }
+ };
- render () {
- const {icon, caption, children, shortcut, selected, disabled, ...others} = this.props;
- const className = ClassNames(style.root, {
- [style.selected]: selected,
- [style.disabled]: disabled
- }, this.props.className);
+ render () {
+ const {icon, caption, children, shortcut, selected, disabled, theme, ...others} = this.props;
+ const className = classnames(theme.menuItem, {
+ [theme.selected]: selected,
+ [theme.disabled]: disabled
+ }, this.props.className);
- return (
-
- {icon ? : null}
- {caption}
- {shortcut ? {shortcut} : null}
- {children}
-
- );
+ return (
+
+ {icon ? : null}
+ {caption}
+ {shortcut ? {shortcut} : null}
+ {children}
+
+ );
+ }
}
-}
-export default Ripple({
- className: style.ripple
-})(MenuItem);
-export {MenuItem as RawMenuItem};
+ return ripple(MenuItem);
+};
+
+const MenuItem = factory(rippleFactory({}));
+export default themr(MENU)(MenuItem);
+export { factory as menuItemFactory };
+export { MenuItem };
diff --git a/components/menu/index.js b/components/menu/index.js
index 779e68d19..e5a52a3cb 100644
--- a/components/menu/index.js
+++ b/components/menu/index.js
@@ -1,4 +1,20 @@
-export Menu from './Menu';
-export MenuItem from './MenuItem';
-export MenuDivider from './MenuDivider';
-export IconMenu from './IconMenu';
+import { themr } from 'react-css-themr';
+import { MENU } from '../identifiers.js';
+import { IconButton } from '../button';
+import { MenuDivider } from './MenuDivider.js';
+import { menuItemFactory } from './MenuItem.js';
+import { menuFactory } from './Menu.js';
+import { iconMenuFactory } from './IconMenu.js';
+import themedRippleFactory from '../ripple';
+import theme from './theme.scss';
+
+const applyTheme = (Component) => themr(MENU, theme)(Component);
+const ThemedMenuDivider = applyTheme(MenuDivider);
+const ThemedMenuItem = applyTheme(menuItemFactory(themedRippleFactory({})));
+const ThemedMenu = applyTheme(menuFactory(ThemedMenuItem));
+const ThemedIconMenu = applyTheme(iconMenuFactory(IconButton, ThemedMenu));
+
+export { ThemedMenuDivider as MenuDivider };
+export { ThemedMenuItem as MenuItem };
+export { ThemedMenu as Menu };
+export { ThemedIconMenu as IconMenu };
diff --git a/components/menu/readme.md b/components/menu/readme.md
index 52bd9d870..ea795d63b 100644
--- a/components/menu/readme.md
+++ b/components/menu/readme.md
@@ -17,10 +17,14 @@ const MenuTest = () => (
);
```
+If you want to provide a theme for any of these subcomponents via context, the component key is `RTMenu`.
+
## Menu
This subcomponent is the default wrapper for a menu and is responsible for the opening behavior. Its properties can affect to the Item children.
+### Properties
+
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `active` | `Boolean` | `false` | If true, the menu will be displayed as opened by default.|
@@ -34,17 +38,27 @@ This subcomponent is the default wrapper for a menu and is responsible for the o
| `selectable` | `Boolean` | `false` | If true, the menu will keep a value to highlight the active child item. |
| `selected` | `Any` | | Used for selectable menus. Indicates the current selected value so the child item with this value can be highlighted. |
-The menu has state to keep a value with the currently selected item. It also exposes methods to show and hide the menu from the code:
-
-- `getValue` is used to get the current value.
-- `setValue` is used to set a new active value.
-- `show` is used to show the menu.
-- `hide` is used to hide the menu.
+### Theming
+
+| Name | Description|
+|:---------|:-----------|
+| `active` | Added to the root element when menu is active.|
+| `bottomLeft` | Added to the root when position is bottom left.|
+| `bottomRight` | Added to the root when position is bottom right.|
+| `menu` | Used for the root element of the menu.|
+| `menuInner` | Used for the inner wrapper.|
+| `outline` | Used to draw the outline.|
+| `rippled` | Added to the menu in case if should have ripple.|
+| `static` | Added to the root in case its static.|
+| `topLeft` | Added to the root when position is top left.|
+| `topRight` | Added to the root when position is top right.|
## Icon Menu
As the most usual scenario will be to open the menu from a click in an Icon, we provide this subcomponent implementing this behavior. The `IconMenu` shows an icon and implements a `Menu` under the covers that is shown when is clicked. Some of its properties are transferred to the menu, others to the children:
+### Properties
+
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `className` | `String` | `''` | Set a class to give custom styles to the icon wrapper.|
@@ -59,10 +73,19 @@ As the most usual scenario will be to open the menu from a click in an Icon, we
| `selectable` | `Boolean` | `false` | If true, the menu will keep a value to highlight the active child item. |
| `selected` | `Any` | | Used for selectable menus. Indicates the current selected value so the child item with this value can be highlighted. |
+### Theming
+
+| Name | Description|
+|:---------|:-----------|
+| `icon` | Used for the icon element.|
+| `iconMenu` | Used for the root element of the icon menu.|
+
## Menu Item
The inner component for menus and describes the content of each option. It behaves in a similar way to List Items but simpler.
+### Properties
+
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `caption` | `String` | | The text to include in the menu item. Required.|
@@ -73,6 +96,17 @@ The inner component for menus and describes the content of each option. It behav
| `selected` | `Boolean` | `false` | Transferred from the `Menu` component for selectable menus. Indicates if it's the current active option. |
| `shortcut` | `String` | `''` | Displays shortcut text on the right side of the `caption` attribute. |
+### Theming
+
+| Name | Description|
+|:---------|:-----------|
+| `caption` | Used for the caption inside the item.|
+| `disabled` | Added to the root element if it's disabled.|
+| `icon` | Used for the icon element if exists.|
+| `menuItem` | Used as the root class for the component.|
+| `selected` | Added to the root element in case it's selected.|
+| `shortcut` | Used for the shortcut element if exists.|
+
## Menu Divider
-A very simple component that just displays a separator between options. It has no props and no state or methods, just markup.
+A very simple component that just displays a separator between options. It has no props and no state or methods, just markup. Also it accepts a single class for the styling which is `menuDivider`.
diff --git a/components/menu/style.icon_menu.scss b/components/menu/style.icon_menu.scss
deleted file mode 100644
index e45b61838..000000000
--- a/components/menu/style.icon_menu.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-@import "../base";
-@import "./config";
-
-.root {
- position: relative;
- display: inline-block;
- text-align: center;
-}
-
-.icon {
- cursor: pointer;
-}
diff --git a/components/menu/style.menu_divider.scss b/components/menu/style.menu_divider.scss
deleted file mode 100644
index dcd63f78e..000000000
--- a/components/menu/style.menu_divider.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-@import "../base";
-@import "./config";
-
-.root {
- display: block;
- width: 100%;
- height: 1px;
- margin: $menu-divider-height 0;
- background-color: $color-divider;
-}
diff --git a/components/menu/style.menu_item.scss b/components/menu/style.menu_item.scss
deleted file mode 100644
index 33c9c724b..000000000
--- a/components/menu/style.menu_item.scss
+++ /dev/null
@@ -1,43 +0,0 @@
-@import "../base";
-@import "./config";
-
-.root {
- position: relative;
- display: flex;
- height: $menu-item-height;
- align-items: center;
- padding: 0 $menu-item-padding;
- overflow: hidden;
- font-size: $menu-item-font-size;
- color: $color-text;
- &:not(.disabled):hover {
- cursor: pointer;
- background-color: $menu-item-hover-background;
- }
- &.disabled {
- pointer-events: none;
- opacity: .5;
- }
- &.selected {
- font-weight: 500;
- background-color: $menu-item-selected-background;
- }
-}
-
-.icon {
- width: $menu-item-icon-size;
- font-size: $menu-item-icon-font-size !important;
-}
-
-.caption {
- flex-grow: 1;
- font-size: $font-size-normal;
-}
-
-.shortcut {
- margin-left: $menu-item-padding;
-}
-
-.ripple {
- color: $color-text-secondary;
-}
diff --git a/components/menu/style.menu.scss b/components/menu/theme.scss
similarity index 58%
rename from components/menu/style.menu.scss
rename to components/menu/theme.scss
index 0026f0419..9f4ec9b3c 100644
--- a/components/menu/style.menu.scss
+++ b/components/menu/theme.scss
@@ -1,10 +1,21 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
-.root {
+.iconMenu {
position: relative;
display: inline-block;
- &.top-left {
+ text-align: center;
+ .icon {
+ cursor: pointer;
+ }
+}
+
+.menu {
+ position: relative;
+ display: inline-block;
+ &.topLeft {
position: absolute;
top: 0;
left: 0;
@@ -12,7 +23,7 @@
transform-origin: 0 0;
}
}
- &.top-right {
+ &.topRight {
position: absolute;
top: 0;
right: 0;
@@ -20,7 +31,7 @@
transform-origin: 100% 0;
}
}
- &.bottom-left {
+ &.bottomLeft {
position: absolute;
bottom: 0;
left: 0;
@@ -28,7 +39,7 @@
transform-origin: 0 100%;
}
}
- &.bottom-right {
+ &.bottomRight {
position: absolute;
right: 0;
bottom: 0;
@@ -46,7 +57,7 @@
transform: scale(0);
will-change: transform;
}
- > .menu {
+ > .menuInner {
position: absolute;
top: 0;
left: 0;
@@ -56,7 +67,7 @@
> .outline {
transition-delay: $menu-ripple-delay;
}
- > .menu {
+ > .menuInner {
transition-delay: $menu-ripple-delay;
}
}
@@ -66,7 +77,7 @@
opacity: 1;
transform: scale(1);
}
- > .menu {
+ > .menuInner {
opacity: 1;
transition: opacity $menu-fade-duration $animation-curve-default,
clip $menu-expand-duration $animation-curve-default;
@@ -85,7 +96,7 @@
border-radius: $menu-outline-border-radius;
}
-.menu {
+.menuInner {
position: relative;
display: block;
padding: $menu-padding;
@@ -93,3 +104,51 @@
white-space: nowrap;
list-style: none;
}
+
+.menuItem {
+ position: relative;
+ display: flex;
+ height: $menu-item-height;
+ align-items: center;
+ padding: 0 $menu-item-padding;
+ overflow: hidden;
+ font-size: $menu-item-font-size;
+ color: $color-text;
+ &:not(.disabled):hover {
+ cursor: pointer;
+ background-color: $menu-item-hover-background;
+ }
+ &.disabled {
+ pointer-events: none;
+ opacity: .5;
+ }
+ &.selected {
+ font-weight: 500;
+ background-color: $menu-item-selected-background;
+ }
+ .ripple {
+ color: $color-text-secondary;
+ }
+
+ .icon {
+ width: $menu-item-icon-size;
+ font-size: $menu-item-icon-font-size !important;
+ }
+}
+
+.caption {
+ flex-grow: 1;
+ font-size: $font-size-normal;
+}
+
+.shortcut {
+ margin-left: $menu-item-padding;
+}
+
+.menuDivider {
+ display: block;
+ width: 100%;
+ height: 1px;
+ margin: $menu-divider-height 0;
+ background-color: $color-divider;
+}
diff --git a/components/navigation/Navigation.js b/components/navigation/Navigation.js
index be1b92078..79d384ae3 100644
--- a/components/navigation/Navigation.js
+++ b/components/navigation/Navigation.js
@@ -1,42 +1,55 @@
-import React from 'react';
-import style from './style';
-import Button from '../button';
-import Link from '../link';
+import React, { PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { NAVIGATION } from '../identifiers.js';
+import InjectButton from '../button/Button.js';
+import InjectLink from '../link/Link.js';
-const Navigation = props => {
- let className = `${style[props.type]}`;
- if (props.className) className += ` ${props.className}`;
+const factory = (Button, Link) => {
+ const Navigation = ({ actions, children, className, routes, theme, type }) => {
+ const _className = classnames(theme[type], className);
+ const buttons = actions.map((action, index) => {
+ return ;
+ });
- const buttons = props.actions.map((action, index) => {
- return ;
- });
+ const links = routes.map((route, index) => {
+ return ;
+ });
- const links = props.routes.map((route, index) => {
- return ;
- });
+ return (
+
+ {links}
+ {buttons}
+ {children}
+
+ );
+ };
- return (
-
- {links}
- {buttons}
- {props.children}
-
- );
-};
+ Navigation.propTypes = {
+ actions: PropTypes.array,
+ children: PropTypes.node,
+ className: PropTypes.string,
+ routes: PropTypes.array,
+ theme: PropTypes.shape({
+ button: PropTypes.string,
+ horizontal: PropTypes.string,
+ link: PropTypes.string,
+ vertical: PropTypes.string
+ }),
+ type: PropTypes.oneOf(['vertical', 'horizontal'])
+ };
-Navigation.propTypes = {
- actions: React.PropTypes.array,
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- routes: React.PropTypes.array,
- type: React.PropTypes.oneOf(['vertical', 'horizontal'])
-};
+ Navigation.defaultProps = {
+ actions: [],
+ className: '',
+ type: 'horizontal',
+ routes: []
+ };
-Navigation.defaultProps = {
- actions: [],
- className: '',
- type: 'horizontal',
- routes: []
+ return Navigation;
};
-export default Navigation;
+const Navigation = factory(InjectButton, InjectLink);
+export default themr(NAVIGATION)(Navigation);
+export { factory as navigationFactory };
+export { Navigation };
diff --git a/components/navigation/index.js b/components/navigation/index.js
index 173ff5a1c..d02ab54d0 100644
--- a/components/navigation/index.js
+++ b/components/navigation/index.js
@@ -1 +1,10 @@
-export default from './Navigation';
+import { themr } from 'react-css-themr';
+import { NAVIGATION } from '../identifiers.js';
+import { navigationFactory } from './Navigation.js';
+import { Button } from '../button';
+import { Link } from '../link';
+import theme from './theme.scss';
+
+const ThemedNavigation = themr(NAVIGATION, theme)(navigationFactory(Button, Link));
+export default ThemedNavigation;
+export { ThemedNavigation as Navigation };
diff --git a/components/navigation/readme.md b/components/navigation/readme.md
index 0540ca8c1..3d143e704 100644
--- a/components/navigation/readme.md
+++ b/components/navigation/readme.md
@@ -23,6 +23,8 @@ const NavigationTest = () => (
);
```
+The theming for this component can be provided using the key `RTNavigation`.
+
## Properties
| Name | Type | Default | Description|
@@ -31,3 +33,12 @@ const NavigationTest = () => (
| `className` | `String` | | Set a custom class styles to style the navigation.|
| `routes` | `Array` | | Array of objects similar to actions but that will be rendered as ` ` component definition. |
| `type` | `String` | `horizontal` | Type of the navigation, it can be `vertical` or `horizontal`.|
+
+## Theming
+
+| Name | Description|
+|:---------|:-----------|
+| `button` | Used for buttons provided in the component.|
+| `horizontal` | Used for the root element if the layout is horizontal.|
+| `link` | Used for links provided in the component.|
+| `vertical` | Used for the root element if the layout is vertical.|
diff --git a/components/navigation/style.scss b/components/navigation/theme.scss
similarity index 87%
rename from components/navigation/style.scss
rename to components/navigation/theme.scss
index b8066c0c9..2d8375fd2 100644
--- a/components/navigation/style.scss
+++ b/components/navigation/theme.scss
@@ -1,4 +1,6 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
.horizontal {
diff --git a/components/overlay/Overlay.js b/components/overlay/Overlay.js
index 467221f36..f7cfe05e2 100644
--- a/components/overlay/Overlay.js
+++ b/components/overlay/Overlay.js
@@ -1,16 +1,23 @@
-import React from 'react';
-import Portal from '../hoc/Portal';
-import ClassNames from 'classnames';
-import style from './style';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { OVERLAY } from '../identifiers.js';
+import Portal from '../hoc/Portal.js';
-class Overlay extends React.Component {
+class Overlay extends Component {
static propTypes = {
- active: React.PropTypes.bool,
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- invisible: React.PropTypes.bool,
- onClick: React.PropTypes.func,
- onEscKeyDown: React.PropTypes.func
+ active: PropTypes.bool,
+ children: PropTypes.node,
+ className: PropTypes.string,
+ invisible: PropTypes.bool,
+ onClick: PropTypes.func,
+ onEscKeyDown: PropTypes.func,
+ theme: PropTypes.shape({
+ active: PropTypes.string,
+ backdrop: PropTypes.string,
+ invisible: PropTypes.string,
+ overlay: PropTypes.string
+ })
};
static defaultProps = {
@@ -49,20 +56,22 @@ class Overlay extends React.Component {
}
render () {
- const className = ClassNames(style.root, {
- [style.active]: this.props.active,
- [style.invisible]: this.props.invisible
- }, this.props.className);
+ const { active, className, children, invisible, onClick, theme } = this.props;
+ const _className = classnames(theme.overlay, {
+ [theme.active]: active,
+ [theme.invisible]: invisible
+ }, className);
return (
-
-
- {this.props.children}
+
);
}
}
-export default Overlay;
+export default themr(OVERLAY)(Overlay);
+export { Overlay };
diff --git a/components/overlay/index.js b/components/overlay/index.js
index 5be89753e..818edb280 100644
--- a/components/overlay/index.js
+++ b/components/overlay/index.js
@@ -1 +1,8 @@
-export default from './Overlay';
+import { themr } from 'react-css-themr';
+import { OVERLAY } from '../identifiers.js';
+import { Overlay } from './Overlay.js';
+import theme from './theme.scss';
+
+const ThemedOverlay = themr(OVERLAY, theme)(Overlay);
+export default ThemedOverlay;
+export { ThemedOverlay as Overlay };
diff --git a/components/overlay/style.scss b/components/overlay/theme.scss
similarity index 87%
rename from components/overlay/style.scss
rename to components/overlay/theme.scss
index 60395fb7f..058bb466b 100644
--- a/components/overlay/style.scss
+++ b/components/overlay/theme.scss
@@ -1,7 +1,9 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
-.root {
+.overlay {
position: fixed;
top: 0;
left: 0;
@@ -19,7 +21,7 @@
}
}
-.overlay {
+.backdrop {
position: absolute;
top: 0;
left: 0;
@@ -34,7 +36,7 @@
.active {
pointer-events: all;
- > .overlay {
+ > .backdrop {
opacity: $overlay-opacity;
}
}
diff --git a/components/progress_bar/ProgressBar.js b/components/progress_bar/ProgressBar.js
index 7deaef32b..ac75e3554 100644
--- a/components/progress_bar/ProgressBar.js
+++ b/components/progress_bar/ProgressBar.js
@@ -1,18 +1,29 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import style from './style';
-import prefixer from '../utils/prefixer';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { PROGRESS_BAR } from '../identifiers.js';
+import prefixer from '../utils/prefixer.js';
-class ProgressBar extends React.Component {
+class ProgressBar extends Component {
static propTypes = {
- buffer: React.PropTypes.number,
- className: React.PropTypes.string,
- max: React.PropTypes.number,
- min: React.PropTypes.number,
- mode: React.PropTypes.string,
- multicolor: React.PropTypes.bool,
- type: React.PropTypes.oneOf(['linear', 'circular']),
- value: React.PropTypes.number
+ buffer: PropTypes.number,
+ className: PropTypes.string,
+ max: PropTypes.number,
+ min: PropTypes.number,
+ mode: PropTypes.oneOf(['determinate', 'indeterminate']),
+ multicolor: PropTypes.bool,
+ theme: PropTypes.shape({
+ buffer: PropTypes.string,
+ circle: PropTypes.string,
+ circular: PropTypes.string,
+ indeterminate: PropTypes.string,
+ linear: PropTypes.string,
+ multicolor: PropTypes.string,
+ path: PropTypes.string,
+ value: PropTypes.string
+ }),
+ type: PropTypes.oneOf(['linear', 'circular']),
+ value: PropTypes.number
};
static defaultProps = {
@@ -51,8 +62,8 @@ class ProgressBar extends React.Component {
renderCircular () {
return (
-
-
+
+
);
}
@@ -61,30 +72,32 @@ class ProgressBar extends React.Component {
const {buffer, value} = this.linearStyle();
return (
-
-
+
+
);
}
render () {
- const className = ClassNames(style[this.props.type], {
- [style[this.props.mode]]: this.props.mode,
- [style.multicolor]: this.props.multicolor
- }, this.props.className);
+ const { className, max, min, mode, multicolor, type, theme, value } = this.props;
+ const _className = classnames(theme[type], {
+ [theme[mode]]: mode,
+ [theme.multicolor]: multicolor
+ }, className);
return (
- {this.props.type === 'circular' ? this.renderCircular() : this.renderLinear()}
+ {type === 'circular' ? this.renderCircular() : this.renderLinear()}
);
}
}
-export default ProgressBar;
+export default themr(PROGRESS_BAR)(ProgressBar);
+export { ProgressBar };
diff --git a/components/progress_bar/__test__/index.spec.js b/components/progress_bar/__test__/index.spec.js
index 4e7a983b0..52e9ae174 100644
--- a/components/progress_bar/__test__/index.spec.js
+++ b/components/progress_bar/__test__/index.spec.js
@@ -1,14 +1,17 @@
+import React from 'react';
import expect from 'expect';
-import style from '../../progress_bar/style';
+import TestUtils from 'react-addons-test-utils';
+import ProgressBar, { ProgressBar as RawProgressBar } from '../ProgressBar';
+import theme from '../theme.scss';
import utils from '../../utils/testing';
-import ProgressBar from '../index';
describe('ProgressBar', function () {
let progressBar;
describe('#calculateRatio', function () {
before(function () {
- progressBar = utils.renderComponent(ProgressBar, {min: 100, max: 300});
+ const tree = TestUtils.renderIntoDocument( );
+ progressBar = TestUtils.findRenderedComponentWithType(tree, RawProgressBar);
});
it('calculates the right ratio', function () {
@@ -28,14 +31,14 @@ describe('ProgressBar', function () {
let buffer, value, wrapper, circle, strokeLength;
it('renders the value and buffer bars when it is linear', function () {
- wrapper = utils.shallowRenderComponent(ProgressBar).props.children;
+ wrapper = utils.shallowRenderComponent(RawProgressBar, {theme}).props.children;
expect(wrapper.props.children.length).toEqual(2);
expect(wrapper.props.children[0].ref).toEqual('buffer');
expect(wrapper.props.children[1].ref).toEqual('value');
});
it('renders the value and buffer bars when it is linear', function () {
- progressBar = utils.shallowRenderComponent(ProgressBar, {mode: 'determinate', value: 30, buffer: 60});
+ progressBar = utils.shallowRenderComponent(RawProgressBar, {mode: 'determinate', value: 30, buffer: 60, theme});
buffer = (progressBar.props.children.props.children[0]);
value = (progressBar.props.children.props.children[1]);
expect(buffer.props.style.transform).toEqual(`scaleX(${0.6})`);
@@ -43,22 +46,22 @@ describe('ProgressBar', function () {
});
it('renders the svg circle when it is circular', function () {
- progressBar = utils.shallowRenderComponent(ProgressBar, {type: 'circular'});
+ progressBar = utils.shallowRenderComponent(RawProgressBar, {type: 'circular', theme});
expect(progressBar.props.children.type).toEqual('svg');
expect(progressBar.props.children.props.children.type).toEqual('circle');
});
it('renders the proper circle length style when it is circular and determinate', function () {
- progressBar = utils.shallowRenderComponent(ProgressBar, {type: 'circular', mode: 'determinate', value: 30});
+ progressBar = utils.shallowRenderComponent(RawProgressBar, {type: 'circular', mode: 'determinate', value: 30, theme});
circle = progressBar.props.children.props.children;
strokeLength = 2 * Math.PI * circle.props.r * 0.3;
expect(circle.props.style.strokeDasharray).toEqual(`${strokeLength}, 400`);
});
it('contains mode and className in its className', function () {
- progressBar = utils.shallowRenderComponent(ProgressBar, {mode: 'determinate', className: 'tight'});
- expect(progressBar.props.className).toContain(style.determinate);
- expect(progressBar.props.className).toContain(style.tight);
+ progressBar = utils.shallowRenderComponent(RawProgressBar, {mode: 'determinate', className: 'tight', theme});
+ expect(progressBar.props.className).toContain(theme.determinate);
+ expect(progressBar.props.className).toContain(theme.tight);
});
});
});
diff --git a/components/progress_bar/index.js b/components/progress_bar/index.js
index f9d75e8cd..6c4a36d1d 100644
--- a/components/progress_bar/index.js
+++ b/components/progress_bar/index.js
@@ -1 +1,9 @@
-export default from './ProgressBar';
+import { themr } from 'react-css-themr';
+import { PROGRESS_BAR } from '../identifiers.js';
+import { ProgressBar } from './ProgressBar.js';
+import theme from './theme.scss';
+
+const ThemedProgressBar = themr(PROGRESS_BAR, theme)(ProgressBar);
+
+export default ThemedProgressBar;
+export { ThemedProgressBar as ProgressBar };
diff --git a/components/progress_bar/readme.md b/components/progress_bar/readme.md
index 9df82588d..760af9f4e 100644
--- a/components/progress_bar/readme.md
+++ b/components/progress_bar/readme.md
@@ -9,11 +9,13 @@ import ProgressBar from 'react-toolbox/lib/progress_bar';
const ProgressTest = () => (
);
```
+If you want to provide a theme via context, the component key is `RTProgressBar`.
+
## Properties
| Name | Type | Default | Description|
@@ -27,3 +29,15 @@ const ProgressTest = () => (
| `type` | `String` | `linear` | Type of the progress bar, it can be `circular` or `linear`.|
| `value` | `Number` | `0` | Value of the current progress.|
+## Theme
+
+| Name | Description|
+|:---------|:-----------|
+| `buffer` | Used to style the buffer element in the linear progress.|
+| `circle` | Used for the circle element in the circular progress.|
+| `circular` | Used for the root element when the type is circular.|
+| `indeterminate` | Added to the root element if mode is indeterminate.|
+| `linear` | Used for the root element when the type is linear.|
+| `multicolor` | Added to the root if the component is multicolor (circular).|
+| `path` | Used for the inner path in the circular progress.|
+| `value` | Used to style the value element in the linear progress.|
diff --git a/components/progress_bar/style.scss b/components/progress_bar/theme.scss
similarity index 97%
rename from components/progress_bar/style.scss
rename to components/progress_bar/theme.scss
index e34bc766e..bcbc64d90 100644
--- a/components/progress_bar/style.scss
+++ b/components/progress_bar/theme.scss
@@ -1,4 +1,6 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
.linear {
diff --git a/components/radio/Radio.js b/components/radio/Radio.js
index 872dc7420..713cffec0 100644
--- a/components/radio/Radio.js
+++ b/components/radio/Radio.js
@@ -1,21 +1,27 @@
import React, { PropTypes } from 'react';
-import Ripple from '../ripple';
-import style from './style';
-const Radio = ({checked, children, onMouseDown}) => {
- const className = style[checked ? 'radio-checked' : 'radio'];
- return {children}
;
-};
+const factory = (ripple) => {
+ const Radio = ({checked, onMouseDown, theme, ...other}) => (
+
+ );
+
+ Radio.propTypes = {
+ checked: PropTypes.bool,
+ children: PropTypes.any,
+ onMouseDown: PropTypes.func,
+ theme: PropTypes.shape({
+ radio: PropTypes.string,
+ radioChecked: PropTypes.string,
+ ripple: PropTypes.string
+ })
+ };
-Radio.propTypes = {
- checked: PropTypes.bool,
- children: PropTypes.any,
- onMouseDown: PropTypes.func
+ return ripple(Radio);
};
-export default Ripple({
- className: style.ripple,
- spread: 2.6,
- centered: true
-})(Radio);
-export {Radio as RawRadio};
+export default factory;
diff --git a/components/radio/RadioButton.js b/components/radio/RadioButton.js
index ca7eefe59..4ac6871ac 100644
--- a/components/radio/RadioButton.js
+++ b/components/radio/RadioButton.js
@@ -1,60 +1,75 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import Radio from './Radio';
-import style from './style';
-
-class RadioButton extends React.Component {
- static propTypes = {
- checked: React.PropTypes.bool,
- className: React.PropTypes.string,
- disabled: React.PropTypes.bool,
- label: React.PropTypes.string,
- name: React.PropTypes.string,
- onBlur: React.PropTypes.func,
- onChange: React.PropTypes.func,
- onFocus: React.PropTypes.func,
- value: React.PropTypes.any
- };
-
- static defaultProps = {
- checked: false,
- className: '',
- disabled: false
- };
-
- handleClick = (event) => {
- const {checked, disabled, onChange} = this.props;
- if (event.pageX !== 0 && event.pageY !== 0) this.blur();
- if (!disabled && !checked && onChange) onChange(event, this);
- };
-
- blur () {
- this.refs.input.blur();
- }
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { RADIO } from '../identifiers.js';
+import rippleFactory from '../ripple/Ripple.js';
+import radioFactory from './Radio.js';
- focus () {
- this.refs.input.focus();
- }
+const factory = (Radio) => {
+ class RadioButton extends Component {
+ static propTypes = {
+ checked: PropTypes.bool,
+ className: PropTypes.string,
+ disabled: PropTypes.bool,
+ label: PropTypes.string,
+ name: PropTypes.string,
+ onBlur: PropTypes.func,
+ onChange: PropTypes.func,
+ onFocus: PropTypes.func,
+ theme: PropTypes.shape({
+ disabled: PropTypes.string,
+ field: PropTypes.string,
+ input: PropTypes.string,
+ text: PropTypes.string
+ }),
+ value: PropTypes.any
+ };
+
+ static defaultProps = {
+ checked: false,
+ className: '',
+ disabled: false
+ };
+
+ handleClick = (event) => {
+ const {checked, disabled, onChange} = this.props;
+ if (event.pageX !== 0 && event.pageY !== 0) this.blur();
+ if (!disabled && !checked && onChange) onChange(event, this);
+ };
- render () {
- const className = ClassNames(style[this.props.disabled ? 'disabled' : 'field'], this.props.className);
- const { onChange, ...others } = this.props; //eslint-disable-line no-unused-vars
-
- return (
-
-
-
- {this.props.label ? {this.props.label} : null}
-
- );
+ blur () {
+ this.refs.input.blur();
+ }
+
+ focus () {
+ this.refs.input.focus();
+ }
+
+ render () {
+ const { className, checked, disabled, label, theme, onChange, ...others } = this.props; // eslint-disable-line
+ const _className = classnames(theme[this.props.disabled ? 'disabled' : 'field'], className);
+ return (
+
+
+
+ {label ? {label} : null}
+
+ );
+ }
}
-}
-export default RadioButton;
+ return RadioButton;
+};
+
+const Radio = radioFactory(rippleFactory({ centered: true, spread: 2.6 }));
+const RadioButton = factory(Radio);
+export default themr(RADIO)(RadioButton);
+export { factory as radioButtonFactory };
+export { RadioButton };
diff --git a/components/radio/RadioGroup.js b/components/radio/RadioGroup.js
index 48dee7c3f..fa86ebcb1 100644
--- a/components/radio/RadioGroup.js
+++ b/components/radio/RadioGroup.js
@@ -1,48 +1,57 @@
-import React from 'react';
-import RadioButton from './RadioButton';
+import React, { Component, PropTypes } from 'react';
+import { themr } from 'react-css-themr';
+import { RADIO } from '../identifiers.js';
+import InjectRadioButton from './RadioButton.js';
-class RadioGroup extends React.Component {
- static propTypes = {
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- disabled: React.PropTypes.bool,
- name: React.PropTypes.string,
- onChange: React.PropTypes.func,
- value: React.PropTypes.any
- };
+const factory = (RadioButton) => {
+ class RadioGroup extends Component {
+ static propTypes = {
+ children: PropTypes.node,
+ className: PropTypes.string,
+ disabled: PropTypes.bool,
+ name: PropTypes.string,
+ onChange: PropTypes.func,
+ value: PropTypes.any
+ };
- static defaultProps = {
- className: '',
- disabled: false
- };
+ static defaultProps = {
+ className: '',
+ disabled: false
+ };
- handleChange = (value) => {
- if (this.props.onChange) this.props.onChange(value);
- };
+ handleChange = (value) => {
+ if (this.props.onChange) this.props.onChange(value);
+ };
- renderRadioButtons () {
- return React.Children.map(this.props.children, (radio, idx) => {
+ renderRadioButtons () {
+ return React.Children.map(this.props.children, (radio, idx) => {
+ return (
+
+ );
+ });
+ }
+
+ render () {
return (
-
+
+ {this.renderRadioButtons()}
+
);
- });
+ }
}
- render () {
- return (
-
- {this.renderRadioButtons()}
-
- );
- }
-}
+ return RadioGroup;
+};
-export default RadioGroup;
+const RadioGroup = factory(InjectRadioButton);
+export default themr(RADIO)(RadioGroup);
+export { factory as radioGroupFactory };
+export { RadioGroup };
diff --git a/components/radio/index.js b/components/radio/index.js
index bd8f8fcc8..62167cce4 100644
--- a/components/radio/index.js
+++ b/components/radio/index.js
@@ -1,2 +1,15 @@
-export RadioButton from './RadioButton';
-export RadioGroup from './RadioGroup';
+import { themr } from 'react-css-themr';
+import { RADIO } from '../identifiers.js';
+import themedRippleFactory from '../ripple';
+import radioFactory from './Radio.js';
+import { radioButtonFactory } from './RadioButton.js';
+import { radioGroupFactory } from './RadioGroup.js';
+import theme from './theme.scss';
+
+const ThemedRadio = radioFactory(themedRippleFactory({ centered: true, spread: 2.6}));
+const ThemedRadioButton = themr(RADIO, theme)(radioButtonFactory(ThemedRadio));
+const ThemedRadioGroup = themr(RADIO, theme)(radioGroupFactory(ThemedRadioButton));
+
+export default ThemedRadioButton;
+export { ThemedRadioButton as RadioButton };
+export { ThemedRadioGroup as RadioGroup };
diff --git a/components/radio/readme.md b/components/radio/readme.md
index 87ac159b9..dcdae9b1f 100644
--- a/components/radio/readme.md
+++ b/components/radio/readme.md
@@ -2,6 +2,8 @@
[Radio buttons](https://www.google.com/design/spec/components/selection-controls.html#selection-controls-radio-button) allow the user to select one option from a set. Use radio buttons for exclusive selection if you think that the user needs to see all available options side-by-side. Otherwise, consider a dropdown, which uses less space than displaying all options. They should always be used along with `RadioGroup`.
+You can provide the theme for this component using the key `ToolboxButton`
+
```jsx
import { RadioGroup, RadioButton } from 'react-toolbox/lib/radio';
@@ -32,6 +34,8 @@ class RadioTest extends React.Component {
A radio selector is mean to get a value from a set of choices, that's why a radio group is needed. It can take some properties and actions that will be transferred to the children, but they also can behave independently.
+### Properties
+
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `className` | `String` | `''` | Set a class to give custom styles to the group.|
@@ -40,14 +44,13 @@ A radio selector is mean to get a value from a set of choices, that's why a radi
| `onChange` | `Function` | | Callback function that will be invoked when the value changes. |
| `value` | `Any` | | Default value selected in the radio group. |
-This component has state to keep the currently selected value and that's why it exposes to methods to work from the code with it:
-- `getValue` used to retrieve the currently selected value.
-- `setValue` used to set a new value.
## Radio Button
The inner component to compose radio selectors. They will be rendered as radio input elements of HTML transferring the given properties that concerns to them.
+### Properties
+
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `checked` | `Boolean` | `false` | If true, the input element will be selected by default. Transferred from the parent. |
@@ -59,3 +62,15 @@ The inner component to compose radio selectors. They will be rendered as radio i
| `onChange` | `Function` | | Callback function that will be invoked when the value changes. |
| `onFocus` | `Function` | | Callback function that will be invoked when the input is focused. |
| `value` | `Any` | | Value for the radio button. |
+
+### Theming
+
+| Name | Description|
+|:---------|:-----------|
+| `disabled` | Added to the root of the Radio in case it's disabled.|
+| `field` | Used as the root class of the component.|
+| `input` | Used for the input element.|
+| `radio` | Used to for the radio element.|
+| `radioChecked` | Used for the radio element when it's checked.|
+| `ripple` | To provide styles for the ripple.|
+| `text` | Used to style the text label element.|
diff --git a/components/radio/style.scss b/components/radio/theme.scss
similarity index 88%
rename from components/radio/style.scss
rename to components/radio/theme.scss
index 21567db06..5d0645089 100644
--- a/components/radio/style.scss
+++ b/components/radio/theme.scss
@@ -1,6 +1,46 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
+.radio {
+ position: relative;
+ display: inline-block;
+ width: $radio-button-size;
+ height: $radio-button-size;
+ vertical-align: top;
+ cursor: pointer;
+ border: .2 * $unit solid $radio-text-color;
+ border-radius: 50%;
+ &:before {
+ @include material-animation-default();
+ position: absolute;
+ top: $radio-inner-margin - .2 * $unit;
+ left: $radio-inner-margin - .2 * $unit;
+ width: $radio-button-size - $radio-inner-margin * 2;
+ height: $radio-button-size - $radio-inner-margin * 2;
+ content: "";
+ background-color: $radio-inner-color;
+ border-radius: 50%;
+ transition: transform;
+ transform: scale(0);
+ }
+
+ .ripple {
+ background-color: $radio-inner-color;
+ opacity: .3;
+ transition-duration: 650ms;
+ }
+}
+
+.radioChecked {
+ @extend .radio;
+ border: .2 * $unit solid $radio-inner-color;
+ &:before {
+ transform: scale(1);
+ }
+}
+
.field {
position: relative;
display: block;
@@ -32,49 +72,11 @@
&:focus ~ .radio {
box-shadow: 0 0 0 $unit $radio-focus-color;
}
- &:focus ~ .radio-checked {
+ &:focus ~ .radioChecked {
box-shadow: 0 0 0 $unit $radio-checked-focus-color;
}
}
-.radio {
- position: relative;
- display: inline-block;
- width: $radio-button-size;
- height: $radio-button-size;
- vertical-align: top;
- cursor: pointer;
- border: .2 * $unit solid $radio-text-color;
- border-radius: 50%;
- &:before {
- @include material-animation-default();
- position: absolute;
- top: $radio-inner-margin - .2 * $unit;
- left: $radio-inner-margin - .2 * $unit;
- width: $radio-button-size - $radio-inner-margin * 2;
- height: $radio-button-size - $radio-inner-margin * 2;
- content: "";
- background-color: $radio-inner-color;
- border-radius: 50%;
- transition: transform;
- transform: scale(0);
- }
-}
-
-.radio-checked {
- @extend .radio;
- border: .2 * $unit solid $radio-inner-color;
- &:before {
- transform: scale(1);
- }
-}
-
-.ripple {
- background-color: $radio-inner-color;
- opacity: .3;
- transition-duration: 650ms;
-}
-
.disabled {
@extend .field;
.text {
@@ -84,7 +86,7 @@
cursor: auto;
border-color: $radio-disabled-color;
}
- .radio-checked {
+ .radioChecked {
cursor: auto;
border-color: $radio-disabled-color;
&:before {
diff --git a/components/ripple/Ripple.js b/components/ripple/Ripple.js
index 382b53cc8..cabd88e9a 100644
--- a/components/ripple/Ripple.js
+++ b/components/ripple/Ripple.js
@@ -1,34 +1,43 @@
-import React from 'react';
+import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
-import ClassNames from 'classnames';
-import style from './style';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { RIPPLE } from '../identifiers.js';
import events from '../utils/events';
import prefixer from '../utils/prefixer';
const defaults = {
centered: false,
className: '',
- spread: 2
+ spread: 2,
+ theme: {}
};
-const Ripple = (options = {}) => {
+const rippleFactory = (options = {}) => {
const {
centered: defaultCentered,
className: defaultClassName,
spread: defaultSpread,
+ theme: defaultTheme,
...props
} = {...defaults, ...options};
return ComposedComponent => {
- return class RippledComponent extends React.Component {
+ class RippledComponent extends Component {
static propTypes = {
- children: React.PropTypes.any,
- disabled: React.PropTypes.bool,
- onRippleEnded: React.PropTypes.func,
- ripple: React.PropTypes.bool,
- rippleCentered: React.PropTypes.bool,
- rippleClassName: React.PropTypes.string,
- rippleSpread: React.PropTypes.number
+ children: PropTypes.any,
+ disabled: PropTypes.bool,
+ onRippleEnded: PropTypes.func,
+ ripple: PropTypes.bool,
+ rippleCentered: PropTypes.bool,
+ rippleClassName: PropTypes.string,
+ rippleSpread: PropTypes.number,
+ theme: PropTypes.shape({
+ ripple: PropTypes.string,
+ rippleActive: PropTypes.string,
+ rippleRestarting: PropTypes.string,
+ rippleWrapper: PropTypes.string
+ })
};
static defaultProps = {
@@ -110,9 +119,9 @@ const Ripple = (options = {}) => {
...other
} = this.props;
- const rippleClassName = ClassNames(style.normal, {
- [style.active]: this.state.active,
- [style.restarting]: this.state.restarting
+ const rippleClassName = classnames(this.props.theme.ripple, {
+ [this.props.theme.rippleActive]: this.state.active,
+ [this.props.theme.rippleRestarting]: this.state.restarting
}, className);
const { left, top, width } = this.state;
@@ -124,15 +133,17 @@ const Ripple = (options = {}) => {
return (
{children ? children : null}
-
+
);
}
}
- };
+ }
+
+ return themr(RIPPLE, defaultTheme)(RippledComponent);
};
};
-export default Ripple;
+export default rippleFactory;
diff --git a/components/ripple/index.js b/components/ripple/index.js
index b065179d2..7299f9fb3 100644
--- a/components/ripple/index.js
+++ b/components/ripple/index.js
@@ -1 +1,4 @@
-export default from './Ripple';
+import rippleFactory from './Ripple.js';
+import theme from './theme.scss';
+
+export default (options) => rippleFactory({ ...options, theme });
diff --git a/components/ripple/readme.md b/components/ripple/readme.md
index c9bac5f52..b7d723155 100644
--- a/components/ripple/readme.md
+++ b/components/ripple/readme.md
@@ -5,6 +5,7 @@ The ripple is a surface reaction that happens when the user interacts with the c
```jsx
import Ripple from 'react-toolbox/lib/ripple';
+import theme from 'react-toolbox/lib/ripple/theme';
const Link = (props) => (
@@ -13,7 +14,7 @@ const Link = (props) => (
);
const RippleLink = Ripple({spread: 3})(Link);
-const RippleTest = () => Test ;
+const RippleTest = () => Test ;
```
## Properties
@@ -26,3 +27,15 @@ In any component you decorate with the Ripple you'd get some additional props:
| `className` | `String` | `''` | String to customize appearance (color and opacity for example).|
| `onRippleEnded` | `Function` | | Function that will be called when the ripple animation ends. |
| `spread` | `Number` | `2` | Factor to indicate how much should the ripple spread under the component.|
+| `theme` | `Object` | `null` | Classnames object defining the ripple style.|
+
+## Theming
+
+You can take a look to the `_config.scss` variables. The themed key for this component is `ToolboxRipple`, it should implement the following interface:
+
+| Name | Description|
+|:-------------------|:-----------|
+| `ripple` | Root classname for the ripple.|
+| `rippleActive` | Applied when the ripple is active.|
+| `rippleRestarting` | Applied when the ripple is restarting.|
+| `rippleWrapper` | Wrapper class to fit to the parent element.|
diff --git a/components/ripple/style.scss b/components/ripple/theme.scss
similarity index 77%
rename from components/ripple/style.scss
rename to components/ripple/theme.scss
index 00902d372..6212078db 100644
--- a/components/ripple/style.scss
+++ b/components/ripple/theme.scss
@@ -1,4 +1,6 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
%ripple {
@@ -12,7 +14,7 @@
transform-origin: 50% 50%;
}
-.wrapper {
+.rippleWrapper {
position: absolute;
top: 0;
right: 0;
@@ -22,18 +24,18 @@
pointer-events: none;
}
-.normal {
+.ripple {
@extend %ripple;
transition-duration: $ripple-duration;
- &.restarting {
+ &.rippleRestarting {
opacity: $ripple-final-opacity;
transition-property: none;
}
- &.active {
+ &.rippleActive {
opacity: $ripple-final-opacity;
transition-property: transform;
}
- &:not(.active):not(.restarting) {
+ &:not(.rippleActive):not(.rippleRestarting) {
opacity: 0;
transition-property: opacity, transform;
}
diff --git a/components/slider/Slider.js b/components/slider/Slider.js
index a5ea2f4bf..2089a8854 100644
--- a/components/slider/Slider.js
+++ b/components/slider/Slider.js
@@ -1,284 +1,308 @@
-import React from 'react';
+import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
-import ClassNames from 'classnames';
-import style from './style';
-import events from '../utils/events';
-import prefixer from '../utils/prefixer';
-import utils from '../utils/utils';
-import ProgressBar from '../progress_bar';
-import Input from '../input';
-
-class Slider extends React.Component {
- static propTypes = {
- className: React.PropTypes.string,
- editable: React.PropTypes.bool,
- max: React.PropTypes.number,
- min: React.PropTypes.number,
- onChange: React.PropTypes.func,
- pinned: React.PropTypes.bool,
- snaps: React.PropTypes.bool,
- step: React.PropTypes.number,
- value: React.PropTypes.number
- };
-
- static defaultProps = {
- className: '',
- editable: false,
- max: 100,
- min: 0,
- pinned: false,
- snaps: false,
- step: 0.01,
- value: 0
- };
-
- state = {
- inputFocused: false,
- inputValue: null,
- sliderLength: 0,
- sliderStart: 0
- };
-
- componentDidMount () {
- window.addEventListener('resize', this.handleResize);
- this.handleResize();
- }
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { SLIDER } from '../identifiers.js';
+import events from '../utils/events.js';
+import prefixer from '../utils/prefixer.js';
+import utils from '../utils/utils.js';
+import InjectProgressBar from '../progress_bar/ProgressBar.js';
+import InjectInput from '../input/Input.js';
+
+const factory = (ProgressBar, Input) => {
+ class Slider extends Component {
+ static propTypes = {
+ className: PropTypes.string,
+ editable: PropTypes.bool,
+ max: PropTypes.number,
+ min: PropTypes.number,
+ onChange: PropTypes.func,
+ pinned: PropTypes.bool,
+ snaps: PropTypes.bool,
+ step: PropTypes.number,
+ theme: PropTypes.shape({
+ container: PropTypes.string,
+ editable: PropTypes.string,
+ innerknob: PropTypes.string,
+ innerprogress: PropTypes.string,
+ input: PropTypes.string,
+ knob: PropTypes.string,
+ pinned: PropTypes.string,
+ pressed: PropTypes.string,
+ progress: PropTypes.string,
+ ring: PropTypes.string,
+ slider: PropTypes.string,
+ snap: PropTypes.string,
+ snaps: PropTypes.string
+ }),
+ value: PropTypes.number
+ };
+
+ static defaultProps = {
+ className: '',
+ editable: false,
+ max: 100,
+ min: 0,
+ pinned: false,
+ snaps: false,
+ step: 0.01,
+ value: 0
+ };
+
+ state = {
+ inputFocused: false,
+ inputValue: null,
+ sliderLength: 0,
+ sliderStart: 0
+ };
- shouldComponentUpdate (nextProps, nextState) {
- if (!this.state.inputFocused && nextState.inputFocused) return false;
- if (this.state.inputFocused && this.props.value !== nextProps.value) {
- this.setState({inputValue: this.valueForInput(nextProps.value)});
- return false;
+ componentDidMount () {
+ window.addEventListener('resize', this.handleResize);
+ this.handleResize();
}
- return true;
- }
- componentWillUnmount () {
- window.removeEventListener('resize', this.handleResize);
- events.removeEventsFromDocument(this.getMouseEventMap());
- events.removeEventsFromDocument(this.getTouchEventMap());
- events.removeEventsFromDocument(this.getKeyboardEvents());
- }
+ shouldComponentUpdate (nextProps, nextState) {
+ if (!this.state.inputFocused && nextState.inputFocused) return false;
+ if (this.state.inputFocused && this.props.value !== nextProps.value) {
+ this.setState({inputValue: this.valueForInput(nextProps.value)});
+ return false;
+ }
+ return true;
+ }
- handleInputFocus = () => {
- this.setState({
- inputFocused: true,
- inputValue: this.valueForInput(this.props.value)
- });
- };
-
- handleInputChange = (value) => {
- this.setState({inputValue: value});
- };
-
- handleInputBlur = (event) => {
- const value = this.state.inputValue || 0;
- this.setState({inputFocused: false, inputValue: null}, () => {
- this.props.onChange(this.trimValue(value), event);
- });
- };
-
- handleKeyDown = (event) => {
- if ([13, 27].indexOf(event.keyCode) !== -1) {
- this.refs.input.blur();
- ReactDOM.findDOMNode(this).blur();
+ componentWillUnmount () {
+ window.removeEventListener('resize', this.handleResize);
+ events.removeEventsFromDocument(this.getMouseEventMap());
+ events.removeEventsFromDocument(this.getTouchEventMap());
+ events.removeEventsFromDocument(this.getKeyboardEvents());
}
- if (event.keyCode === 38) this.addToValue(this.props.step);
- if (event.keyCode === 40) this.addToValue(-this.props.step);
- };
-
- handleMouseDown = (event) => {
- if (this.state.inputFocused) this.refs.input.blur();
- events.addEventsToDocument(this.getMouseEventMap());
- this.start(events.getMousePosition(event));
- events.pauseEvent(event);
- };
-
- handleMouseMove = (event) => {
- events.pauseEvent(event);
- this.move(events.getMousePosition(event));
- };
-
- handleMouseUp = () => {
- this.end(this.getMouseEventMap());
- };
-
- handleResize = (event, callback) => {
- const {left, right} = ReactDOM.findDOMNode(this.refs.progressbar).getBoundingClientRect();
- const cb = (callback) || (() => {});
- this.setState({sliderStart: left, sliderLength: right - left}, cb);
- };
-
- handleSliderBlur = () => {
- events.removeEventsFromDocument(this.getKeyboardEvents());
- };
-
- handleSliderFocus = () => {
- events.addEventsToDocument(this.getKeyboardEvents());
- };
-
- handleTouchEnd = () => {
- this.end(this.getTouchEventMap());
- };
-
- handleTouchMove = (event) => {
- this.move(events.getTouchPosition(event));
- };
-
- handleTouchStart = (event) => {
- if (this.state.inputFocused) this.refs.input.blur();
- this.start(events.getTouchPosition(event));
- events.addEventsToDocument(this.getTouchEventMap());
- events.pauseEvent(event);
- };
-
- addToValue (increment) {
- let value = this.state.inputFocused ? parseFloat(this.state.inputValue) : this.props.value;
- value = this.trimValue(value + increment);
- if (value !== this.props.value) this.props.onChange(value);
- }
- getKeyboardEvents () {
- return {
- keydown: this.handleKeyDown
+ handleInputFocus = () => {
+ this.setState({
+ inputFocused: true,
+ inputValue: this.valueForInput(this.props.value)
+ });
};
- }
- getMouseEventMap () {
- return {
- mousemove: this.handleMouseMove,
- mouseup: this.handleMouseUp
+ handleInputChange = (value) => {
+ this.setState({inputValue: value});
};
- }
- getTouchEventMap () {
- return {
- touchmove: this.handleTouchMove,
- touchend: this.handleTouchEnd
+ handleInputBlur = (event) => {
+ const value = this.state.inputValue || 0;
+ this.setState({inputFocused: false, inputValue: null}, () => {
+ this.props.onChange(this.trimValue(value), event);
+ });
};
- }
- end (revents) {
- events.removeEventsFromDocument(revents);
- this.setState({ pressed: false });
- }
+ handleKeyDown = (event) => {
+ if ([13, 27].indexOf(event.keyCode) !== -1) {
+ this.refs.input.blur();
+ ReactDOM.findDOMNode(this).blur();
+ }
+ if (event.keyCode === 38) this.addToValue(this.props.step);
+ if (event.keyCode === 40) this.addToValue(-this.props.step);
+ };
- knobOffset () {
- const { max, min } = this.props;
- return this.state.sliderLength * (this.props.value - min) / (max - min);
- }
+ handleMouseDown = (event) => {
+ if (this.state.inputFocused) this.refs.input.blur();
+ events.addEventsToDocument(this.getMouseEventMap());
+ this.start(events.getMousePosition(event));
+ events.pauseEvent(event);
+ };
- move (position) {
- const newValue = this.positionToValue(position);
- if (newValue !== this.props.value) this.props.onChange(newValue);
- }
+ handleMouseMove = (event) => {
+ events.pauseEvent(event);
+ this.move(events.getMousePosition(event));
+ };
- positionToValue (position) {
- const { sliderStart: start, sliderLength: length } = this.state;
- const { max, min } = this.props;
- return this.trimValue((position.x - start) / length * (max - min) + min);
- }
+ handleMouseUp = () => {
+ this.end(this.getMouseEventMap());
+ };
- start (position) {
- this.handleResize(null, () => {
- this.setState({pressed: true});
- this.props.onChange(this.positionToValue(position));
- });
- }
+ handleResize = (event, callback) => {
+ const {left, right} = ReactDOM.findDOMNode(this.refs.progressbar).getBoundingClientRect();
+ const cb = (callback) || (() => {});
+ this.setState({sliderStart: left, sliderLength: right - left}, cb);
+ };
- stepDecimals () {
- return (this.props.step.toString().split('.')[1] || []).length;
- }
+ handleSliderBlur = () => {
+ events.removeEventsFromDocument(this.getKeyboardEvents());
+ };
- trimValue (value) {
- if (value < this.props.min) return this.props.min;
- if (value > this.props.max) return this.props.max;
- return utils.round(value, this.stepDecimals());
- }
+ handleSliderFocus = () => {
+ events.addEventsToDocument(this.getKeyboardEvents());
+ };
- valueForInput (value) {
- const decimals = this.stepDecimals();
- return decimals > 0 ? value.toFixed(decimals) : value.toString();
- }
+ handleTouchEnd = () => {
+ this.end(this.getTouchEventMap());
+ };
- renderSnaps () {
- if (this.props.snaps) {
- return (
-
- {utils.range(0, (this.props.max - this.props.min) / this.props.step).map(i => {
- return
;
+ handleTouchMove = (event) => {
+ this.move(events.getTouchPosition(event));
+ };
+
+ handleTouchStart = (event) => {
+ if (this.state.inputFocused) this.refs.input.blur();
+ this.start(events.getTouchPosition(event));
+ events.addEventsToDocument(this.getTouchEventMap());
+ events.pauseEvent(event);
+ };
+
+ addToValue (increment) {
+ let value = this.state.inputFocused ? parseFloat(this.state.inputValue) : this.props.value;
+ value = this.trimValue(value + increment);
+ if (value !== this.props.value) this.props.onChange(value);
+ }
+
+ getKeyboardEvents () {
+ return {
+ keydown: this.handleKeyDown
+ };
+ }
+
+ getMouseEventMap () {
+ return {
+ mousemove: this.handleMouseMove,
+ mouseup: this.handleMouseUp
+ };
+ }
+
+ getTouchEventMap () {
+ return {
+ touchmove: this.handleTouchMove,
+ touchend: this.handleTouchEnd
+ };
+ }
+
+ end (revents) {
+ events.removeEventsFromDocument(revents);
+ this.setState({ pressed: false });
+ }
+
+ knobOffset () {
+ const { max, min } = this.props;
+ return this.state.sliderLength * (this.props.value - min) / (max - min);
+ }
+
+ move (position) {
+ const newValue = this.positionToValue(position);
+ if (newValue !== this.props.value) this.props.onChange(newValue);
+ }
+
+ positionToValue (position) {
+ const { sliderStart: start, sliderLength: length } = this.state;
+ const { max, min } = this.props;
+ return this.trimValue((position.x - start) / length * (max - min) + min);
+ }
+
+ start (position) {
+ this.handleResize(null, () => {
+ this.setState({pressed: true});
+ this.props.onChange(this.positionToValue(position));
+ });
+ }
+
+ stepDecimals () {
+ return (this.props.step.toString().split('.')[1] || []).length;
+ }
+
+ trimValue (value) {
+ if (value < this.props.min) return this.props.min;
+ if (value > this.props.max) return this.props.max;
+ return utils.round(value, this.stepDecimals());
+ }
+
+ valueForInput (value) {
+ const decimals = this.stepDecimals();
+ return decimals > 0 ? value.toFixed(decimals) : value.toString();
+ }
+
+ renderSnaps () {
+ if (this.props.snaps) {
+ return (
+
+ {utils.range(0, (this.props.max - this.props.min) / this.props.step).map(i => {
+ return
;
})}
-
- );
+
+ );
+ }
}
- }
- renderInput () {
- if (this.props.editable) {
- const value = this.state.inputFocused ? this.state.inputValue : this.valueForInput(this.props.value);
- return (
-
- );
+ renderInput () {
+ if (this.props.editable) {
+ const value = this.state.inputFocused ? this.state.inputValue : this.valueForInput(this.props.value);
+ return (
+
+ );
+ }
}
- }
- render () {
- const knobStyles = prefixer({transform: `translateX(${this.knobOffset()}px)`});
- const className = ClassNames(style.root, {
- [style.editable]: this.props.editable,
- [style.pinned]: this.props.pinned,
- [style.pressed]: this.state.pressed,
- [style.ring]: this.props.value === this.props.min
- }, this.props.className);
-
- return (
-
+ render () {
+ const { theme } = this.props;
+ const knobStyles = prefixer({transform: `translateX(${this.knobOffset()}px)`});
+ const className = classnames(theme.slider, {
+ [theme.editable]: this.props.editable,
+ [theme.pinned]: this.props.pinned,
+ [theme.pressed]: this.state.pressed,
+ [theme.ring]: this.props.value === this.props.min
+ }, this.props.className);
+
+ return (
+ className={className}
+ data-react-toolbox='slider'
+ onBlur={this.handleSliderBlur}
+ onFocus={this.handleSliderFocus}
+ tabIndex='0'
+ >
-
+ >
+
+
+
+
+ {this.renderSnaps()}
+
-
-
- {this.renderSnaps()}
-
+ {this.renderInput()}
-
- {this.renderInput()}
-
- );
+ );
+ }
}
-}
-export default Slider;
+ return Slider;
+};
+
+const Slider = factory(InjectProgressBar, InjectInput);
+export default themr(SLIDER)(Slider);
+export { factory as sliderFactory };
+export { Slider };
diff --git a/components/slider/__tests__/index.spec.js b/components/slider/__tests__/index.spec.js
index 7fe605e00..bf4e3ab1e 100644
--- a/components/slider/__tests__/index.spec.js
+++ b/components/slider/__tests__/index.spec.js
@@ -1,20 +1,21 @@
-import expect from 'expect';
+import React from 'react';
+import TestUtils from 'react-addons-test-utils';
import sinon from 'sinon';
+import expect from 'expect';
+import { ProgressBar } from '../../progress_bar/ProgressBar.js';
+import Input, { Input as RawInput } from '../../input/Input.js';
+import Slider, { Slider as RawSlider } from '../Slider.js';
import utils from '../../utils/testing';
-import style from '../../slider/style';
-import ProgressBar from '../../progress_bar';
-import Input from '../../input';
-import Slider from '../index';
-import TestUtils from 'react-addons-test-utils';
+import theme from '../theme.scss';
describe('Slider', function () {
- let props, state, slider, progress, input, onChange;
+ let slider, progress, input, onChange;
describe('#positionToValue', function () {
before(function () {
- props = { min: -500, max: 500 };
- state = { sliderStart: 500, sliderLength: 100 };
- slider = utils.renderComponent(Slider, props, state);
+ const tree = TestUtils.renderIntoDocument( );
+ slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
+ slider.setState({ sliderStart: 500, sliderLength: 100 });
});
it('returns min when position is less than origin', function () {
@@ -32,8 +33,8 @@ describe('Slider', function () {
describe('#trimValue', function () {
before(function () {
- props = { min: 0, max: 100, step: 0.1 };
- slider = utils.renderComponent(Slider, props);
+ const tree = TestUtils.renderIntoDocument( );
+ slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
});
it('rounds to the proper number', function () {
@@ -52,8 +53,8 @@ describe('Slider', function () {
describe('#valueForInput', function () {
before(function () {
- props = { min: 0, max: 100, step: 0.01 };
- slider = utils.renderComponent(Slider, props);
+ const tree = TestUtils.renderIntoDocument( );
+ slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
});
it('returns a fixed number when an integer is given', function () {
@@ -67,16 +68,17 @@ describe('Slider', function () {
describe('#knobOffset', function () {
it('returns the corresponding offset for a given value and slider length/start', function () {
- props = { min: -500, max: 500, value: -250 };
- state = { sliderStart: 500, sliderLength: 100 };
- slider = utils.renderComponent(Slider, props, state);
+ const tree = TestUtils.renderIntoDocument( );
+ slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
+ slider.setState({ sliderStart: 500, sliderLength: 100 });
expect(slider.knobOffset()).toEqual(25);
});
});
describe('#render', function () {
it('contains a linear progress bar with proper properties', function () {
- slider = utils.renderComponent(Slider, {min: 100, max: 1000, value: 140});
+ const tree = TestUtils.renderIntoDocument( );
+ slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
progress = TestUtils.findRenderedComponentWithType(slider, ProgressBar);
expect(progress.props.mode).toEqual('determinate');
expect(progress.props.type).toEqual('linear');
@@ -86,26 +88,27 @@ describe('Slider', function () {
});
it('contains an input component if its editable', function () {
- slider = utils.renderComponent(Slider, {editable: true, value: 130});
+ const tree = TestUtils.renderIntoDocument( );
+ slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
input = TestUtils.findRenderedComponentWithType(slider, Input);
expect(parseInt(input.props.value)).toEqual(slider.props.value);
});
it('contains the proper number of snaps when snapped', function () {
- slider = utils.shallowRenderComponent(Slider, {editable: true, pinned: true});
- expect(slider.props.className).toContain(style.ring);
- expect(slider.props.className).toContain(style.pinned);
- slider = utils.shallowRenderComponent(Slider, {editable: true, value: 50});
- expect(slider.props.className).toNotContain(style.ring);
+ slider = utils.shallowRenderComponent(RawSlider, {editable: true, pinned: true, theme});
+ expect(slider.props.className).toContain(theme.ring);
+ expect(slider.props.className).toContain(theme.pinned);
+ slider = utils.shallowRenderComponent(RawSlider, {editable: true, value: 50, theme});
+ expect(slider.props.className).toNotContain(theme.ring);
});
});
describe('#events', function () {
beforeEach(function () {
onChange = sinon.spy();
- props = { min: -500, max: 500, onChange };
- state = { sliderStart: 0, sliderLength: 1000 };
- slider = utils.renderComponent(Slider, props, state);
+ const tree = TestUtils.renderIntoDocument( );
+ slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
+ slider.setState({ sliderStart: 0, sliderLength: 1000 });
slider.handleResize = (event, callback) => { callback(); };
});
@@ -132,7 +135,9 @@ describe('Slider', function () {
});
it('changes input value when slider changes', function () {
- slider = utils.renderComponent(Slider, {editable: true, onChange}, {sliderStart: 0, sliderLength: 1000});
+ const tree = TestUtils.renderIntoDocument( );
+ slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
+ slider.setState({sliderStart: 0, sliderLength: 1000});
slider.handleResize = (event, callback) => { callback(); };
input = TestUtils.findRenderedComponentWithType(slider, Input);
TestUtils.Simulate.mouseDown(slider.refs.slider, { pageX: 900 });
@@ -141,8 +146,9 @@ describe('Slider', function () {
});
it('changes its value when input is blurred', function () {
- slider = utils.renderComponent(Slider, {editable: true, value: 50, onChange});
- input = TestUtils.findRenderedComponentWithType(slider, Input);
+ const tree = TestUtils.renderIntoDocument( );
+ slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
+ input = TestUtils.findRenderedComponentWithType(slider, RawInput);
TestUtils.Simulate.change(input.refs.input, {target: {value: '80'}});
TestUtils.Simulate.blur(input.refs.input);
expect(onChange.called).toEqual(true);
@@ -151,7 +157,9 @@ describe('Slider', function () {
it('calls onChange callback when the value is changed', function () {
const onChangeSpy = sinon.spy();
- slider = utils.renderComponent(Slider, {onChange: onChangeSpy}, {sliderStart: 0, sliderLength: 1000});
+ const tree = TestUtils.renderIntoDocument( );
+ slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
+ slider.setState({sliderStart: 0, sliderLength: 1000});
TestUtils.Simulate.mouseDown(slider.refs.slider, { pageX: 900 });
expect(onChangeSpy.called).toEqual(true);
});
diff --git a/components/slider/index.js b/components/slider/index.js
index 12b099f9b..629a34570 100644
--- a/components/slider/index.js
+++ b/components/slider/index.js
@@ -1 +1,10 @@
-export default from './Slider';
+import { themr } from 'react-css-themr';
+import { SLIDER } from '../identifiers.js';
+import { ProgressBar } from '../progress_bar';
+import { Input} from '../input';
+import { sliderFactory } from './Slider.js';
+import theme from './theme.scss';
+
+const ThemedSlider = themr(SLIDER, theme)(sliderFactory(ProgressBar, Input));
+export default ThemedSlider;
+export { ThemedSlider as Slider };
diff --git a/components/slider/readme.md b/components/slider/readme.md
index 02da62d86..4d46429ba 100644
--- a/components/slider/readme.md
+++ b/components/slider/readme.md
@@ -33,6 +33,8 @@ class SliderTest extends React.Component {
}
```
+This component can be styled by context providing a theme with the key `RTSlider` through the theme provider.
+
## Properties
| Name | Type | Default | Description|
@@ -46,3 +48,21 @@ class SliderTest extends React.Component {
| `snaps` | `Boolean` | `false` | If true, the slider thumb snaps to tick marks evenly spaced based on the step property value.|
| `step` | `Number` | `0.01` | Amount to vary the value when the knob is moved or increase/decrease is called.|
| `value` | `Number` | `0` | Current value of the slider.|
+
+## Theme
+
+| Name | Description|
+|:---------|:-----------|
+| `container` | Used as an inner container of the root component.|
+| `editable` | Added to the root of in case the Slider is editable.|
+| `innerknob` | Used to style the inner element of the knob.|
+| `innerprogress` | Provided to the ProgressBar component.|
+| `input` | Provided to the Input element in case it's editable.|
+| `knob` | Used to style the outer layer of the knob.|
+| `pinned` | Added to the root in case the Slider is pinned.|
+| `pressed` | Added to the root in case the state is pressed.|
+| `progress` | Used as an outer wrapper for the ProgressBar.|
+| `ring` | Used in the root when the knob should be a ring.|
+| `slider` | Class used for the root element.|
+| `snap` | Used for every individual snap element.|
+| `snaps` | Used as a wrapper for the group of snaps when it's snapped.|
diff --git a/components/slider/style.scss b/components/slider/theme.scss
similarity index 98%
rename from components/slider/style.scss
rename to components/slider/theme.scss
index 53b824712..bf3468152 100644
--- a/components/slider/style.scss
+++ b/components/slider/theme.scss
@@ -1,11 +1,13 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
.container {
position: relative;
width: calc(100% - #{$slider-knob-size});
- margin-right: $slider-knob-size;
height: $slider-knob-size;
+ margin-right: $slider-knob-size;
user-select: none;
&:not(:last-child) {
margin-right: $slider-side-separation + $slider-knob-size;
@@ -96,7 +98,7 @@
}
}
-.root {
+.slider {
&:focus .knob:before {
position: absolute;
top: 0;
diff --git a/components/snackbar/Snackbar.js b/components/snackbar/Snackbar.js
index f96342d38..033d33091 100644
--- a/components/snackbar/Snackbar.js
+++ b/components/snackbar/Snackbar.js
@@ -1,53 +1,71 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import ActivableRenderer from '../hoc/ActivableRenderer';
-import Button from '../button';
-import FontIcon from '../font_icon';
-import Overlay from '../overlay';
-import style from './style';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { SNACKBAR } from '../identifiers.js';
+import ActivableRenderer from '../hoc/ActivableRenderer.js';
+import FontIcon from '../font_icon/FontIcon.js';
+import InjectOverlay from '../overlay/Overlay.js';
+import InjectButton from '../button/Button.js';
-class Snackbar extends React.Component {
- static propTypes = {
- action: React.PropTypes.string,
- active: React.PropTypes.bool,
- className: React.PropTypes.string,
- icon: React.PropTypes.oneOfType([
- React.PropTypes.string,
- React.PropTypes.element
- ]),
- label: React.PropTypes.string.isRequired,
- onClick: React.PropTypes.func,
- onTimeout: React.PropTypes.func,
- timeout: React.PropTypes.number,
- type: React.PropTypes.string
- };
+const factory = (Overlay, Button) => {
+ class Snackbar extends Component {
+ static propTypes = {
+ action: PropTypes.string,
+ active: PropTypes.bool,
+ className: PropTypes.string,
+ icon: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.element
+ ]),
+ label: PropTypes.string,
+ onClick: PropTypes.func,
+ onTimeout: PropTypes.func,
+ theme: PropTypes.shape({
+ accept: PropTypes.string,
+ active: PropTypes.string,
+ button: PropTypes.string,
+ cancel: PropTypes.string,
+ icon: PropTypes.string,
+ label: PropTypes.string,
+ snackbar: PropTypes.string,
+ warning: PropTypes.string
+ }),
+ timeout: PropTypes.number,
+ type: PropTypes.oneOf([ 'accept', 'cancel', 'warning' ])
+ };
- componentWillReceiveProps (nextProps) {
- if (nextProps.active && nextProps.timeout) {
- if (this.curTimeout) clearTimeout(this.curTimeout);
- this.curTimeout = setTimeout(() => {
- nextProps.onTimeout();
- this.curTimeout = null;
- }, nextProps.timeout);
+ componentWillReceiveProps (nextProps) {
+ if (nextProps.active && nextProps.timeout) {
+ if (this.curTimeout) clearTimeout(this.curTimeout);
+ this.curTimeout = setTimeout(() => {
+ nextProps.onTimeout();
+ this.curTimeout = null;
+ }, nextProps.timeout);
+ }
}
- }
- render () {
- const {action, active, icon, label, onClick, type } = this.props;
- const className = ClassNames([style.root, style[type]], {
- [style.active]: active
- }, this.props.className);
+ render () {
+ const {action, active, icon, label, onClick, theme, type } = this.props;
+ const className = classnames([theme.snackbar, theme[type]], {
+ [theme.active]: active
+ }, this.props.className);
- return (
-
-
- {icon ? : null}
- {label}
- {action ? : null}
-
-
- );
+ return (
+
+
+ {icon ? : null}
+ {label}
+ {action ? : null}
+
+
+ );
+ }
}
-}
-export default ActivableRenderer()(Snackbar);
+ return ActivableRenderer()(Snackbar);
+};
+
+const Snackbar = factory(InjectOverlay, InjectButton);
+export default themr(SNACKBAR)(Snackbar);
+export { factory as snackbarFactory };
+export { Snackbar };
diff --git a/components/snackbar/index.js b/components/snackbar/index.js
index 43bbf46da..5825088c3 100644
--- a/components/snackbar/index.js
+++ b/components/snackbar/index.js
@@ -1 +1,11 @@
-export default from './Snackbar';
+import { themr } from 'react-css-themr';
+import { SNACKBAR } from '../identifiers.js';
+import { snackbarFactory } from './Snackbar.js';
+import { Overlay } from '../overlay';
+import { Button } from '../button';
+import theme from './theme.scss';
+
+const ThemedSnackbar = themr(SNACKBAR, theme)(snackbarFactory(Overlay, Button));
+
+export default ThemedSnackbar;
+export { ThemedSnackbar as Snackbar };
diff --git a/components/snackbar/readme.md b/components/snackbar/readme.md
index 620f12233..1985194e0 100644
--- a/components/snackbar/readme.md
+++ b/components/snackbar/readme.md
@@ -33,6 +33,8 @@ class SnackbarTest extends React.Component {
}
```
+This component can be styled by context providing a theme with the key `RTSnackbar` through the theme provider.
+
## Properties
| Name | Type | Default | Description|
@@ -46,3 +48,16 @@ class SnackbarTest extends React.Component {
| `onTimeout` | `Function` | | Callback function when finish the set timeout.|
| `timeout` | `Number` | | Amount of time in milliseconds after the Snackbar will be automatically hidden.|
| `type` | `String` | | Indicates the action type. Can be `accept`, `warning` or `cancel`|
+
+## Theme
+
+| Name | Description|
+|:---------|:-----------|
+| `accept` | Added to the root element in case it's accept type.|
+| `active` | Added to the root element when its active.|
+| `button` | Used for the button inside the component.|
+| `cancel` | Added to the root element in case it's cancel type.|
+| `icon` | Used for the icon element.|
+| `label` | Used for the label element.|
+| `snackbar` | Used as the className for the root element of the component.|
+| `warning` | Added to the root element in case it's warning type.|
diff --git a/components/snackbar/style.scss b/components/snackbar/theme.scss
similarity index 93%
rename from components/snackbar/style.scss
rename to components/snackbar/theme.scss
index d27da625d..e9e7071ac 100644
--- a/components/snackbar/style.scss
+++ b/components/snackbar/theme.scss
@@ -1,8 +1,9 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
-
-.root {
+.snackbar {
position: fixed;
right: $snackbar-horizontal-offset;
bottom: 0;
diff --git a/components/switch/Switch.js b/components/switch/Switch.js
index c6539f024..32f1a090b 100644
--- a/components/switch/Switch.js
+++ b/components/switch/Switch.js
@@ -1,64 +1,83 @@
-import React from 'react';
-import Thumb from './Thumb';
-import style from './style';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { SWITCH } from '../identifiers.js';
+import rippleFactory from '../ripple/Ripple.js';
+import thumbFactory from './Thumb.js';
-class Switch extends React.Component {
- static propTypes = {
- checked: React.PropTypes.bool,
- className: React.PropTypes.string,
- disabled: React.PropTypes.bool,
- label: React.PropTypes.string,
- name: React.PropTypes.string,
- onBlur: React.PropTypes.func,
- onChange: React.PropTypes.func,
- onFocus: React.PropTypes.func
- };
+const factory = (Thumb) => {
+ class Switch extends Component {
+ static propTypes = {
+ checked: PropTypes.bool,
+ className: PropTypes.string,
+ disabled: PropTypes.bool,
+ label: PropTypes.string,
+ name: PropTypes.string,
+ onBlur: PropTypes.func,
+ onChange: PropTypes.func,
+ onFocus: PropTypes.func,
+ theme: PropTypes.shape({
+ disabled: PropTypes.string,
+ field: PropTypes.string,
+ input: PropTypes.string,
+ off: PropTypes.string,
+ on: PropTypes.string,
+ ripple: PropTypes.string,
+ text: PropTypes.string,
+ thumb: PropTypes.string
+ })
+ };
- static defaultProps = {
- checked: false,
- className: '',
- disabled: false
- };
+ static defaultProps = {
+ checked: false,
+ className: '',
+ disabled: false
+ };
- handleToggle = (event) => {
- if (event.pageX !== 0 && event.pageY !== 0) this.blur();
- if (!this.props.disabled && this.props.onChange) {
- this.props.onChange(!this.props.checked, event);
+ handleToggle = (event) => {
+ if (event.pageX !== 0 && event.pageY !== 0) this.blur();
+ if (!this.props.disabled && this.props.onChange) {
+ this.props.onChange(!this.props.checked, event);
+ }
+ };
+
+ blur () {
+ this.refs.input.blur();
}
- };
- blur () {
- this.refs.input.blur();
- }
+ focus () {
+ this.refs.input.focus();
+ }
- focus () {
- this.refs.input.focus();
+ render () {
+ const { className, checked, disabled, onChange, theme, ...others } = this.props; //eslint-disable-line no-unused-vars
+ const _className = classnames(theme[disabled ? 'disabled' : 'field'], className);
+ return (
+
+
+
+
+
+ {this.props.label ? {this.props.label} : null}
+
+ );
+ }
}
- render () {
- let className = style[this.props.disabled ? 'disabled' : 'field'];
- const switchClassName = style[this.props.checked ? 'on' : 'off'];
- const { onChange, ...others } = this.props; //eslint-disable-line no-unused-vars
- if (this.props.className) className += ` ${this.props.className}`;
+ return Switch;
+};
- return (
-
-
-
-
-
- {this.props.label ? {this.props.label} : null}
-
- );
- }
-}
+const Thumb = thumbFactory(rippleFactory({ centered: true, spread: 2.6 }));
+const Switch = factory(Thumb);
-export default Switch;
+export default themr(SWITCH)(Switch);
+export { factory as switchFactory };
+export { Switch };
diff --git a/components/switch/Thumb.js b/components/switch/Thumb.js
index 73adff1ae..f9694cd9b 100644
--- a/components/switch/Thumb.js
+++ b/components/switch/Thumb.js
@@ -1,18 +1,19 @@
import React, { PropTypes } from 'react';
-import Ripple from '../ripple';
-import style from './style';
-const Thumb = ({children, onMouseDown}) => (
- {children}
-);
+const factory = (ripple) => {
+ const Thumb = ({onMouseDown, theme, ...other}) => (
+
+ );
-Thumb.propTypes = {
- children: PropTypes.any
+ Thumb.propTypes = {
+ children: PropTypes.any,
+ theme: PropTypes.shape({
+ ripple: PropTypes.string,
+ thumb: PropTypes.string
+ })
+ };
+
+ return ripple(Thumb);
};
-export default Ripple({
- className: style.ripple,
- spread: 2.6,
- centered: true
-})(Thumb);
-export {Thumb as RawThumb};
+export default factory;
diff --git a/components/switch/index.js b/components/switch/index.js
index bc6c05d20..28c3ec386 100644
--- a/components/switch/index.js
+++ b/components/switch/index.js
@@ -1 +1,14 @@
-export default from './Switch';
+import { themr } from 'react-css-themr';
+import { switchFactory } from './Switch.js';
+import { SWITCH } from '../identifiers.js';
+import thumbFactory from './Thumb.js';
+import themedRippleFactory from '../ripple';
+import theme from './theme.scss';
+
+const applyTheme = (Component) => themr(SWITCH, theme)(Component);
+const ripple = themedRippleFactory({ centered: true, spread: 2.6 });
+const ThemedThumb = applyTheme(thumbFactory(ripple));
+const ThemedSwitch = applyTheme(switchFactory(ThemedThumb));
+
+export default ThemedSwitch;
+export { ThemedSwitch as Switch };
diff --git a/components/switch/readme.md b/components/switch/readme.md
index 00e7c67c9..41d814147 100644
--- a/components/switch/readme.md
+++ b/components/switch/readme.md
@@ -42,6 +42,8 @@ class SwitchTest extends React.Component {
}
```
+This component can be styled by context providing a theme with the key `RTSwitch` through the theme provider.
+
## Properties
| Name | Type | Default | Description|
@@ -54,3 +56,16 @@ class SwitchTest extends React.Component {
| `onBlur` | `Function` | | Callback function that is fired when when the switch is blurred.|
| `onChange` | `Function` | | Callback function that is fired when the component's value changes.|
| `onFocus` | `Function` | | Callback function that is fired when the switch is focused.|
+
+## Theme
+
+| Name | Description|
+|:---------|:-----------|
+| `disabled` | Used for the root element if the component is disabled.|
+| `field` | Used for the root element if the component is not disabled.|
+| `input` | Used for the input element.|
+| `off` | Used for a wrapper around the thumb if checked is `false`.|
+| `on` | Used for a wrapper around the thumb if checked is `true`.|
+| `ripple` | Used for the ripple inside the switch.|
+| `text` | Used for the text label element.|
+| `thumb` | Used for the thumb element.|
diff --git a/components/switch/style.scss b/components/switch/theme.scss
similarity index 93%
rename from components/switch/style.scss
rename to components/switch/theme.scss
index 066760f76..c01664379 100644
--- a/components/switch/style.scss
+++ b/components/switch/theme.scss
@@ -1,4 +1,6 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
.field {
@@ -40,12 +42,12 @@
cursor: pointer;
border-radius: 50%;
transition-property: left;
-}
-.ripple {
- background-color: $switch-color;
- opacity: .3;
- transition-duration: $switch-ripple-duration;
+ .ripple {
+ background-color: $switch-color;
+ opacity: .3;
+ transition-duration: $switch-ripple-duration;
+ }
}
.on {
diff --git a/components/table/Table.js b/components/table/Table.js
index 18bd50925..3a7979d07 100644
--- a/components/table/Table.js
+++ b/components/table/Table.js
@@ -1,103 +1,121 @@
-import React from 'react';
-import TableHead from './TableHead';
-import TableRow from './TableRow';
-import style from './style';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { TABLE } from '../identifiers.js';
+import InjectCheckbox from '../checkbox/Checkbox.js';
+import tableHeadFactory from './TableHead.js';
+import tableRowFactory from './TableRow.js';
-class Table extends React.Component {
- static propTypes = {
- className: React.PropTypes.string,
- heading: React.PropTypes.bool,
- model: React.PropTypes.object,
- multiSelectable: React.PropTypes.bool,
- onChange: React.PropTypes.func,
- onSelect: React.PropTypes.func,
- selectable: React.PropTypes.bool,
- selected: React.PropTypes.array,
- source: React.PropTypes.array
- };
+const factory = (TableHead, TableRow) => {
+ class Table extends Component {
+ static propTypes = {
+ className: PropTypes.string,
+ heading: PropTypes.bool,
+ model: PropTypes.object,
+ multiSelectable: PropTypes.bool,
+ onChange: PropTypes.func,
+ onSelect: PropTypes.func,
+ selectable: PropTypes.bool,
+ selected: PropTypes.array,
+ source: PropTypes.array,
+ theme: PropTypes.shape({
+ table: PropTypes.string
+ })
+ };
- static defaultProps = {
- className: '',
- heading: true,
- selectable: true,
- multiSelectable: true,
- selected: [],
- source: []
- };
+ static defaultProps = {
+ className: '',
+ heading: true,
+ selectable: true,
+ multiSelectable: true,
+ selected: [],
+ source: []
+ };
- handleFullSelect = () => {
- if (this.props.onSelect) {
- const {source, selected} = this.props;
- const newSelected = source.length === selected.length ? [] : source.map((i, idx) => idx);
- this.props.onSelect(newSelected);
- }
- };
+ handleFullSelect = () => {
+ if (this.props.onSelect) {
+ const {source, selected} = this.props;
+ const newSelected = source.length === selected.length ? [] : source.map((i, idx) => idx);
+ this.props.onSelect(newSelected);
+ }
+ };
- handleRowSelect = (index) => {
- if (this.props.onSelect) {
- const position = this.props.selected.indexOf(index);
- let newSelected = [...this.props.selected];
- if (position !== -1) { newSelected.splice(position, 1); }
- if (position !== -1 && this.props.multiSelectable) {
- newSelected.push(index);
- } else {
- newSelected = [index];
+ handleRowSelect = (index) => {
+ if (this.props.onSelect) {
+ const position = this.props.selected.indexOf(index);
+ let newSelected = [...this.props.selected];
+ if (position !== -1) { newSelected.splice(position, 1); }
+ if (position !== -1 && this.props.multiSelectable) {
+ newSelected.push(index);
+ } else {
+ newSelected = [index];
+ }
+ this.props.onSelect(newSelected);
}
- this.props.onSelect(newSelected);
- }
- };
+ };
- handleRowChange = (index, key, value) => {
- if (this.props.onChange) {
- this.props.onChange(index, key, value);
+ handleRowChange = (index, key, value) => {
+ if (this.props.onChange) {
+ this.props.onChange(index, key, value);
+ }
+ };
+
+ renderHead () {
+ if (this.props.heading) {
+ const {model, selected, source, selectable, multiSelectable} = this.props;
+ const isSelected = selected.length === source.length;
+ return (
+
+ );
+ }
}
- };
- renderHead () {
- if (this.props.heading) {
- const {model, selected, source, selectable, multiSelectable} = this.props;
- const isSelected = selected.length === source.length;
+ renderBody () {
+ const { source, model, onChange, selectable, selected, theme } = this.props;
return (
-
+
+ {source.map((data, index) => (
+
+ ))}
+
);
}
- }
- renderBody () {
- const rows = this.props.source.map((data, index) => {
+ render () {
+ const { className, theme } = this.props;
return (
-
+
+ {this.renderHead()}
+ {this.renderBody()}
+
);
- });
-
- return {rows} ;
+ }
}
- render () {
- let className = style.root;
- if (this.props.className) className += ` ${this.props.className}`;
- return (
-
- {this.renderHead()}
- {this.renderBody()}
-
- );
- }
-}
+ return Table;
+};
+
+const TableHead = tableHeadFactory(InjectCheckbox);
+const TableRow = tableRowFactory(InjectCheckbox);
+const Table = factory(TableHead, TableRow);
-export default Table;
+export default themr(TABLE)(Table);
+export { factory as tableFactory };
+export { Table };
diff --git a/components/table/TableHead.js b/components/table/TableHead.js
index 55b4638a3..57f39a640 100644
--- a/components/table/TableHead.js
+++ b/components/table/TableHead.js
@@ -1,45 +1,50 @@
-import React from 'react';
-import Checkbox from '../checkbox';
-import style from './style';
+import React, { PropTypes } from 'react';
-const TableHead = ({model, onSelect, selectable, multiSelectable, selected}) => {
- let selectCell;
- const contentCells = Object.keys(model).map((key) => {
- const name = model[key].title || key;
- return {name} ;
- });
+const factory = (Checkbox) => {
+ const TableHead = ({model, onSelect, selectable, multiSelectable, selected, theme}) => {
+ let selectCell;
+ const contentCells = Object.keys(model).map((key) => {
+ const name = model[key].title || key;
+ return {name} ;
+ });
- if (selectable && multiSelectable) {
- selectCell = (
-
-
-
+ if (selectable && multiSelectable) {
+ selectCell = (
+
+
+
+ );
+ } else if (selectable) {
+ selectCell = (
+
+ );
+ }
+ return (
+
+ {[selectCell, ...contentCells]}
+
);
- } else if (selectable) {
- selectCell = (
-
- );
- }
- return (
-
- {[selectCell, ...contentCells]}
-
- );
-};
+ };
-TableHead.propTypes = {
- className: React.PropTypes.string,
- model: React.PropTypes.object,
- onSelect: React.PropTypes.func,
- selectable: React.PropTypes.bool,
- multiSelectable: React.PropTypes.bool,
- selected: React.PropTypes.bool
-};
+ TableHead.propTypes = {
+ className: PropTypes.string,
+ model: PropTypes.object,
+ multiSelectable: PropTypes.bool,
+ onSelect: PropTypes.func,
+ selectable: PropTypes.bool,
+ selected: PropTypes.bool,
+ theme: PropTypes.shape({
+ selectable: PropTypes.string
+ })
+ };
+
+ TableHead.defaultProps = {
+ className: '',
+ model: {},
+ selected: false
+ };
-TableHead.defaultProps = {
- className: '',
- model: {},
- selected: false
+ return TableHead;
};
-export default TableHead;
+export default factory;
diff --git a/components/table/TableRow.js b/components/table/TableRow.js
index 5d08be30d..00822dcc2 100644
--- a/components/table/TableRow.js
+++ b/components/table/TableRow.js
@@ -1,85 +1,93 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import Checkbox from '../checkbox';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
import utils from '../utils/utils';
-import style from './style';
-class TableRow extends React.Component {
- static propTypes = {
- data: React.PropTypes.object,
- index: React.PropTypes.number,
- model: React.PropTypes.object,
- onChange: React.PropTypes.func,
- onSelect: React.PropTypes.func,
- selectable: React.PropTypes.bool,
- selected: React.PropTypes.bool
- };
+const factory = (Checkbox) => {
+ class TableRow extends Component {
+ static propTypes = {
+ data: PropTypes.object,
+ index: PropTypes.number,
+ model: PropTypes.object,
+ onChange: PropTypes.func,
+ onSelect: PropTypes.func,
+ selectable: PropTypes.bool,
+ selected: PropTypes.bool,
+ theme: PropTypes.shape({
+ editable: PropTypes.string,
+ row: PropTypes.string,
+ selectable: PropTypes.string,
+ selected: PropTypes.string
+ })
+ };
- handleInputChange = (index, key, type, event) => {
- const value = type === 'checkbox' ? event.target.checked : event.target.value;
- const onChange = this.props.model[key].onChange || this.props.onChange;
- onChange(index, key, value);
- };
+ handleInputChange = (index, key, type, event) => {
+ const value = type === 'checkbox' ? event.target.checked : event.target.value;
+ const onChange = this.props.model[key].onChange || this.props.onChange;
+ onChange(index, key, value);
+ };
- renderSelectCell () {
- if (this.props.selectable) {
- return (
-
-
-
- );
+ renderSelectCell () {
+ if (this.props.selectable) {
+ return (
+
+
+
+ );
+ }
}
- }
- renderCells () {
- return Object.keys(this.props.model).map((key) => {
- return {this.renderCell(key)} ;
- });
- }
+ renderCells () {
+ return Object.keys(this.props.model).map((key) => {
+ return {this.renderCell(key)} ;
+ });
+ }
- renderCell (key) {
- const value = this.props.data[key];
+ renderCell (key) {
+ const value = this.props.data[key];
- // if the value is a valid React element return it directly, since it
- // cannot be edited and should not be converted to a string...
- if (React.isValidElement(value)) { return value; }
+ // if the value is a valid React element return it directly, since it
+ // cannot be edited and should not be converted to a string...
+ if (React.isValidElement(value)) { return value; }
- const onChange = this.props.model[key].onChange || this.props.onChange;
- if (onChange) {
- return this.renderInput(key, value);
- } else if (value) {
- return value.toString();
+ const onChange = this.props.model[key].onChange || this.props.onChange;
+ if (onChange) {
+ return this.renderInput(key, value);
+ } else if (value) {
+ return value.toString();
+ }
}
- }
- renderInput (key, value) {
- const index = this.props.index;
- const inputType = utils.inputTypeForPrototype(this.props.model[key].type);
- const inputValue = utils.prepareValueForInput(value, inputType);
- const checked = inputType === 'checkbox' && value ? true : null;
- return (
-
- );
- }
+ renderInput (key, value) {
+ const index = this.props.index;
+ const inputType = utils.inputTypeForPrototype(this.props.model[key].type);
+ const inputValue = utils.prepareValueForInput(value, inputType);
+ const checked = inputType === 'checkbox' && value ? true : null;
+ return (
+
+ );
+ }
- render () {
- const className = ClassNames(style.row, {
- [style.editable]: this.props.onChange,
- [style.selected]: this.props.selected
- });
+ render () {
+ const className = classnames(this.props.theme.row, {
+ [this.props.theme.editable]: this.props.onChange,
+ [this.props.theme.selected]: this.props.selected
+ });
- return (
-
- {this.renderSelectCell()}
- {this.renderCells()}
-
- );
+ return (
+
+ {this.renderSelectCell()}
+ {this.renderCells()}
+
+ );
+ }
}
-}
-export default TableRow;
+ return TableRow;
+};
+
+export default factory;
diff --git a/components/table/index.js b/components/table/index.js
index aa0f03a09..77e1c7751 100644
--- a/components/table/index.js
+++ b/components/table/index.js
@@ -1 +1,15 @@
-export default from './Table';
+import { themr } from 'react-css-themr';
+import { TABLE } from '../identifiers.js';
+import { Checkbox } from '../checkbox';
+import { tableFactory } from './Table.js';
+import tableHeadFactory from './TableHead.js';
+import tableRowFactory from './TableRow.js';
+import theme from './theme.scss';
+
+const applyTheme = (Component) => themr(TABLE, theme)(Component);
+const ThemedTableHead = applyTheme(tableHeadFactory(Checkbox));
+const ThemedTableRow = applyTheme(tableRowFactory(Checkbox));
+const ThemedTable = applyTheme(tableFactory(ThemedTableHead, ThemedTableRow));
+
+export default ThemedTable;
+export { ThemedTable as Table };
diff --git a/components/table/readme.md b/components/table/readme.md
index 063e04f41..5353fe4de 100644
--- a/components/table/readme.md
+++ b/components/table/readme.md
@@ -49,6 +49,8 @@ class TableTest extends React.Component {
}
```
+This component can be styled by context providing a theme with the key `RTTable` through the theme provider.
+
## Properties
| Name | Type | Default | Description|
@@ -62,3 +64,13 @@ class TableTest extends React.Component {
| `multiSelectable` | `Boolean` | `true` | If true, the header and each row will display a checkbox to allow the user to select multiple rows.|
| `selected` | `Array` | | Array of indexes of the items in the source that should appear as selected.|
| `source` | `Array` | | Array of objects representing each item to show.|
+
+## Theme
+
+| Name | Description|
+|:---------|:-----------|
+| `editable` | It will be added to a row in case it is editable.|
+| `row` | Used for the row element.|
+| `selectable` | It will be added to a row in case it is selectable.|
+| `selected` | Added to a row in case it is selected.|
+| `table` | Classname used for the root element.|
diff --git a/components/table/style.scss b/components/table/theme.scss
similarity index 92%
rename from components/table/style.scss
rename to components/table/theme.scss
index 36f98c538..50db57d34 100644
--- a/components/table/style.scss
+++ b/components/table/theme.scss
@@ -1,7 +1,9 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
-.root {
+.table {
width: 100%;
font-size: $font-size-tiny;
color: $table-text-color;
diff --git a/components/tabs/Tab.js b/components/tabs/Tab.js
index b7d8c3c35..052a05f97 100644
--- a/components/tabs/Tab.js
+++ b/components/tabs/Tab.js
@@ -1,17 +1,24 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import style from './style';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { TABS } from '../identifiers.js';
-class TabHeader extends React.Component {
+class Tab extends Component {
static propTypes = {
- active: React.PropTypes.bool,
- activeClassName: React.PropTypes.string,
- className: React.PropTypes.string,
- disabled: React.PropTypes.bool,
- hidden: React.PropTypes.bool,
- label: React.PropTypes.any.isRequired,
- onActive: React.PropTypes.func,
- onClick: React.PropTypes.func
+ active: PropTypes.bool,
+ activeClassName: PropTypes.string,
+ className: PropTypes.string,
+ disabled: PropTypes.bool,
+ hidden: PropTypes.bool,
+ label: PropTypes.any.isRequired,
+ onActive: PropTypes.func,
+ onClick: PropTypes.func,
+ theme: PropTypes.shape({
+ active: PropTypes.string,
+ disabled: PropTypes.string,
+ hidden: PropTypes.string,
+ label: PropTypes.string
+ })
};
static defaultProps = {
@@ -34,19 +41,21 @@ class TabHeader extends React.Component {
};
render () {
- const className = ClassNames(style.label, {
- [style.active]: this.props.active,
- [style.hidden]: this.props.hidden,
- [style.disabled]: this.props.disabled,
- [this.props.activeClassName]: this.props.active
- }, this.props.className);
+ const { active, activeClassName, hidden, disabled, className, theme } = this.props;
+ const _className = classnames(theme.label, {
+ [theme.active]: active,
+ [theme.hidden]: hidden,
+ [theme.disabled]: disabled,
+ [activeClassName]: active
+ }, className);
return (
-
+
{this.props.label}
);
}
}
-export default TabHeader;
+export default themr(TABS)(Tab);
+export { Tab };
diff --git a/components/tabs/TabContent.js b/components/tabs/TabContent.js
index 04a9810ad..b4ff9f839 100644
--- a/components/tabs/TabContent.js
+++ b/components/tabs/TabContent.js
@@ -1,12 +1,18 @@
-import React from 'react';
-import style from './style';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { TABS } from '../identifiers.js';
-class TabContent extends React.Component {
+class TabContent extends Component {
static propTypes = {
- active: React.PropTypes.bool,
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- tabIndex: React.PropTypes.number
+ active: PropTypes.bool,
+ children: PropTypes.node,
+ className: PropTypes.string,
+ tabIndex: PropTypes.number,
+ theme: PropTypes.shape({
+ active: PropTypes.string,
+ tab: PropTypes.string
+ })
};
static defaultProps = {
@@ -15,9 +21,9 @@ class TabContent extends React.Component {
};
render () {
- let className = style.tab;
- if (this.props.active) className += ` ${style.active}`;
- if (this.props.className) className += ` ${this.props.className}`;
+ const className = classnames(this.props.theme.tab, {
+ [this.props.theme.active]: this.props.active
+ }, this.props.className);
return (
@@ -27,4 +33,5 @@ class TabContent extends React.Component {
}
}
-export default TabContent;
+export default themr(TABS)(TabContent);
+export { TabContent };
diff --git a/components/tabs/Tabs.js b/components/tabs/Tabs.js
index a858028ca..ecb5dd0b5 100644
--- a/components/tabs/Tabs.js
+++ b/components/tabs/Tabs.js
@@ -1,113 +1,125 @@
-import React from 'react';
-import Tab from './Tab';
-import TabContent from './TabContent';
-import style from './style';
-
-class Tabs extends React.Component {
- static propTypes = {
- children: React.PropTypes.node,
- className: React.PropTypes.string,
- disableAnimatedBottomBorder: React.PropTypes.bool,
- index: React.PropTypes.number,
- onChange: React.PropTypes.func
- };
-
- static defaultProps = {
- index: 0
- };
-
- state = {
- pointer: {}
- };
-
- componentDidMount () {
- !this.props.disableAnimatedBottomBorder && this.updatePointer(this.props.index);
- }
-
- componentWillReceiveProps (nextProps) {
- !this.props.disableAnimatedBottomBorder && this.updatePointer(nextProps.index);
- }
-
- componentWillUnmount () {
- clearTimeout(this.pointerTimeout);
- }
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { TABS } from '../identifiers.js';
+import InjectTab from './Tab.js';
+import InjectTabContent from './TabContent.js';
+
+const factory = (Tab, TabContent) => {
+ class Tabs extends Component {
+ static propTypes = {
+ children: PropTypes.node,
+ className: PropTypes.string,
+ disableAnimatedBottomBorder: PropTypes.bool,
+ index: PropTypes.number,
+ onChange: PropTypes.func,
+ theme: PropTypes.shape({
+ navigation: PropTypes.string,
+ pointer: PropTypes.string,
+ tabs: PropTypes.string
+ })
+ };
+
+ static defaultProps = {
+ index: 0
+ };
+
+ state = {
+ pointer: {}
+ };
+
+ componentDidMount () {
+ !this.props.disableAnimatedBottomBorder && this.updatePointer(this.props.index);
+ }
- handleHeaderClick = (idx) => {
- if (this.props.onChange) this.props.onChange(idx);
- };
+ componentWillReceiveProps (nextProps) {
+ !this.props.disableAnimatedBottomBorder && this.updatePointer(nextProps.index);
+ }
- parseChildren () {
- const headers = [];
- const contents = [];
+ componentWillUnmount () {
+ clearTimeout(this.pointerTimeout);
+ }
- React.Children.forEach(this.props.children, (item) => {
- if (item.type === Tab) {
- headers.push(item);
- if (item.props.children) {
- contents.push();
+ handleHeaderClick = (idx) => {
+ if (this.props.onChange) this.props.onChange(idx);
+ };
+
+ parseChildren () {
+ const headers = [];
+ const contents = [];
+
+ React.Children.forEach(this.props.children, (item) => {
+ if (item.type === Tab) {
+ headers.push(item);
+ if (item.props.children) {
+ contents.push( );
+ }
+ } else if (item.type === TabContent) {
+ contents.push(item);
}
- } else if (item.type === TabContent) {
- contents.push(item);
- }
- });
+ });
- return {headers, contents};
- }
+ return {headers, contents};
+ }
- updatePointer (idx) {
- clearTimeout(this.pointerTimeout);
- this.pointerTimeout = setTimeout(() => {
- const startPoint = this.refs.tabs.getBoundingClientRect().left;
- const label = this.refs.navigation.children[idx].getBoundingClientRect();
- this.setState({
- pointer: {
- top: `${this.refs.navigation.getBoundingClientRect().height}px`,
- left: `${label.left - startPoint}px`,
- width: `${label.width}px`
- }
+ updatePointer (idx) {
+ clearTimeout(this.pointerTimeout);
+ this.pointerTimeout = setTimeout(() => {
+ const startPoint = this.refs.tabs.getBoundingClientRect().left;
+ const label = this.refs.navigation.children[idx].getBoundingClientRect();
+ this.setState({
+ pointer: {
+ top: `${this.refs.navigation.getBoundingClientRect().height}px`,
+ left: `${label.left - startPoint}px`,
+ width: `${label.width}px`
+ }
+ });
+ }, 20);
+ }
+
+ renderHeaders (headers) {
+ return headers.map((item, idx) => {
+ return React.cloneElement(item, {
+ key: idx,
+ active: this.props.index === idx,
+ onClick: this.handleHeaderClick.bind(this, idx, item)
+ });
});
- }, 20);
- }
+ }
- renderHeaders (headers) {
- return headers.map((item, idx) => {
- return React.cloneElement(item, {
- key: idx,
- active: this.props.index === idx,
- onClick: this.handleHeaderClick.bind(this, idx, item)
+ renderContents (contents) {
+ const activeIdx = contents.findIndex((item, idx) => {
+ return this.props.index === idx;
});
- });
- }
- renderContents (contents) {
- const activeIdx = contents.findIndex((item, idx) => {
- return this.props.index === idx;
- });
+ if (contents && contents[activeIdx]) {
+ return React.cloneElement(contents[activeIdx], {
+ key: activeIdx,
+ active: true,
+ tabIndex: activeIdx
+ });
+ }
+ }
- if (contents && contents[activeIdx]) {
- return React.cloneElement(contents[activeIdx], {
- key: activeIdx,
- active: true,
- tabIndex: activeIdx
- });
+ render () {
+ const { className, theme } = this.props;
+ const { headers, contents } = this.parseChildren();
+ return (
+
+
+ {this.renderHeaders(headers)}
+
+
+ {this.renderContents(contents)}
+
+ );
}
}
- render () {
- let className = style.root;
- const { headers, contents } = this.parseChildren();
- if (this.props.className) className += ` ${this.props.className}`;
-
- return (
-
-
- {this.renderHeaders(headers)}
-
-
- {this.renderContents(contents)}
-
- );
- }
-}
+ return Tabs;
+};
-export default Tabs;
+const Tabs = factory(InjectTab, InjectTabContent);
+export default themr(TABS)(Tabs);
+export { factory as tabsFactory };
+export { Tabs };
diff --git a/components/tabs/index.js b/components/tabs/index.js
index f6619dd74..85c995a61 100644
--- a/components/tabs/index.js
+++ b/components/tabs/index.js
@@ -1,2 +1,14 @@
-export Tabs from './Tabs';
-export Tab from './Tab';
+import { themr } from 'react-css-themr';
+import { TABS } from '../identifiers.js';
+import { tabsFactory } from './Tabs.js';
+import { TabContent } from './TabContent.js';
+import { Tab } from './Tab.js';
+import theme from './theme.scss';
+
+const applyTheme = (Component) => themr(TABS, theme)(Component);
+const ThemedTabContent = applyTheme(TabContent);
+const ThemedTab = applyTheme(Tab);
+const ThemedTabs = applyTheme(tabsFactory(ThemedTab, ThemedTabContent));
+
+export { ThemedTab as Tab };
+export { ThemedTabs as Tabs };
diff --git a/components/tabs/readme.md b/components/tabs/readme.md
index a4a95121c..b2c8d3a1b 100644
--- a/components/tabs/readme.md
+++ b/components/tabs/readme.md
@@ -33,10 +33,14 @@ class TabsTest extends React.Component {
}
```
+If you want to provide a theme via context, the component key is `RTTabs`.
+
## Tabs
This component acts as the wrapper and the main controller of the content that is being displayed. It gets some properties that can be spread to the children.
+### Properties
+
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `className` | `String` | `''` | Additional class name to provide custom styling.|
@@ -44,10 +48,22 @@ This component acts as the wrapper and the main controller of the content that i
| `index` | `Number` | `0` | Current |
| `onChange` | `Function` | | Callback function that is fired when the tab changes.|
+### Theming
+
+| Name | Description|
+|:---------|:-----------|
+| `active` | Added to the active tab content and header.|
+| `navigation` | Used for the navigation element.|
+| `pointer` | Used for the moving underline element.|
+| `tabs` |Used as a root classname for the component.|
+| `tab` | Used for the tab content element.|
+
## Tab
Represent a single tab element and it should include some properties to describe the tab itself and get children elements as content.
+### Properties
+
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `active` | `Boolean` | `false` | If true, the current component is visible.|
@@ -58,3 +74,12 @@ Represent a single tab element and it should include some properties to describe
| `label` | `String` | | Label text for navigation header. Required. |
| `onActive` | `Function` | | Callback function that is fired when the tab is activated. |
| `onClick` | `Function` | | Callback function that is fired when the tab is clicked. |
+
+### Theme
+
+| Name | Description|
+|:---------|:-----------|
+| `active` | Added to the navigation tab element in case it's active.|
+| `disabled` | Added to the navigation tab element in case it's disabled.|
+| `hidden` | Added to the navigation tab element in case it's hidden.|
+| `label` | Added to the navigation tab element in case it's active.|
diff --git a/components/tabs/style.scss b/components/tabs/theme.scss
similarity index 94%
rename from components/tabs/style.scss
rename to components/tabs/theme.scss
index 8f3388d26..f2e77760e 100644
--- a/components/tabs/style.scss
+++ b/components/tabs/theme.scss
@@ -1,7 +1,9 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
-.root {
+.tabs {
position: relative;
display: flex;
flex-direction: column;
diff --git a/components/time_picker/Clock.js b/components/time_picker/Clock.js
index b5bae57ea..d432fdea4 100644
--- a/components/time_picker/Clock.js
+++ b/components/time_picker/Clock.js
@@ -1,19 +1,23 @@
-import React from 'react';
+import React, { Component, PropTypes } from 'react';
import CssTransitionGroup from 'react-addons-css-transition-group';
import { ZoomIn, ZoomOut } from '../animations';
-import style from './style.clock';
-import time from '../utils/time';
-import Hours from './ClockHours';
-import Minutes from './ClockMinutes';
+import time from '../utils/time.js';
+import Hours from './ClockHours.js';
+import Minutes from './ClockMinutes.js';
-class Clock extends React.Component {
+class Clock extends Component {
static propTypes = {
- className: React.PropTypes.string,
- display: React.PropTypes.oneOf(['hours', 'minutes']),
- format: React.PropTypes.oneOf(['24hr', 'ampm']),
- onChange: React.PropTypes.func,
- onHandMoved: React.PropTypes.func,
- time: React.PropTypes.object
+ className: PropTypes.string,
+ display: PropTypes.oneOf(['hours', 'minutes']),
+ format: PropTypes.oneOf(['24hr', 'ampm']),
+ onChange: PropTypes.func,
+ onHandMoved: PropTypes.func,
+ theme: PropTypes.shape({
+ clock: PropTypes.string,
+ clockWrapper: PropTypes.string,
+ placeholder: PropTypes.string
+ }),
+ time: PropTypes.object
};
static defaultProps = {
@@ -81,6 +85,7 @@ class Clock extends React.Component {
selected={this.props.time.getHours()}
spacing={this.state.radius * 0.18}
onHandMoved={this.props.onHandMoved}
+ theme={this.props.theme}
/>
);
}
@@ -94,17 +99,19 @@ class Clock extends React.Component {
selected={this.props.time.getMinutes()}
spacing={this.state.radius * 0.18}
onHandMoved={this.props.onHandMoved}
+ theme={this.props.theme}
/>
);
}
render () {
+ const { theme } = this.props;
const animation = this.props.display === 'hours' ? ZoomOut : ZoomIn;
return (
-
-
+
+
-
+
{this.props.display === 'hours' ? this.renderHours() : null}
{this.props.display === 'minutes' ? this.renderMinutes() : null}
diff --git a/components/time_picker/ClockFace.js b/components/time_picker/ClockFace.js
index ae1821325..6031468cb 100644
--- a/components/time_picker/ClockFace.js
+++ b/components/time_picker/ClockFace.js
@@ -1,13 +1,18 @@
-import React from 'react';
-import style from './style.clock';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
-class Face extends React.Component {
+class Face extends Component {
static propTypes = {
- active: React.PropTypes.number,
- numbers: React.PropTypes.array,
- radius: React.PropTypes.number,
- spacing: React.PropTypes.number,
- twoDigits: React.PropTypes.bool
+ active: PropTypes.number,
+ numbers: PropTypes.array,
+ radius: PropTypes.number,
+ spacing: PropTypes.number,
+ theme: PropTypes.shape({
+ active: PropTypes.string,
+ face: PropTypes.string,
+ number: PropTypes.string
+ }),
+ twoDigits: PropTypes.bool
};
static defaultProps = {
@@ -33,29 +38,29 @@ class Face extends React.Component {
}
renderNumber (number, idx) {
- let className = style.number;
- if (number === this.props.active) className += ` ${style.active}`;
+ const { active, radius, spacing, theme, twoDigits } = this.props;
return (
- {this.props.twoDigits ? ('0' + number).slice(-2) : number}
+ {twoDigits ? ('0' + number).slice(-2) : number}
);
}
render () {
+ const { numbers, onTouchStart, onMouseDown, theme } = this.props;
return (
- {this.props.numbers.map(this.renderNumber.bind(this))}
+ {numbers.map(this.renderNumber.bind(this))}
);
}
diff --git a/components/time_picker/ClockHand.js b/components/time_picker/ClockHand.js
index 8e4653132..53bd83626 100644
--- a/components/time_picker/ClockHand.js
+++ b/components/time_picker/ClockHand.js
@@ -1,18 +1,21 @@
-import React from 'react';
-import style from './style.clock';
-import events from '../utils/events';
-import prefixer from '../utils/prefixer';
-import utils from '../utils/utils';
+import React, { Component, PropTypes } from 'react';
+import events from '../utils/events.js';
+import prefixer from '../utils/prefixer.js';
+import utils from '../utils/utils.js';
-class Hand extends React.Component {
+class Hand extends Component {
static propTypes = {
- angle: React.PropTypes.number,
- className: React.PropTypes.string,
- length: React.PropTypes.number,
- onMove: React.PropTypes.func,
- onMoved: React.PropTypes.func,
- origin: React.PropTypes.object,
- step: React.PropTypes.number
+ angle: PropTypes.number,
+ className: PropTypes.string,
+ length: PropTypes.number,
+ onMove: PropTypes.func,
+ onMoved: PropTypes.func,
+ origin: PropTypes.object,
+ step: PropTypes.number,
+ theme: PropTypes.shape({
+ hand: PropTypes.string,
+ knob: PropTypes.string
+ })
};
static defaultProps = {
@@ -104,7 +107,8 @@ class Hand extends React.Component {
}
render () {
- const className = `${style.hand} ${this.props.className}`;
+ const { theme } = this.props;
+ const className = `${theme.hand} ${this.props.className}`;
const handStyle = prefixer({
height: this.props.length - this.state.knobWidth / 2,
transform: `rotate(${this.props.angle}deg)`
@@ -112,7 +116,7 @@ class Hand extends React.Component {
return (
);
}
diff --git a/components/time_picker/ClockHours.js b/components/time_picker/ClockHours.js
index a1c2491fb..5e37cd2e3 100644
--- a/components/time_picker/ClockHours.js
+++ b/components/time_picker/ClockHours.js
@@ -1,22 +1,23 @@
-import React from 'react';
-import utils from '../utils/utils';
-import Face from './ClockFace';
-import Hand from './ClockHand';
+import React, { Component, PropTypes } from 'react';
+import utils from '../utils/utils.js';
+import Hand from './ClockHand.js';
+import Face from './ClockFace.js';
const outerNumbers = [0, ...utils.range(13, 24)];
const innerNumbers = [12, ...utils.range(1, 12)];
const innerSpacing = 1.7;
const step = 360 / 12;
-class Hours extends React.Component {
+class Hours extends Component {
static propTypes = {
- center: React.PropTypes.object,
- format: React.PropTypes.oneOf(['24hr', 'ampm']),
- onChange: React.PropTypes.func,
- onHandMoved: React.PropTypes.func,
- radius: React.PropTypes.number,
- selected: React.PropTypes.number,
- spacing: React.PropTypes.number
+ center: PropTypes.object,
+ format: PropTypes.oneOf(['24hr', 'ampm']),
+ onChange: PropTypes.func,
+ onHandMoved: PropTypes.func,
+ radius: PropTypes.number,
+ selected: PropTypes.number,
+ spacing: PropTypes.number,
+ theme: PropTypes.object
};
state = {
@@ -59,6 +60,7 @@ class Hours extends React.Component {
numbers={innerNumbers}
spacing={this.props.spacing}
radius={innerRadius}
+ theme={this.props.theme}
active={this.props.selected}
/>
);
@@ -79,12 +81,14 @@ class Hours extends React.Component {
radius={radius}
twoDigits={is24hr}
active={is24hr ? selected : (selected % 12 || 12)}
+ theme={this.props.theme}
/>
{this.renderInnerFace(radius - spacing * innerSpacing)}
diff --git a/components/time_picker/TimePicker.js b/components/time_picker/TimePicker.js
index 6fa5bd0d6..fd2384157 100644
--- a/components/time_picker/TimePicker.js
+++ b/components/time_picker/TimePicker.js
@@ -1,70 +1,84 @@
-import React from 'react';
+import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
-import events from '../utils/events';
-import time from '../utils/time';
-import style from './style';
-import Input from '../input';
-import TimePickerDialog from './TimePickerDialog';
+import { themr } from 'react-css-themr';
+import { TIME_PICKER } from '../identifiers.js';
+import events from '../utils/events.js';
+import time from '../utils/time.js';
+import InjectDialog from '../dialog/Dialog.js';
+import InjectInput from '../input/Input.js';
+import timePickerDialogFactory from './TimePickerDialog.js';
-class TimePicker extends React.Component {
- static propTypes = {
- className: React.PropTypes.string,
- error: React.PropTypes.string,
- format: React.PropTypes.oneOf(['24hr', 'ampm']),
- inputClassName: React.PropTypes.string,
- label: React.PropTypes.string,
- onChange: React.PropTypes.func,
- value: React.PropTypes.object
- };
+const factory = (TimePickerDialog, Input) => {
+ class TimePicker extends Component {
+ static propTypes = {
+ className: PropTypes.string,
+ error: PropTypes.string,
+ format: PropTypes.oneOf(['24hr', 'ampm']),
+ inputClassName: PropTypes.string,
+ label: PropTypes.string,
+ onChange: PropTypes.func,
+ theme: PropTypes.shape({
+ input: PropTypes.string
+ }),
+ value: PropTypes.object
+ };
- static defaultProps = {
- className: '',
- format: '24hr'
- };
+ static defaultProps = {
+ className: '',
+ format: '24hr'
+ };
- state = {
- active: false
- };
+ state = {
+ active: false
+ };
- handleDismiss = () => {
- this.setState({active: false});
- };
+ handleDismiss = () => {
+ this.setState({active: false});
+ };
- handleInputMouseDown = (event) => {
- events.pauseEvent(event);
- this.setState({active: true});
- };
+ handleInputMouseDown = (event) => {
+ events.pauseEvent(event);
+ this.setState({active: true});
+ };
- handleSelect = (value, event) => {
- if (this.props.onChange) this.props.onChange(value, event);
- this.setState({active: false});
- };
+ handleSelect = (value, event) => {
+ if (this.props.onChange) this.props.onChange(value, event);
+ this.setState({active: false});
+ };
- render () {
- const { value, format, inputClassName } = this.props;
- const formattedTime = value ? time.formatTime(value, format) : null;
- return (
-
-
-
-
- );
+ render () {
+ const { value, format, inputClassName, theme } = this.props;
+ const formattedTime = value ? time.formatTime(value, format) : '';
+ return (
+
+
+
+
+ );
+ }
}
-}
-export default TimePicker;
+ return TimePicker;
+};
+
+const TimePickerDialog = timePickerDialogFactory(InjectDialog);
+const TimePicker = factory(TimePickerDialog, InjectInput);
+export default themr(TIME_PICKER)(TimePicker);
+export { factory as timePickerFactory };
+export { TimePicker };
diff --git a/components/time_picker/TimePickerDialog.js b/components/time_picker/TimePickerDialog.js
index ad52d9529..5791c5cf8 100644
--- a/components/time_picker/TimePickerDialog.js
+++ b/components/time_picker/TimePickerDialog.js
@@ -1,108 +1,128 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import style from './style';
-import time from '../utils/time';
-import Clock from './Clock';
-import Dialog from '../dialog';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import time from '../utils/time.js';
+import Clock from './Clock.js';
-class TimePickerDialog extends React.Component {
- static propTypes = {
- active: React.PropTypes.bool,
- className: React.PropTypes.string,
- format: React.PropTypes.oneOf(['24hr', 'ampm']),
- onDismiss: React.PropTypes.func,
- onSelect: React.PropTypes.func,
- value: React.PropTypes.object
- };
+const factory = (Dialog) => {
+ class TimePickerDialog extends Component {
+ static propTypes = {
+ active: PropTypes.bool,
+ className: PropTypes.string,
+ format: PropTypes.oneOf(['24hr', 'ampm']),
+ onDismiss: PropTypes.func,
+ onSelect: PropTypes.func,
+ theme: PropTypes.shape({
+ am: PropTypes.string,
+ amFormat: PropTypes.string,
+ ampm: PropTypes.string,
+ button: PropTypes.string,
+ dialog: PropTypes.string,
+ header: PropTypes.string,
+ hours: PropTypes.string,
+ hoursDisplay: PropTypes.string,
+ minutes: PropTypes.string,
+ minutesDisplay: PropTypes.string,
+ pm: PropTypes.string,
+ pmFormat: PropTypes.string,
+ separator: PropTypes.string
+ }),
+ value: PropTypes.object
+ };
- static defaultProps = {
- active: false,
- format: '24hr',
- value: new Date()
- };
+ static defaultProps = {
+ active: false,
+ format: '24hr',
+ value: new Date()
+ };
- state = {
- display: 'hours',
- displayTime: this.props.value
- };
+ state = {
+ display: 'hours',
+ displayTime: this.props.value
+ };
- componentDidUpdate (prevProps) {
- if (!prevProps.active && this.props.active) {
- setTimeout(this.refs.clock.handleCalculateShape, 1000);
+ componentDidUpdate (prevProps) {
+ if (!prevProps.active && this.props.active) {
+ setTimeout(this.refs.clock.handleCalculateShape, 1000);
+ }
}
- }
- handleClockChange = (value) => {
- this.setState({displayTime: value});
- };
+ handleClockChange = (value) => {
+ this.setState({displayTime: value});
+ };
- handleSelect = (event) => {
- this.props.onSelect(this.state.displayTime, event);
- };
+ handleSelect = (event) => {
+ this.props.onSelect(this.state.displayTime, event);
+ };
- toggleTimeMode = () => {
- this.setState({displayTime: time.toggleTimeMode(this.state.displayTime)});
- };
+ toggleTimeMode = () => {
+ this.setState({displayTime: time.toggleTimeMode(this.state.displayTime)});
+ };
- handleHandMoved = () => {
- if (this.state.display === 'hours') this.setState({display: 'minutes'});
- };
+ handleHandMoved = () => {
+ if (this.state.display === 'hours') this.setState({display: 'minutes'});
+ };
- switchDisplay = (display) => {
- this.setState({display});
- };
+ switchDisplay = (display) => {
+ this.setState({display});
+ };
- actions = [
- { label: 'Cancel', className: style.button, onClick: this.props.onDismiss },
- { label: 'Ok', className: style.button, onClick: this.handleSelect }
- ];
+ actions = [
+ { label: 'Cancel', className: this.props.theme.button, onClick: this.props.onDismiss },
+ { label: 'Ok', className: this.props.theme.button, onClick: this.handleSelect }
+ ];
- formatHours () {
- if (this.props.format === 'ampm') {
- return this.state.displayTime.getHours() % 12 || 12;
- } else {
- return this.state.displayTime.getHours();
+ formatHours () {
+ if (this.props.format === 'ampm') {
+ return this.state.displayTime.getHours() % 12 || 12;
+ } else {
+ return this.state.displayTime.getHours();
+ }
}
- }
- renderAMPMLabels () {
- if (this.props.format === 'ampm') {
+ renderAMPMLabels () {
+ const { theme } = this.props;
+ if (this.props.format === 'ampm') {
+ return (
+
+ AM
+ PM
+
+ );
+ }
+ }
+
+ render () {
+ const { theme } = this.props;
+ const display = `${this.state.display}Display`;
+ const format = `${time.getTimeMode(this.state.displayTime)}Format`;
+ const className = classnames([theme.dialog, theme[display], theme[format]], this.props.className);
return (
-
- AM
- PM
-
+
+
+
+ {('0' + this.formatHours()).slice(-2)}
+
+ :
+
+ {('0' + this.state.displayTime.getMinutes()).slice(-2)}
+
+ {this.renderAMPMLabels()}
+
+
+
);
}
}
- render () {
- const display = `display-${this.state.display}`;
- const format = `format-${time.getTimeMode(this.state.displayTime)}`;
- const className = ClassNames([style.dialog, style[display], style[format]], this.props.className);
- return (
-
-
-
- {('0' + this.formatHours()).slice(-2)}
-
- :
-
- {('0' + this.state.displayTime.getMinutes()).slice(-2)}
-
- {this.renderAMPMLabels()}
-
-
-
- );
- }
-}
+ return TimePickerDialog;
+};
-export default TimePickerDialog;
+export default factory;
diff --git a/components/time_picker/index.js b/components/time_picker/index.js
index 57f9fc91c..c9ec8b8f7 100644
--- a/components/time_picker/index.js
+++ b/components/time_picker/index.js
@@ -1 +1,12 @@
-export default from './TimePicker';
+import { themr } from 'react-css-themr';
+import { TIME_PICKER } from '../identifiers.js';
+import { timePickerFactory } from './TimePicker.js';
+import timePickerDialogFactory from './TimePickerDialog.js';
+import Dialog from '../dialog';
+import Input from '../input';
+import theme from './theme.scss';
+
+const TimePickerDialog = timePickerDialogFactory(Dialog);
+const ThemedTimePicker = themr(TIME_PICKER, theme)(timePickerFactory(TimePickerDialog, Input));
+export default ThemedTimePicker;
+export { ThemedTimePicker as TimePicker };
diff --git a/components/time_picker/readme.md b/components/time_picker/readme.md
index de3ce67a4..84c3bc126 100644
--- a/components/time_picker/readme.md
+++ b/components/time_picker/readme.md
@@ -5,23 +5,32 @@ A [dialog picker](https://www.google.com/design/spec/components/pickers.html#pic
```jsx
import TimePicker from 'react-toolbox/lib/time_picker';
+
let time = new Date();
time.setHours(17);
time.setMinutes(28);
class TimePickerTest extends React.Component {
- state = {time};
+ state = {time};
handleChange = (time) => {
- this.setState({time});
+ this.setState({time});
};
render () {
- return ;
+ return (
+
+ );
}
}
```
+If you want to provide a theme via context, the component key is `RTTimePicker`.
+
## Properties
| Name | Type | Default | Description|
@@ -33,3 +42,31 @@ class TimePickerTest extends React.Component {
| `label` | `String` | | The text string to use for the floating label element in the input component.|
| `onChange` | `Function` | | Callback called when the picker value is changed.|
| `value` | `Date` | | Datetime object with currrently selected time. |
+
+## Theme
+
+| Name | Description|
+|:---------|:-----------|
+| `active` | Added to the number which is active in clock face.|
+| `am` | AM label in dialog header when mode is AM/PM.|
+| `amFormat` | Added to the dialog when the selected format is AM.|
+| `ampm` | Wrapper for AM and PM labels in header when mode is AM/PM.|
+| `button` | Used for buttons inside the dialog of the picker.|
+| `clock` | Clock root class element.|
+| `clockWrapper` | Wrapper for the proper positioning of the clock.|
+| `dialog` | Used for the dialog component.|
+| `face` | Used to style the clock face.|
+| `hand` | Used for the clock's hand.|
+| `header` | Dialog header wrapper class.|
+| `hours` | Used for hours in dialog header.|
+| `hoursDisplay` | Added to the dialog hours are displayed.|
+| `input` | Used for Input element that opens the picker.|
+| `knob` | Used for the knob of the hand.|
+| `minutes` | Used for minutes in dialog header.|
+| `minutesDisplay` | Added to the dialog minutes are displayed.|
+| `number` | Each of the numbers in the clock's face.|
+| `placeholder` | Placeholder for the clock inside the dialog (inner wrapper).|
+| `pm` | PM label in dialog header when mode is AM/PM.|
+| `pmFormat` | Added to the dialog when the selected format is PM.|
+| `separator` | Is the `:` separator between hours and minutes in dialog header.|
+| `small` | Added to the knob when no round number is selected.|
diff --git a/components/time_picker/style.scss b/components/time_picker/style.scss
deleted file mode 100644
index 7bead8753..000000000
--- a/components/time_picker/style.scss
+++ /dev/null
@@ -1,66 +0,0 @@
-@import "../base";
-@import "./config";
-
-.input > [role="input"] {
- cursor: pointer;
-}
-
-.header {
- position: relative;
- width: 100%;
- padding: $timepicker-header-padding;
- font-size: $timepicker-header-font-size;
- color: $timepicker-primary-contrast-color;
- text-align: center;
- background: $timepicker-primary-color;
-}
-
-.hours, .minutes {
- display: inline-block;
- cursor: pointer;
- opacity: .6;
-}
-
-.separator {
- margin: 0 $timepicker-header-padding / 2;
- opacity: .6;
-}
-
-.ampm {
- position: absolute;
- top: 50%;
- right: 2 * $unit;
- width: $timepicker-ampm-width;
- height: $timepicker-ampm-height * 2;
- margin-top: - $timepicker-ampm-height;
- font-size: $timepicker-ampm-font-size;
- line-height: $timepicker-ampm-height;
- text-align: center;
-}
-
-.am, .pm {
- display: block;
- cursor: pointer;
- opacity: .6;
-}
-
-.dialog {
- width: $timepicker-dialog-width;
- > [role="body"] {
- padding: 0;
- overflow-y: visible;
- }
- > [role="navigation"] > .button {
- color: $timepicker-primary-color;
- &:hover {
- background: $timepicker-primary-hover-color;
- }
- &:focus:not(:active) {
- background: $timepicker-primary-hover-color;
- }
- }
- &.display-hours .hours, &.display-minutes .minutes, &.format-am .am,
- &.format-pm .pm {
- opacity: 1;
- }
-}
diff --git a/components/time_picker/style.clock.scss b/components/time_picker/theme.scss
similarity index 61%
rename from components/time_picker/style.clock.scss
rename to components/time_picker/theme.scss
index e89bd53b9..297982895 100644
--- a/components/time_picker/style.clock.scss
+++ b/components/time_picker/theme.scss
@@ -1,7 +1,73 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
-.root {
+.input > [role="input"] {
+ cursor: pointer;
+}
+
+.header {
+ position: relative;
+ width: 100%;
+ padding: $timepicker-header-padding;
+ font-size: $timepicker-header-font-size;
+ color: $timepicker-primary-contrast-color;
+ text-align: center;
+ background: $timepicker-primary-color;
+}
+
+.hours, .minutes {
+ display: inline-block;
+ cursor: pointer;
+ opacity: .6;
+}
+
+.separator {
+ margin: 0 $timepicker-header-padding / 2;
+ opacity: .6;
+}
+
+.ampm {
+ position: absolute;
+ top: 50%;
+ right: 2 * $unit;
+ width: $timepicker-ampm-width;
+ height: $timepicker-ampm-height * 2;
+ margin-top: - $timepicker-ampm-height;
+ font-size: $timepicker-ampm-font-size;
+ line-height: $timepicker-ampm-height;
+ text-align: center;
+}
+
+.am, .pm {
+ display: block;
+ cursor: pointer;
+ opacity: .6;
+}
+
+.dialog {
+ width: $timepicker-dialog-width;
+ > [role="body"] {
+ padding: 0;
+ overflow-y: visible;
+ }
+ > [role="navigation"] > .button {
+ color: $timepicker-primary-color;
+ &:hover {
+ background: $timepicker-primary-hover-color;
+ }
+ &:focus:not(:active) {
+ background: $timepicker-primary-hover-color;
+ }
+ }
+ &.hoursDisplay .hours, &.minutesDisplay .minutes, &.amFormat .am,
+ &.pmFormat .pm {
+ opacity: 1;
+ }
+}
+
+.clock {
padding: $clock-padding;
}
@@ -10,7 +76,7 @@
z-index: $z-index-high;
}
-.wrapper {
+.clockWrapper {
position: absolute;
width: 100%;
background-color: $color-divider;
diff --git a/components/tooltip/Tooltip.js b/components/tooltip/Tooltip.js
index 6694aa90b..e926b1590 100644
--- a/components/tooltip/Tooltip.js
+++ b/components/tooltip/Tooltip.js
@@ -1,67 +1,83 @@
-import React from 'react';
-import ClassNames from 'classnames';
-import style from './style';
+import React, { Component, PropTypes } from 'react';
+import classnames from 'classnames';
+import { themr } from 'react-css-themr';
+import { TOOLTIP } from '../identifiers.js';
-const Tooltip = (ComposedComponent) => class extends React.Component {
- static propTypes = {
- children: React.PropTypes.any,
- className: React.PropTypes.string,
- onClick: React.PropTypes.func,
- onMouseEnter: React.PropTypes.func,
- onMouseLeave: React.PropTypes.func,
- tooltip: React.PropTypes.string,
- tooltipDelay: React.PropTypes.number,
- tooltipHideOnClick: React.PropTypes.bool
- };
+const factory = (defaultTheme = {}) => {
+ const Tooltip = (ComposedComponent) => {
+ class TooltippedComponent extends Component {
+ static propTypes = {
+ children: PropTypes.any,
+ className: PropTypes.string,
+ onClick: PropTypes.func,
+ onMouseEnter: PropTypes.func,
+ onMouseLeave: PropTypes.func,
+ theme: PropTypes.shape({
+ tooltip: PropTypes.string,
+ tooltipActive: PropTypes.string,
+ tooltipWrapper: PropTypes.string
+ }),
+ tooltip: PropTypes.string,
+ tooltipDelay: PropTypes.number,
+ tooltipHideOnClick: PropTypes.bool
+ };
- static defaultProps = {
- className: '',
- tooltipDelay: 0,
- tooltipHideOnClick: true
- };
+ static defaultProps = {
+ className: '',
+ tooltipDelay: 0,
+ tooltipHideOnClick: true
+ };
- state = {
- active: false
- };
+ state = {
+ active: false
+ };
- handleMouseEnter = (event) => {
- if (this.timeout) clearTimeout(this.timeout);
- this.timeout = setTimeout(() =>this.setState({active: true}), this.props.tooltipDelay);
- if (this.props.onMouseEnter) this.props.onMouseEnter(event);
- };
+ handleMouseEnter = (event) => {
+ if (this.timeout) clearTimeout(this.timeout);
+ this.timeout = setTimeout(() =>this.setState({active: true}), this.props.tooltipDelay);
+ if (this.props.onMouseEnter) this.props.onMouseEnter(event);
+ };
- handleMouseLeave = (event) => {
- if (this.timeout) clearTimeout(this.timeout);
- if (this.state.active) this.setState({active: false});
- if (this.props.onMouseLeave) this.props.onMouseLeave(event);
- };
+ handleMouseLeave = (event) => {
+ if (this.timeout) clearTimeout(this.timeout);
+ if (this.state.active) this.setState({active: false});
+ if (this.props.onMouseLeave) this.props.onMouseLeave(event);
+ };
- handleClick = (event) => {
- if (this.timeout) clearTimeout(this.timeout);
- if (this.props.tooltipHideOnClick) this.setState({active: false});
- if (this.props.onClick) this.props.onClick(event);
- };
+ handleClick = (event) => {
+ if (this.timeout) clearTimeout(this.timeout);
+ if (this.props.tooltipHideOnClick) this.setState({active: false});
+ if (this.props.onClick) this.props.onClick(event);
+ };
+
+ render () {
+ const {children, className, tooltip,
+ tooltipDelay, tooltipHideOnClick, theme, ...other} = this.props; //eslint-disable-line no-unused-vars
+ const composedClassName = classnames(this.props.theme.tooltipWrapper, className);
+ const tooltipClassName = classnames(this.props.theme.tooltip, {
+ [this.props.theme.tooltipActive]: this.state.active
+ });
- render () {
- const {children, className, tooltip, tooltipDelay, tooltipHideOnClick, ...other} = this.props; //eslint-disable-line no-unused-vars
- const composedClassName = ClassNames(style.root, className);
- const tooltipClassName = ClassNames(style.tooltip, {
- [style.active]: this.state.active
- });
+ return (
+
+ {children ? children : null}
+ {tooltip}
+
+ );
+ }
+ }
+
+ return themr(TOOLTIP, defaultTheme)(TooltippedComponent);
+ };
- return (
-
- {children ? children : null}
- {tooltip}
-
- );
- }
+ return Tooltip;
};
-export default Tooltip;
+export default factory();
+export { factory as tooltipFactory };
diff --git a/components/tooltip/index.js b/components/tooltip/index.js
index 17f06b52b..3fcd819f2 100644
--- a/components/tooltip/index.js
+++ b/components/tooltip/index.js
@@ -1 +1,4 @@
-export default from './Tooltip';
+import { tooltipFactory } from './Tooltip.js';
+import theme from './theme.scss';
+
+export default tooltipFactory(theme);
diff --git a/components/tooltip/readme.md b/components/tooltip/readme.md
index e5f27bf29..19db1ee45 100644
--- a/components/tooltip/readme.md
+++ b/components/tooltip/readme.md
@@ -22,6 +22,8 @@ const TooltipTest = () => (
);
```
+This component can be styled by context providing a theme with the key `RTTooltip` through the theme provider.
+
## Properties
In any component you decorate with the Tooltip you'd get some additional props:
@@ -35,3 +37,11 @@ In any component you decorate with the Tooltip you'd get some additional props:
| `tooltip` | `String` | | The text string to use for the tooltip.|
| `tooltipDelay` | `Number` | | Amount of time in miliseconds spent before the tooltip is visible.|
| `tooltipHideOnClick` | `Boolean` | `true` | If true, the Tooltip hides after a click in the host component.|
+
+## Theming
+
+| Name | Description|
+|:---------|:-----------|
+| `tooltip` | Added to the tooltip element.|
+| `tooltipActive` | Added to the root when the tooltip is active.|
+| `tooltipWrapper` | Wrapper for the root element used to position the tooltip.|
diff --git a/components/tooltip/style.scss b/components/tooltip/theme.scss
similarity index 87%
rename from components/tooltip/style.scss
rename to components/tooltip/theme.scss
index 85b9867a0..e87d013f2 100644
--- a/components/tooltip/style.scss
+++ b/components/tooltip/theme.scss
@@ -1,7 +1,9 @@
-@import "../base";
+@import "../colors";
+@import "../globals";
+@import "../mixins";
@import "./config";
-.root {
+.tooltipWrapper {
position: relative;
}
@@ -26,7 +28,7 @@
transition: $animation-curve-default $tooltip-animation-duration transform;
transform: scale(0) translateX(-50%);
transform-origin: top left;
- &.active {
+ &.tooltipActive {
transform: scale(1) translateX(-50%);
}
}
diff --git a/components/utils/index.js b/components/utils/index.js
index c4b256034..5e804193e 100644
--- a/components/utils/index.js
+++ b/components/utils/index.js
@@ -1,7 +1,7 @@
-import events from './events';
-import prefixer from './prefixer';
-import time from './time';
-import utils from './utils';
+import events from './events.js';
+import prefixer from './prefixer.js';
+import time from './time.js';
+import utils from './utils.js';
export default {events, prefixer, time, utils};
export {events};
diff --git a/components/utils/utils.js b/components/utils/utils.js
index 202caf082..bf805bc8c 100644
--- a/components/utils/utils.js
+++ b/components/utils/utils.js
@@ -53,7 +53,7 @@ export default {
prepareValueForInput (value, type) {
if (type === 'date') return new Date(value).toISOString().slice(0, 10);
if (type === 'checkbox') {
- return value ? 'on' : null;
+ return value ? 'on' : '';
}
return value;
}
diff --git a/docs/app/components/_globals.scss b/docs/app/components/_globals.scss
index 2cfef2640..cfd472090 100644
--- a/docs/app/components/_globals.scss
+++ b/docs/app/components/_globals.scss
@@ -1,6 +1,5 @@
-$import-flex-attributes: true;
-
@import "~react-toolbox/base";
+@import "../theme/theme";
$unit: 1rem;
$color-primary-dark: $color-primary-dark;
diff --git a/docs/app/components/appbar/style.scss b/docs/app/components/appbar/style.scss
index fce7d795d..8e97ff23f 100644
--- a/docs/app/components/appbar/style.scss
+++ b/docs/app/components/appbar/style.scss
@@ -8,7 +8,6 @@
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
- background: $color-primary-dark;
box-shadow: $appbar-shadow;
}
diff --git a/docs/app/components/layout/install/install.md b/docs/app/components/layout/install/install.md
index c3fde6bde..54d026650 100644
--- a/docs/app/components/layout/install/install.md
+++ b/docs/app/components/layout/install/install.md
@@ -1,65 +1,218 @@
# Installation, usage and customization
-React Toolbox is a set of [React](http://facebook.github.io/react/) components that implement [Google's Material Design specification](https://www.google.com/design/spec/material-design/introduction.html). It's powered by [CSS Modules](https://github.com/css-modules/css-modules) and harmoniously integrates with your [Webpack](http://webpack.github.io/) workflow. You can take a tour through our documentation website and try the components live!
+React Toolbox is a set of [React](http://facebook.github.io/react/) components that implement [Google's Material Design specification](https://www.google.com/design/spec/material-design/introduction.html). It's powered by [CSS Modules](https://github.com/css-modules/css-modules) and harmoniously integrates with your [webpack](http://webpack.github.io/) workflow, although you can use any other module bundler. You can take a tour through our documentation website and try the components live!
## Installation
-React Toolbox can be installed as an [npm package](https://www.npmjs.org/package/react-toolbox);
+React Toolbox can be installed as an [npm package](https://www.npmjs.org/package/react-toolbox):
+```bash
+$ npm install --save react-toolbox
```
-npm install --save react-toolbox
+
+## Prerequisites
+
+React Toolbox uses [CSS Modules](https://github.com/css-modules/css-modules) by default to import stylesheets written in [SASS](http://sass-lang.com/). In case you want to import the components already bundled with CSS, your module bundler should be able to require these SASS modules.
+
+Although we recommend [webpack](https://webpack.github.io/), you are free to use whatever module bundler you want as long as it can compile and require SASS files located in your `node_modules`. If you are experiencing require errors, make sure your configuration satisfies this requirement.
+
+Of course this is a set of React components so you should be familiar with [React](https://facebook.github.io/react/). If want to customize your components via themes, you may want to take a look to [react-css-themr](https://github.com/javivelasco/react-css-themr) which is used by React Toolbox to make component easily themeable.
+
+## Basic usage
+
+In this minimal example, we import a `Button` with styles already bundled:
+
+```js
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { Button } from 'react-toolbox/lib/button';
+
+ReactDOM.render(
+ ,
+ document.getElementById('app')
+);
+```
+
+Take into account that any required style will be included in the final CSS so your final would include `Button` styles in this case. It's more convenient to import components this way (or with raw imports) because if you require from the project root, every stylesheet of React Toolbox will be included, even if you don't use it.
+
+## Importing components
+
+First let's take a look on how the components are structured in the project. The [components](https://github.com/react-toolbox/react-toolbox/tree/dev/components) folder contains a folder for each component or set of related components. For example, the `app_bar`:
+
+```
+ |- /app_bar
+ |---- AppBar.js
+ |---- _config.scss
+ |---- index.js
+ |---- readme.md
+ |---- theme.scss
+```
+
+As you can see in the previous block, each folder includes: a Javascript file for each component/subcomponent; a README with documentation, an index Javascript file that imports and injects styles and dependencies for you, a default theme SASS stylesheet and a configuration partial with configuration variables. Depending on whether you want the styles to be directly bundled or not, you can import components in **two** different ways.
+
+### Bundled component
+
+You import from the index file so the imported component comes with all dependencies and themes already required and injected for you. This means that the CSS for each dependency will be bundled in your final CSS automatically and the component markup includes the classnames to be styled. For example:
+
+```js
+import { AppBar } from 'react-toolbox/lib/app_bar';
```
-## Usage
+### Raw component
-Although there are other ways to use React Toolbox, the recommended way is to create a Webpack workflow with [Babel Loader](https://github.com/babel/babel-loader), [CSS Loader](https://github.com/webpack/css-loader) and [SASS Loader](https://github.com/jtangelder/sass-loader). A good starting point is [React Hot Webpack Boilerplate](https://github.com/gaearon/react-hot-boilerplate).
+You import from the component definition so the imported component is bundled with its dependencies but it does not require any style for you. This means that no CSS will be bundled and the component markup will **not** include any classname. It's your responsibility to provide a theme to the component to be properly style and you can do it via properties or context. For example:
+
+```js
+import { AppBar } from 'react-toolbox/lib/app_bar/AppBar.js';
+```
-Once you have the workflow ready, you can just require and use the components:
+## Customizing components
-```jsx
+Every component accepts a `theme` property intended to provide a [CSS Module import object](https://github.com/css-modules/css-modules) that will be used by the component to assign local classnames to its DOM nodes. Therefore, each one implements a documented **classname API** so if you want to customize a component, you just need to provide a theme object with the appropriated classname mapping.
+
+If the component has already a theme injected, the properties you pass will be merged with the injected. In this way, you can **add** classnames to the nodes of a specific component and use them to add or to override styles. For example, if you want to customize the `AppBar` to be purple:
+
+```js
import React from 'react';
-import Button from 'react-toolbox/lib/button';
+import { AppBar } from 'react-toolbox/lib/app_bar';
+import theme from './PurpleAppBar.scss';
-const CustomButton = () => (
-
+const PurpleAppBar = (props) => (
+
);
-export default CustomButton;
+export default PurpleAppBar;
+
+```
+
+```scss
+.appBar {
+ background-color: #800080;
+}
```
-The previous code creates a React button component based on React toolbox button. It's important to notice that requiring a module from the exposed root of the package will import the **SASS** of the component.
+In this case we are **adding** styles to an `AppBar` component that already has some styles injected. It works because the component background by default has the same priority as the one we added. There will be cases where the original rule is more restrictive. For those cases you would need to boost priority using the same restrictions as in the original stylesheet. Feel free to take a look into the original themes or just check the selectors you want to override in DevTools.
-## Roboto Font and Material Design Icons
+If the component has no styles injected, you should provide a theme object implementing the full API. You are free to require the CSS Module you want but take into account that every classname is there for a reason. You can either provide a theme via prop or via context as we will see later.
-React Toolbox assumes that you are importing [Roboto Font](https://www.google.com/fonts/specimen/Roboto) and [Material Design Icons](https://www.google.com/design/icons/).
+## Theming
-In order to import the fonts for you, we'd need to include them in the CSS which is considered a bad practice. If you are not including them in your app to the linked sites and follow the instructions.
+You can afford theming in multiple ways. First of all, you have to understand that React Toolbox stylesheets are written in SASS and configured using the **config** files we saw earlier. Also you may want to check [colors](https://github.com/react-toolbox/react-toolbox/blob/dev/components/_colors.scss) and [globals](https://github.com/react-toolbox/react-toolbox/blob/dev/components/_globals.scss) files to get an overview on the **variables** you have to override to get the results you want.
-## Customization
+In most scenarios you can get more customized themes by overriding those variables and compiling stylesheets with them. For example, you can create a `_theme.scss` SASS file:
-Since React Toolbox styles are written in CSS it's pretty easy to customize your components. We have several ways:
+```scss
+@import "~react-toolbox/lib/colors";
-### Via React Toolbox Loader
+$color-primary: $palette-blue-500;
+$color-primary-dark: $palette-blue-700;
+```
-Thanks to the power of SASS, all components in React Toolbox are configured from a variables file. The best way to customize your build is to create a custom configuration SASS file overriding configuration variables like colors or sizes.
+This file should be prepended to each stylesheet compilation which be achieved in multiple ways.
-With [toolbox-loader](https://github.com/react-toolbox/toolbox-loader) you can tell webpack where your configuration file is and it will prepend your config to each SASS build. This will result in your customized CSS for React Toolbox Components. For now you can browse the configuration files and override what you want.
+### Using SASS Loader
+
+If you are using [Webpack](http://webpack.github.io/) as module bundler, you are probably using [sass-loader](https://github.com/jtangelder/sass-loader) as well. What we want to do is to prepend to each SASS file compilation a bunch of variables to override and this can be done with the `data` option. For example:
+
+```js
+sassLoader: {
+ data: '@import "' + path.resolve(__dirname, 'theme/_theme.scss') + '";'
+}
+```
-### Via `className` property
+In this case we have are prepending the theme import to each SASS compilation so the primary color will be changed in every single stylesheet. If you are not using webpack maybe your loader still has a similar option, otherwise don't worry, there are solutions.
-Generally each component will have a `className` prop so that you can apply a class name to the root node of the resulting markup. All markup is styled with the lowest specificity level so you can just nest one level in your CSS and the result will be applied. Consider this example:
+### Using SASS imports and props
-```jsx
-const CustomButton = () => (
-
+Remember that you can import components without styles and provide those styles using the theme property. For example, a theme for a button customized with the previous theme file would be like:
+
+```scss
+@import "theme.scss";
+@import "~react-toolbox/lib/button/theme";
+```
+
+Then, when you use a button you can inject the appropriated theme:
+
+```js
+import { Button } from 'react-toolbox/lib/button/Button';
+import buttonTheme from './theme/button.scss';
+
+const ThemedButton = (props) => (
+
);
+
+export default ThemedButton;
```
-If you browse the resulting markup you will see *data attributes* like `data-role="label"` which can be used to style components without using tag name selectors. You can now write your CSS:
+With this technique you have to create wrappers for every component and this is not cool at all... but don't worry, we can provide the theme via context to avoid this.
+
+### Using SASS imports and context
-```css
-.customized > [data-react-toolbox="label"] {
- color: green;
- font-weight: bold;
+This is a good moment to check out [react-css-themr](http://github.com/javivelasco/react-css-themr) if you still didn't. Every component in React Toolbox has a key assigned that can be used to provide a default CSS Module. You can create a theme like:
+
+```js
+export default {
+ RTRipple: require('./ripple.scss'),
+ RTButton: require('./button.scss')
}
```
+
+Check for each component what key uses. Then, when you have a theme object fully imported and customized for each component your application uses, you can use it like we list here:
+
+```js
+import React from 'react';
+import { render } from 'react-dom';
+import { ThemeProvider } from 'react-css-themr';
+import theme from './theme/theme.js';
+import App from './App.js';
+
+render(
+
+
+
+, document.getElementById('app'))
+```
+
+A couple of things here. First you need to use raw components to get this styles properly applied. Second, you have to add dependency themes by yourself. For example, the `Button` requires `Ripple` so you have to provide styles for both of them.
+
+## Roboto Font and Material Design Icons
+
+React Toolbox assumes that you are importing [Roboto Font](https://www.google.com/fonts/specimen/Roboto) and [Material Design Icons](https://www.google.com/design/icons/).
+
+In order to import the fonts for you, we'd need to include them in the CSS which is considered a bad practice. If you are not including them in your app, go to the linked sites and follow the instructions.
+
+## Server Side Rendering
+
+The only requirement for SSR is to be able to require ES6 and CSS Modules in the backend. To make it possible you can check projects like [CSS Modules register hook](https://github.com/css-modules/css-modules-require-hook) or [Webpack Isomorphic tools](https://github.com/halt-hammerzeit/webpack-isomorphic-tools). Also, make sure you can import from `node_modules`.
+
+## Examples
+
+For now we have a [repository example](https://github.com/react-toolbox/react-toolbox-example) demonstrating configuration and some basic customization. For now it's not using SSR rendering but it shouldn't be difficult to implement an example so it will come soon. Feel free to PR your example project or to add some use cases to the repository.
+
+## TypeScript
+
+A TypeScript definition file `react-toolbox.d.ts` is available. It is referenced in `package.json` and should be picked up by the TypeScript compiler when importing from the npm package.
+
+Note that to comply with the typings requirement, a triple-slash reference to `react.d.ts` is *NOT included*. You will need to reference [react.d.ts](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/react/react.d.ts) somewhere in your project.
+
+## Authors and Contributors
+
+The project is being initially developed and maintained by [Javier Velasco](http://javivelasco.com) and [Javier Jiménez](http://soyjavi.com) and the [contribution scene](https://github.com/react-toolbox/react-toolbox/graphs/contributors) is just getting warm. We want to create reference components so any contribution is very welcome.
+
+To work in the project you'd need a `node` version supporting ES6 syntax. Although the project is built using Babel we use some ES6 features in the development server. Also, the package has been tested with `node 4.2.1`. Consider using [nvm](https://github.com/creationix/nvm) or [n](https://github.com/tj/n) to handle different node versions!
+
+To start the documentation site locally, you'll need to install the dependencies from both the main package and the docs subproject:
+
+```
+$ git clone https://github.com/react-toolbox/react-toolbox.git
+$ npm install
+$ cd docs/
+$ npm install
+$ npm start
+```
+
+Local documentation will then be available at `http://localhost:8081/`.
+
+## License
+
+This project is licensed under the terms of the [MIT license](https://github.com/react-toolbox/react-toolbox/blob/master/LICENSE).
diff --git a/docs/app/components/layout/main/button-theme.scss b/docs/app/components/layout/main/button-theme.scss
new file mode 100644
index 000000000..a2900e340
--- /dev/null
+++ b/docs/app/components/layout/main/button-theme.scss
@@ -0,0 +1,2 @@
+@import "../../globals";
+@import "~react-toolbox/button/theme";
diff --git a/docs/app/components/layout/main/index.js b/docs/app/components/layout/main/index.js
index 7f81cdf1a..7685e20f6 100644
--- a/docs/app/components/layout/main/index.js
+++ b/docs/app/components/layout/main/index.js
@@ -7,6 +7,7 @@ import Playground from './components/playground.js';
import MainNavigation from './components/navigation.js';
import BaseDocs from './modules/components.md';
import components from './modules/components.js';
+import buttonTheme from './button-theme.scss';
import style from './style';
const LoadExampleButton = (props) => (
@@ -14,6 +15,7 @@ const LoadExampleButton = (props) => (
accent
icon='code'
label="Load in playground"
+ theme={buttonTheme}
onClick={props.onClick}
raised
/>
diff --git a/docs/app/components/preview/index.js b/docs/app/components/preview/index.js
index e7b6cd492..e9b45aed9 100644
--- a/docs/app/components/preview/index.js
+++ b/docs/app/components/preview/index.js
@@ -1,7 +1,10 @@
+/*eslint-disable no-eval*/
import React from 'react';
import ReactDOM from 'react-dom';
+import { ThemeProvider } from 'react-css-themr';
import { transform } from 'babel-standalone';
import * as ReactToolbox from 'react-toolbox';
+import theme from '../../theme/theme.js';
import style from './style';
const ERROR_TIMEOUT = 500;
@@ -70,11 +73,11 @@ const Preview = React.createClass({
}
try {
- const compiledCode = this.compileCode();
-
- /*eslint-disable no-eval*/
- const Component = eval(compiledCode)(...scope);
- ReactDOM.render(Component, mountNode);
+ ReactDOM.render(
+
+ {eval(this.compileCode())(...scope)}
+
+ , mountNode);
if (this.state.error) {
this.setState({error: null});
}
diff --git a/docs/app/index.js b/docs/app/index.js
index 56dcdcb2d..1b5ecc877 100644
--- a/docs/app/index.js
+++ b/docs/app/index.js
@@ -2,9 +2,6 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, useRouterHistory } from 'react-router';
import createHashHistory from 'history/lib/createHashHistory';
-
-import 'react-toolbox/commons';
-
import Home from './components/layout/home';
import Install from './components/layout/install';
import Main from './components/layout/main';
diff --git a/docs/theme.scss b/docs/app/theme/_theme.scss
similarity index 83%
rename from docs/theme.scss
rename to docs/app/theme/_theme.scss
index 4eef11c78..96be0e6ba 100644
--- a/docs/theme.scss
+++ b/docs/app/theme/_theme.scss
@@ -1,3 +1,5 @@
+@import "~react-toolbox/colors"; // Import minimal colors
+
$color-primary: $palette-indigo-500;
$color-primary-dark: $palette-indigo-700;
$color-primary-light: $palette-indigo-500;
diff --git a/docs/app/theme/theme.js b/docs/app/theme/theme.js
new file mode 100644
index 000000000..68c3ea1c6
--- /dev/null
+++ b/docs/app/theme/theme.js
@@ -0,0 +1,61 @@
+import 'react-toolbox/commons';
+
+import ToolboxAppBar from 'react-toolbox/app_bar/theme.scss';
+import ToolboxAutocomplete from 'react-toolbox/autocomplete/theme.scss';
+import ToolboxAvatar from 'react-toolbox/avatar/theme.scss';
+import ToolboxButton from 'react-toolbox/button/theme.scss';
+import ToolboxCard from 'react-toolbox/card/theme.scss';
+import ToolboxCheckbox from 'react-toolbox/checkbox/theme.scss';
+import ToolboxChip from 'react-toolbox/chip/theme.scss';
+import ToolboxDatePicker from 'react-toolbox/date_picker/theme.scss';
+import ToolboxDialog from 'react-toolbox/dialog/theme.scss';
+import ToolboxDrawer from 'react-toolbox/drawer/theme.scss';
+import ToolboxDropdown from 'react-toolbox/dropdown/theme.scss';
+import ToolboxInput from 'react-toolbox/input/theme.scss';
+import ToolboxLayout from 'react-toolbox/layout/theme.scss';
+import ToolboxLink from 'react-toolbox/link/theme.scss';
+import ToolboxList from 'react-toolbox/list/theme.scss';
+import ToolboxMenu from 'react-toolbox/menu/theme.scss';
+import ToolboxNavigation from 'react-toolbox/navigation/theme.scss';
+import ToolboxOverlay from 'react-toolbox/overlay/theme.scss';
+import ToolboxProgress from 'react-toolbox/progress_bar/theme.scss';
+import ToolboxRadio from 'react-toolbox/radio/theme.scss';
+import ToolboxRipple from 'react-toolbox/ripple/theme.scss';
+import ToolboxSlider from 'react-toolbox/slider/theme.scss';
+import ToolboxSnackbar from 'react-toolbox/snackbar/theme.scss';
+import ToolboxSwitch from 'react-toolbox/switch/theme.scss';
+import ToolboxTable from 'react-toolbox/table/theme.scss';
+import ToolboxTabs from 'react-toolbox/tabs/theme.scss';
+import ToolboxTimePicker from 'react-toolbox/time_picker/theme.scss';
+import ToolboxTooltip from 'react-toolbox/tooltip/theme.scss';
+
+export default {
+ ToolboxAppBar,
+ ToolboxAutocomplete,
+ ToolboxAvatar,
+ ToolboxButton,
+ ToolboxCard,
+ ToolboxCheckbox,
+ ToolboxChip,
+ ToolboxDatePicker,
+ ToolboxDialog,
+ ToolboxDrawer,
+ ToolboxDropdown,
+ ToolboxInput,
+ ToolboxLayout,
+ ToolboxLink,
+ ToolboxList,
+ ToolboxMenu,
+ ToolboxNavigation,
+ ToolboxOverlay,
+ ToolboxProgress,
+ ToolboxRadio,
+ ToolboxRipple,
+ ToolboxSlider,
+ ToolboxSnackbar,
+ ToolboxSwitch,
+ ToolboxTable,
+ ToolboxTabs,
+ ToolboxTimePicker,
+ ToolboxTooltip
+};
diff --git a/docs/package.json b/docs/package.json
index 21a3335f0..eafc34133 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -14,6 +14,7 @@
"codemirror": "^5.14.2",
"history": "^2.1.1",
"react": "^15.0.2",
+ "react-css-themr": "^1.1.0",
"react-dom": "^15.0.2",
"react-router": "^2.4.0"
},
@@ -45,7 +46,6 @@
"redbox-react": "^1.2.4",
"sass-loader": "^3.2.0",
"style-loader": "^0.13.1",
- "toolbox-loader": "0.0.3",
"transfer-webpack-plugin": "^0.1.4",
"webpack": "^1.13.0",
"webpack-dev-middleware": "^1.6.1",
diff --git a/docs/webpack.config.development.js b/docs/webpack.config.development.js
index 906bf0bc7..7d4857f87 100644
--- a/docs/webpack.config.development.js
+++ b/docs/webpack.config.development.js
@@ -37,7 +37,7 @@ module.exports = {
loader: 'babel'
}, {
test: /\.(scss|css)$/,
- loader: ExtractTextPlugin.extract('style', 'css?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss!sass?sourceMap!toolbox')
+ loader: ExtractTextPlugin.extract('style', 'css?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss!sass?sourceMap')
}, {
test: /\.(txt)$/,
loader: 'raw',
@@ -49,6 +49,9 @@ module.exports = {
]
},
postcss: [autoprefixer],
+ sassLoader: {
+ data: '@import "' + path.resolve(__dirname, 'app/theme/_theme.scss') + '";'
+ },
plugins: [
new ExtractTextPlugin('docs.css', { allChunks: true }),
new TransferWebpackPlugin([{
diff --git a/docs/webpack.config.production.js b/docs/webpack.config.production.js
index 5f0442c32..1eda47694 100644
--- a/docs/webpack.config.production.js
+++ b/docs/webpack.config.production.js
@@ -33,7 +33,7 @@ module.exports = {
loader: 'babel'
}, {
test: /\.(scss|css)$/,
- loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss!sass!toolbox')
+ loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss!sass')
}, {
test: /\.(txt)$/,
loader: 'raw',
@@ -45,6 +45,9 @@ module.exports = {
]
},
postcss: [autoprefixer],
+ sassLoader: {
+ data: '@import "' + path.resolve(__dirname, 'app/theme/_theme.scss') + '";'
+ },
plugins: [
new ExtractTextPlugin('docs.css', { allChunks: true }),
new webpack.optimize.UglifyJsPlugin({
diff --git a/lib/_colors.scss b/lib/_colors.scss
new file mode 100644
index 000000000..c2eb5099c
--- /dev/null
+++ b/lib/_colors.scss
@@ -0,0 +1,301 @@
+//-- Color definitions taken from Material Design Lite
+
+// Red
+$palette-red-50: rgb(255,235,238);
+$palette-red-100: rgb(255,205,210);
+$palette-red-200: rgb(239,154,154);
+$palette-red-300: rgb(229,115,115);
+$palette-red-400: rgb(239,83,80);
+$palette-red-500: rgb(244,67,54);
+$palette-red-600: rgb(229,57,53);
+$palette-red-700: rgb(211,47,47);
+$palette-red-800: rgb(198,40,40);
+$palette-red-900: rgb(183,28,28);
+$palette-red-a100: rgb(255,138,128);
+$palette-red-a200: rgb(255,82,82);
+$palette-red-a400: rgb(255,23,68);
+$palette-red-a700: rgb(213,0,0);
+
+// Pink
+$palette-pink-50: rgb(252,228,236);
+$palette-pink-100: rgb(248,187,208);
+$palette-pink-200: rgb(244,143,177);
+$palette-pink-300: rgb(240,98,146);
+$palette-pink-400: rgb(236,64,122);
+$palette-pink-500: rgb(233,30,99);
+$palette-pink-600: rgb(216,27,96);
+$palette-pink-700: rgb(194,24,91);
+$palette-pink-800: rgb(173,20,87);
+$palette-pink-900: rgb(136,14,79);
+$palette-pink-a100: rgb(255,128,171);
+$palette-pink-a200: rgb(255,64,129);
+$palette-pink-a400: rgb(245,0,87);
+$palette-pink-a700: rgb(197,17,98);
+
+// Purple
+$palette-purple-50: rgb(243,229,245);
+$palette-purple-100: rgb(225,190,231);
+$palette-purple-200: rgb(206,147,216);
+$palette-purple-300: rgb(186,104,200);
+$palette-purple-400: rgb(171,71,188);
+$palette-purple-500: rgb(156,39,176);
+$palette-purple-600: rgb(142,36,170);
+$palette-purple-700: rgb(123,31,162);
+$palette-purple-800: rgb(106,27,154);
+$palette-purple-900: rgb(74,20,140);
+$palette-purple-a100: rgb(234,128,252);
+$palette-purple-a200: rgb(224,64,251);
+$palette-purple-a400: rgb(213,0,249);
+$palette-purple-a700: rgb(170,0,255);
+
+//Deep Purple
+$palette-deep-purple-50: rgb(237,231,246);
+$palette-deep-purple-100: rgb(209,196,233);
+$palette-deep-purple-200: rgb(179,157,219);
+$palette-deep-purple-300: rgb(149,117,205);
+$palette-deep-purple-400: rgb(126,87,194);
+$palette-deep-purple-500: rgb(103,58,183);
+$palette-deep-purple-600: rgb(94,53,177);
+$palette-deep-purple-700: rgb(81,45,168);
+$palette-deep-purple-800: rgb(69,39,160);
+$palette-deep-purple-900: rgb(49,27,146);
+$palette-deep-purple-a100: rgb(179,136,255);
+$palette-deep-purple-a200: rgb(124,77,255);
+$palette-deep-purple-a400: rgb(101,31,255);
+$palette-deep-purple-a700: rgb(98,0,234);
+
+// Indigo
+$palette-indigo-50: rgb(232,234,246);
+$palette-indigo-100: rgb(197,202,233);
+$palette-indigo-200: rgb(159,168,218);
+$palette-indigo-300: rgb(121,134,203);
+$palette-indigo-400: rgb(92,107,192);
+$palette-indigo-500: rgb(63,81,181);
+$palette-indigo-600: rgb(57,73,171);
+$palette-indigo-700: rgb(48,63,159);
+$palette-indigo-800: rgb(40,53,147);
+$palette-indigo-900: rgb(26,35,126);
+$palette-indigo-a100: rgb(140,158,255);
+$palette-indigo-a200: rgb(83,109,254);
+$palette-indigo-a400: rgb(61,90,254);
+$palette-indigo-a700: rgb(48,79,254);
+
+// Blue
+$palette-blue-50: rgb(227,242,253);
+$palette-blue-100: rgb(187,222,251);
+$palette-blue-200: rgb(144,202,249);
+$palette-blue-300: rgb(100,181,246);
+$palette-blue-400: rgb(66,165,245);
+$palette-blue-500: rgb(33,150,243);
+$palette-blue-600: rgb(30,136,229);
+$palette-blue-700: rgb(25,118,210);
+$palette-blue-800: rgb(21,101,192);
+$palette-blue-900: rgb(13,71,161);
+$palette-blue-a100: rgb(130,177,255);
+$palette-blue-a200: rgb(68,138,255);
+$palette-blue-a400: rgb(41,121,255);
+$palette-blue-a700: rgb(41,98,255);
+
+// Light Blue
+$palette-light-blue-50: rgb(225,245,254);
+$palette-light-blue-100: rgb(179,229,252);
+$palette-light-blue-200: rgb(129,212,250);
+$palette-light-blue-300: rgb(79,195,247);
+$palette-light-blue-400: rgb(41,182,246);
+$palette-light-blue-500: rgb(3,169,244);
+$palette-light-blue-600: rgb(3,155,229);
+$palette-light-blue-700: rgb(2,136,209);
+$palette-light-blue-800: rgb(2,119,189);
+$palette-light-blue-900: rgb(1,87,155);
+$palette-light-blue-a100: rgb(128,216,255);
+$palette-light-blue-a200: rgb(64,196,255);
+$palette-light-blue-a400: rgb(0,176,255);
+$palette-light-blue-a700: rgb(0,145,234);
+
+// Cyan
+$palette-cyan-50: rgb(224,247,250);
+$palette-cyan-100: rgb(178,235,242);
+$palette-cyan-200: rgb(128,222,234);
+$palette-cyan-300: rgb(77,208,225);
+$palette-cyan-400: rgb(38,198,218);
+$palette-cyan-500: rgb(0,188,212);
+$palette-cyan-600: rgb(0,172,193);
+$palette-cyan-700: rgb(0,151,167);
+$palette-cyan-800: rgb(0,131,143);
+$palette-cyan-900: rgb(0,96,100);
+$palette-cyan-a100: rgb(132,255,255);
+$palette-cyan-a200: rgb(24,255,255);
+$palette-cyan-a400: rgb(0,229,255);
+$palette-cyan-a700: rgb(0,184,212);
+
+// Teal
+$palette-teal-50: rgb(224,242,241);
+$palette-teal-100: rgb(178,223,219);
+$palette-teal-200: rgb(128,203,196);
+$palette-teal-300: rgb(77,182,172);
+$palette-teal-400: rgb(38,166,154);
+$palette-teal-500: rgb(0,150,136);
+$palette-teal-600: rgb(0,137,123);
+$palette-teal-700: rgb(0,121,107);
+$palette-teal-800: rgb(0,105,92);
+$palette-teal-900: rgb(0,77,64);
+$palette-teal-a100: rgb(167,255,235);
+$palette-teal-a200: rgb(100,255,218);
+$palette-teal-a400: rgb(29,233,182);
+$palette-teal-a700: rgb(0,191,165);
+
+// Green
+$palette-green-50: rgb(232,245,233);
+$palette-green-100: rgb(200,230,201);
+$palette-green-200: rgb(165,214,167);
+$palette-green-300: rgb(129,199,132);
+$palette-green-400: rgb(102,187,106);
+$palette-green-500: rgb(76,175,80);
+$palette-green-600: rgb(67,160,71);
+$palette-green-700: rgb(56,142,60);
+$palette-green-800: rgb(46,125,50);
+$palette-green-900: rgb(27,94,32);
+$palette-green-a100: rgb(185,246,202);
+$palette-green-a200: rgb(105,240,174);
+$palette-green-a400: rgb(0,230,118);
+$palette-green-a700: rgb(0,200,83);
+
+// Green
+$palette-light-green-50: rgb(241,248,233);
+$palette-light-green-100: rgb(220,237,200);
+$palette-light-green-200: rgb(197,225,165);
+$palette-light-green-300: rgb(174,213,129);
+$palette-light-green-400: rgb(156,204,101);
+$palette-light-green-500: rgb(139,195,74);
+$palette-light-green-600: rgb(124,179,66);
+$palette-light-green-700: rgb(104,159,56);
+$palette-light-green-800: rgb(85,139,47);
+$palette-light-green-900: rgb(51,105,30);
+$palette-light-green-a100: rgb(204,255,144);
+$palette-light-green-a200: rgb(178,255,89);
+$palette-light-green-a400: rgb(118,255,3);
+$palette-light-green-a700: rgb(100,221,23);
+
+// Lime
+$palette-lime-50: rgb(249,251,231);
+$palette-lime-100: rgb(240,244,195);
+$palette-lime-200: rgb(230,238,156);
+$palette-lime-300: rgb(220,231,117);
+$palette-lime-400: rgb(212,225,87);
+$palette-lime-500: rgb(205,220,57);
+$palette-lime-600: rgb(192,202,51);
+$palette-lime-700: rgb(175,180,43);
+$palette-lime-800: rgb(158,157,36);
+$palette-lime-900: rgb(130,119,23);
+$palette-lime-a100: rgb(244,255,129);
+$palette-lime-a200: rgb(238,255,65);
+$palette-lime-a400: rgb(198,255,0);
+$palette-lime-a700: rgb(174,234,0);
+
+// Yellow
+$palette-yellow-50: rgb(255,253,231);
+$palette-yellow-100: rgb(255,249,196);
+$palette-yellow-200: rgb(255,245,157);
+$palette-yellow-300: rgb(255,241,118);
+$palette-yellow-400: rgb(255,238,88);
+$palette-yellow-500: rgb(255,235,59);
+$palette-yellow-600: rgb(253,216,53);
+$palette-yellow-700: rgb(251,192,45);
+$palette-yellow-800: rgb(249,168,37);
+$palette-yellow-900: rgb(245,127,23);
+$palette-yellow-a100: rgb(255,255,141);
+$palette-yellow-a200: rgb(255,255,0);
+$palette-yellow-a400: rgb(255,234,0);
+$palette-yellow-a700: rgb(255,214,0);
+
+// Amber
+$palette-amber-50: rgb(255,248,225);
+$palette-amber-100: rgb(255,236,179);
+$palette-amber-200: rgb(255,224,130);
+$palette-amber-300: rgb(255,213,79);
+$palette-amber-400: rgb(255,202,40);
+$palette-amber-500: rgb(255,193,7);
+$palette-amber-600: rgb(255,179,0);
+$palette-amber-700: rgb(255,160,0);
+$palette-amber-800: rgb(255,143,0);
+$palette-amber-900: rgb(255,111,0);
+$palette-amber-a100: rgb(255,229,127);
+$palette-amber-a200: rgb(255,215,64);
+$palette-amber-a400: rgb(255,196,0);
+$palette-amber-a700: rgb(255,171,0);
+
+// Orange
+$palette-orange-50: rgb(255,243,224);
+$palette-orange-100: rgb(255,224,178);
+$palette-orange-200: rgb(255,204,128);
+$palette-orange-300: rgb(255,183,77);
+$palette-orange-400: rgb(255,167,38);
+$palette-orange-500: rgb(255,152,0);
+$palette-orange-600: rgb(251,140,0);
+$palette-orange-700: rgb(245,124,0);
+$palette-orange-800: rgb(239,108,0);
+$palette-orange-900: rgb(230,81,0);
+$palette-orange-a100: rgb(255,209,128);
+$palette-orange-a200: rgb(255,171,64);
+$palette-orange-a400: rgb(255,145,0);
+$palette-orange-a700: rgb(255,109,0);
+
+// Deep Orange
+$palette-deep-orange-50: rgb(251,233,231);
+$palette-deep-orange-100: rgb(255,204,188);
+$palette-deep-orange-200: rgb(255,171,145);
+$palette-deep-orange-300: rgb(255,138,101);
+$palette-deep-orange-400: rgb(255,112,67);
+$palette-deep-orange-500: rgb(255,87,34);
+$palette-deep-orange-600: rgb(244,81,30);
+$palette-deep-orange-700: rgb(230,74,25);
+$palette-deep-orange-800: rgb(216,67,21);
+$palette-deep-orange-900: rgb(191,54,12);
+$palette-deep-orange-a100: rgb(255,158,128);
+$palette-deep-orange-a200: rgb(255,110,64);
+$palette-deep-orange-a400: rgb(255,61,0);
+$palette-deep-orange-a700: rgb(221,44,0);
+
+// Brown
+$palette-brown-50: rgb(239,235,233);
+$palette-brown-100: rgb(215,204,200);
+$palette-brown-200: rgb(188,170,164);
+$palette-brown-300: rgb(161,136,127);
+$palette-brown-400: rgb(141,110,99);
+$palette-brown-500: rgb(121,85,72);
+$palette-brown-600: rgb(109,76,65);
+$palette-brown-700: rgb(93,64,55);
+$palette-brown-800: rgb(78,52,46);
+$palette-brown-900: rgb(62,39,35);
+
+// Grey
+$palette-grey-50: rgb(250,250,250);
+$palette-grey-100: rgb(245,245,245);
+$palette-grey-200: rgb(238,238,238);
+$palette-grey-300: rgb(224,224,224);
+$palette-grey-400: rgb(189,189,189);
+$palette-grey-500: rgb(158,158,158);
+$palette-grey-600: rgb(117,117,117);
+$palette-grey-700: rgb(97,97,97);
+$palette-grey-800: rgb(66,66,66);
+$palette-grey-900: rgb(33,33,33);
+
+// Blue Grey
+$palette-blue-grey-50: rgb(236,239,241);
+$palette-blue-grey-100: rgb(207,216,220);
+$palette-blue-grey-200: rgb(176,190,197);
+$palette-blue-grey-300: rgb(144,164,174);
+$palette-blue-grey-400: rgb(120,144,156);
+$palette-blue-grey-500: rgb(96,125,139);
+$palette-blue-grey-600: rgb(84,110,122);
+$palette-blue-grey-700: rgb(69,90,100);
+$palette-blue-grey-800: rgb(55,71,79);
+$palette-blue-grey-900: rgb(38,50,56);
+
+$color-black: rgb(0,0,0);
+$color-white: rgb(255,255,255);
+
+//-- The two possible colors for overlayed text.
+$styleguide-generate-template: false !default;
+$color-dark-contrast: $color-white !default;
+$color-light-contrast: $color-black !default;
diff --git a/lib/_globals.scss b/lib/_globals.scss
new file mode 100644
index 000000000..191881082
--- /dev/null
+++ b/lib/_globals.scss
@@ -0,0 +1,81 @@
+//-- Color configuration
+$color-divider: $palette-grey-200 !default;
+$color-background: $color-white !default;
+$color-text: $palette-grey-900 !default;
+$color-text-secondary: $palette-grey-600 !default;
+
+$color-primary: $palette-indigo-500 !default;
+$color-primary-dark: $palette-indigo-700 !default;
+$color-accent: $palette-pink-a200 !default;
+$color-accent-dark: $palette-pink-700 !default;
+$color-primary-contrast: $color-dark-contrast !default;
+$color-accent-contrast: $color-dark-contrast !default;
+
+//-- Sizing
+$unit: 1rem !default;
+
+// -- Fonts
+$preferred-font: "Roboto", "Helvetica", "Arial", sans-serif !default;
+$font-size: 1.6 * $unit !default;
+$font-size-tiny: 1.2 * $unit !default;
+$font-size-small: 1.4 * $unit !default;
+$font-size-normal: $font-size !default;
+$font-size-big: 1.8 * $unit !default;
+$font-weight-thin: 300 !default;
+$font-weight-normal: 400 !default;
+$font-weight-semi-bold: 500 !default;
+$font-weight-bold: 700 !default;
+
+//-- Shadows
+$shadow-key-umbra-opacity: 0.2 !default;
+$shadow-key-penumbra-opacity: 0.14 !default;
+$shadow-ambient-shadow-opacity: 0.12 !default;
+
+//-- Depth Shadows
+$zdepth-shadow-1: 0 1px 6px rgba(0,0,0,.12), 0 1px 4px rgba(0,0,0,.24);
+$zdepth-shadow-2: 0 3px 10px rgba(0,0,0,.16), 0 3px 10px rgba(0,0,0,.23);
+$zdepth-shadow-3: 0 10px 30px rgba(0,0,0,.19), 0 6px 10px rgba(0,0,0,.23);
+$zdepth-shadow-4: 0 14px 45px rgba(0,0,0,.25), 0 10px 18px rgba(0,0,0,.22);
+$zdepth-shadow-5: 0 19px 60px rgba(0,0,0,.3), 0 15px 20px rgba(0,0,0,.22);
+
+//-- Animation
+$animation-duration: .35s;
+$animation-delay: $animation-duration / 5;
+$animation-curve-fast-out-slow-in: cubic-bezier(0.4, 0, 0.2, 1) !default;
+$animation-curve-linear-out-slow-in: cubic-bezier(0, 0, 0.2, 1) !default;
+$animation-curve-fast-out-linear-in: cubic-bezier(0.4, 0, 1, 1) !default;
+$animation-curve-default: $animation-curve-fast-out-slow-in !default;
+
+//-- Indexes
+$z-index-highest: 300 !default;
+$z-index-higher: 200 !default;
+$z-index-high: 100 !default;
+$z-index-normal: 1 !default;
+$z-index-low: -100 !default;
+$z-index-lower: -200 !default;
+
+//-- Breakpoints
+// height of app bar
+// https://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-keylines-spacing
+$standard-increment-mobile: (5.6 * $unit) !default;
+$standard-increment-desktop: (6.4 * $unit) !default;
+
+// https://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-baseline-grids
+$baseline-grid: (0.8 * $unit) !default;
+$layout-gutter-width-sm: ($baseline-grid * 2) !default;
+$layout-gutter-width: ($baseline-grid * 3) !default;
+
+// https://www.google.com/design/spec/layout/responsive-ui.html#responsive-ui-breakpoints
+// 4 columns
+$layout-breakpoint-xxs: 480px !default;
+// 8 columns
+$layout-breakpoint-xs: 600px !default;
+// 12 columns
+$layout-breakpoint-sm-tablet: 720px !default;
+$layout-breakpoint-sm: 840px !default;
+$layout-breakpoint-md: 960px !default;
+$layout-breakpoint-lg-tablet: 1024px !default;
+$layout-breakpoint-lg: 1280px !default;
+$layout-breakpoint-xl: 1440px !default;
+$layout-breakpoint-xxl: 1600px !default;
+$layout-breakpoint-xxxl: 1920px !default;
diff --git a/lib/_mixins.scss b/lib/_mixins.scss
new file mode 100644
index 000000000..f6159734b
--- /dev/null
+++ b/lib/_mixins.scss
@@ -0,0 +1,260 @@
+// scss-lint:disable VendorPrefix
+@mixin typo-preferred-font($use-preferred: true) {
+ @if $use-preferred {
+ font-family: $preferred-font;
+ }
+}
+
+@mixin typo-display-4($color-contrast: false, $use-preferred: true) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 11.2rem;
+ font-weight: 300;
+ line-height: 1;
+ letter-spacing: -.04em;
+
+ @if $color-contrast {
+ opacity: .54;
+ }
+}
+
+@mixin typo-display-3($color-contrast: false, $use-preferred: true) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 5.6rem;
+ font-weight: 400;
+ line-height: 1.35;
+ letter-spacing: -.02em;
+
+ @if $color-contrast {
+ opacity: .54;
+ }
+}
+
+@mixin typo-display-2($color-contrast: false, $use-preferred: true) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 4.5rem;
+ font-weight: 400;
+ line-height: 4.8rem;
+
+ @if $color-contrast {
+ opacity: .54;
+ }
+}
+
+@mixin typo-display-1($color-contrast: false, $use-preferred: true) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 3.4rem;
+ font-weight: 400;
+ line-height: 4rem;
+
+ @if $color-contrast {
+ opacity: .54;
+ }
+}
+
+@mixin typo-headline($color-contrast: false, $use-preferred: true) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 2.4rem;
+ font-weight: 400;
+ line-height: 3.2rem;
+ -moz-osx-font-smoothing: grayscale;
+
+ @if $color-contrast {
+ opacity: .87;
+ }
+}
+
+@mixin typo-title($color-contrast: false, $use-preferred: true) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 2rem;
+ font-weight: 500;
+ line-height: 1;
+ letter-spacing: .02em;
+
+ @if $color-contrast {
+ opacity: .87;
+ }
+}
+
+@mixin typo-subhead($color-contrast: false, $use-preferred: true) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 1.6rem;
+ font-weight: 400;
+ line-height: 2.4rem;
+ letter-spacing: .04em;
+
+ @if $color-contrast {
+ opacity: .87;
+ }
+}
+
+@mixin typo-subhead-2($color-contrast: false, $use-preferred: true) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 1.6rem;
+ font-weight: 400;
+ line-height: 2.8rem;
+ letter-spacing: .04em;
+
+ @if $color-contrast {
+ opacity: .87;
+ }
+}
+
+@mixin typo-body-2($color-contrast: false, $use-preferred: false) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 1.4rem;
+ line-height: 2.4rem;
+ letter-spacing: 0;
+
+ @if $use-preferred {
+ font-weight: 500;
+ } @else {
+ font-weight: bold;
+ }
+
+ @if $color-contrast {
+ opacity: .87;
+ }
+}
+
+@mixin typo-body-1($color-contrast: false, $use-preferred: false) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 1.4rem;
+ font-weight: 400;
+ line-height: 2.4rem;
+ letter-spacing: 0;
+
+ @if $color-contrast {
+ opacity: .87;
+ }
+}
+
+@mixin typo-caption($color-contrast: false, $use-preferred: false) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 1.2rem;
+ font-weight: 400;
+ line-height: 1;
+ letter-spacing: 0;
+
+ @if $color-contrast {
+ opacity: .54;
+ }
+}
+
+@mixin typo-blockquote($color-contrast: false, $use-preferred: true) {
+ @include typo-preferred-font($use-preferred);
+ position: relative;
+ font-size: 2.4rem;
+ font-style: italic;
+ font-weight: 300;
+ line-height: 1.35;
+ letter-spacing: .08em;
+
+ &:before {
+ position: absolute;
+ left: -.5em;
+ content: "“";
+ }
+
+ &:after {
+ margin-left: -.05em;
+ content: "”";
+ }
+
+ @if $color-contrast {
+ opacity: .54;
+ }
+}
+
+@mixin typo-menu($color-contrast: false, $use-preferred: true) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 1.4rem;
+ font-weight: 500;
+ line-height: 1;
+ letter-spacing: 0;
+
+ @if $color-contrast {
+ opacity: .87;
+ }
+}
+
+@mixin typo-button($color-contrast: false, $use-preferred: true) {
+ @include typo-preferred-font($use-preferred);
+ font-size: 1.4rem;
+ font-weight: 500;
+ line-height: 1;
+ text-transform: uppercase;
+ letter-spacing: 0;
+
+ @if $color-contrast {
+ opacity: .87;
+ }
+}
+
+//-- Shadows
+@mixin focus-shadow() {
+ box-shadow: 0 0 8px rgba(0, 0, 0, .18), 0 8px 16px rgba(0, 0, 0, .36);
+}
+
+@mixin shadow-2dp() {
+ box-shadow: 0 2px 2px 0 rgba(0, 0, 0, $shadow-key-penumbra-opacity),
+ 0 3px 1px -2px rgba(0, 0, 0, $shadow-key-umbra-opacity),
+ 0 1px 5px 0 rgba(0, 0, 0, $shadow-ambient-shadow-opacity);
+}
+
+@mixin shadow-3dp() {
+ box-shadow: 0 3px 4px 0 rgba(0, 0, 0, $shadow-key-penumbra-opacity),
+ 0 3px 3px -2px rgba(0, 0, 0, $shadow-key-umbra-opacity),
+ 0 1px 8px 0 rgba(0, 0, 0, $shadow-ambient-shadow-opacity);
+}
+
+@mixin shadow-4dp() {
+ box-shadow: 0 4px 5px 0 rgba(0, 0, 0, $shadow-key-penumbra-opacity),
+ 0 1px 10px 0 rgba(0, 0, 0, $shadow-ambient-shadow-opacity),
+ 0 2px 4px -1px rgba(0, 0, 0, $shadow-key-umbra-opacity);
+}
+
+@mixin shadow-6dp() {
+ box-shadow: 0 6px 10px 0 rgba(0, 0, 0, $shadow-key-penumbra-opacity),
+ 0 1px 18px 0 rgba(0, 0, 0, $shadow-ambient-shadow-opacity),
+ 0 3px 5px -1px rgba(0, 0, 0, $shadow-key-umbra-opacity);
+}
+
+@mixin shadow-8dp() {
+ box-shadow: 0 8px 10px 1px rgba(0, 0, 0, $shadow-key-penumbra-opacity),
+ 0 3px 14px 2px rgba(0, 0, 0, $shadow-ambient-shadow-opacity),
+ 0 5px 5px -3px rgba(0, 0, 0, $shadow-key-umbra-opacity);
+}
+
+@mixin shadow-16dp() {
+ box-shadow: 0 16px 24px 2px rgba(0, 0, 0, $shadow-key-penumbra-opacity),
+ 0 6px 30px 5px rgba(0, 0, 0, $shadow-ambient-shadow-opacity),
+ 0 8px 10px -5px rgba(0, 0, 0, $shadow-key-umbra-opacity);
+}
+
+//-- Animations
+@mixin material-animation-fast-out-slow-in($duration: .2s) {
+ transition-timing-function: $animation-curve-fast-out-slow-in;
+ transition-duration: $duration;
+}
+
+@mixin material-animation-linear-out-slow-in($duration: .2s) {
+ transition-timing-function: $animation-curve-linear-out-slow-in;
+ transition-duration: $duration;
+}
+
+@mixin material-animation-fast-out-linear-in($duration: .2s) {
+ transition-timing-function: $animation-curve-fast-out-linear-in;
+ transition-duration: $duration;
+}
+
+@mixin material-animation-default($duration: .2s) {
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $duration;
+}
+
+@mixin no-webkit-scrollbar {
+ &::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+ }
+}
diff --git a/lib/animations/index.js b/lib/animations/index.js
new file mode 100644
index 000000000..787971bcc
--- /dev/null
+++ b/lib/animations/index.js
@@ -0,0 +1,29 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ZoomOut = exports.ZoomIn = exports.SlideRight = exports.SlideLeft = undefined;
+
+var _slideLeft = require('./slide-left.scss');
+
+var _slideLeft2 = _interopRequireDefault(_slideLeft);
+
+var _slideRight = require('./slide-right.scss');
+
+var _slideRight2 = _interopRequireDefault(_slideRight);
+
+var _zoomIn = require('./zoom-in.scss');
+
+var _zoomIn2 = _interopRequireDefault(_zoomIn);
+
+var _zoomOut = require('./zoom-out.scss');
+
+var _zoomOut2 = _interopRequireDefault(_zoomOut);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+exports.SlideLeft = _slideLeft2.default;
+exports.SlideRight = _slideRight2.default;
+exports.ZoomIn = _zoomIn2.default;
+exports.ZoomOut = _zoomOut2.default;
\ No newline at end of file
diff --git a/lib/animations/slide-left.scss b/lib/animations/slide-left.scss
new file mode 100644
index 000000000..aba039958
--- /dev/null
+++ b/lib/animations/slide-left.scss
@@ -0,0 +1,26 @@
+.enter, .leave {
+ position: absolute;
+ transition-timing-function: ease-in-out;
+ transition-duration: .35s;
+ transition-property: transform, opacity;
+}
+
+.enter {
+ opacity: 0;
+ transform: translate3d(-100%, 0, 0);
+
+ &.enterActive {
+ opacity: 1;
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+.leave {
+ opacity: 1;
+ transform: translate3d(0, 0, 0);
+
+ &.leaveActive {
+ opacity: 0;
+ transform: translate3d(100%, 0, 0);
+ }
+}
diff --git a/lib/animations/slide-right.scss b/lib/animations/slide-right.scss
new file mode 100644
index 000000000..95d0e15d9
--- /dev/null
+++ b/lib/animations/slide-right.scss
@@ -0,0 +1,29 @@
+.enter, .leave {
+ position: absolute;
+}
+
+.enterActive, .leaveActive {
+ transition-timing-function: ease-in-out;
+ transition-duration: 350ms;
+ transition-property: transform, opacity;
+}
+
+.enter {
+ opacity: 0;
+ transform: translateX(100%);
+
+ &.enterActive {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+.leave {
+ opacity: 1;
+ transform: translateX(0);
+
+ &.leaveActive {
+ opacity: 0;
+ transform: translateX(-100%);
+ }
+}
diff --git a/lib/animations/zoom-in.scss b/lib/animations/zoom-in.scss
new file mode 100644
index 000000000..d752e9401
--- /dev/null
+++ b/lib/animations/zoom-in.scss
@@ -0,0 +1,33 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+
+.enter, .leave {
+ position: absolute;
+}
+
+.enterActive, .leaveActive {
+ transition: transform, opacity;
+ transition-timing-function: $animation-curve-fast-out-slow-in;
+ transition-duration: 500ms;
+}
+
+.enter {
+ opacity: 0;
+ transform: scale(0.85);
+
+ &.enterActive {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+.leave {
+ opacity: 1;
+ transform: scale(1);
+
+ &.leaveActive {
+ opacity: 0;
+ transform: scale(1.25);
+ }
+}
diff --git a/lib/animations/zoom-out.scss b/lib/animations/zoom-out.scss
new file mode 100644
index 000000000..cf1500fc1
--- /dev/null
+++ b/lib/animations/zoom-out.scss
@@ -0,0 +1,33 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+
+.enter, .leave {
+ position: absolute;
+}
+
+.enterActive, .leaveActive {
+ transition: transform, opacity;
+ transition-timing-function: $animation-curve-fast-out-slow-in;
+ transition-duration: 500ms;
+}
+
+.enter {
+ opacity: 0;
+ transform: scale(1.25);
+
+ &.enterActive {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+.leave {
+ opacity: 1;
+ transform: scale(1);
+
+ &.leaveActive {
+ opacity: 0;
+ transform: scale(0.85);
+ }
+}
diff --git a/lib/app_bar/AppBar.js b/lib/app_bar/AppBar.js
new file mode 100644
index 000000000..ebf43b0f7
--- /dev/null
+++ b/lib/app_bar/AppBar.js
@@ -0,0 +1,61 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.AppBar = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var AppBar = function AppBar(_ref) {
+ var _classnames;
+
+ var theme = _ref.theme;
+
+ var props = _objectWithoutProperties(_ref, ['theme']);
+
+ var className = (0, _classnames3.default)(theme.appBar, (_classnames = {}, _defineProperty(_classnames, theme.fixed, props.fixed), _defineProperty(_classnames, theme.flat, props.flat), _classnames), props.className);
+
+ return _react2.default.createElement(
+ 'header',
+ { className: className, 'data-react-toolbox': 'app-bar' },
+ props.children
+ );
+};
+
+AppBar.propTypes = {
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ fixed: _react.PropTypes.bool,
+ flat: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ appBar: _react.PropTypes.string,
+ fixed: _react.PropTypes.string,
+ flat: _react.PropTypes.string
+ })
+};
+
+AppBar.defaultProps = {
+ className: '',
+ fixed: false,
+ flat: false
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.APP_BAR)(AppBar);
+exports.AppBar = AppBar;
\ No newline at end of file
diff --git a/lib/app_bar/_config.scss b/lib/app_bar/_config.scss
new file mode 100644
index 000000000..5c76b6189
--- /dev/null
+++ b/lib/app_bar/_config.scss
@@ -0,0 +1,8 @@
+$appbar-color: $color-primary-dark !default;
+$appbar-contrast: $color-primary-contrast !default;
+$appbar-title-total-distance: 8 * $unit !default;
+$appbar-height: 6.4 * $unit !default;
+$appbar-height-m-portrait: 5.6 * $unit !default;
+$appbar-height-m-landscape: 4.8 * $unit !default;
+$appbar-h-padding: 2.4 * $unit !default;
+$appbar-title-distance: $appbar-title-total-distance - $appbar-h-padding !default;
diff --git a/lib/app_bar/index.js b/lib/app_bar/index.js
new file mode 100644
index 000000000..8fa609cc6
--- /dev/null
+++ b/lib/app_bar/index.js
@@ -0,0 +1,23 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.AppBar = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _AppBar = require('./AppBar.js');
+
+var _identifiers = require('../identifiers.js');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ThemedAppBar = (0, _reactCssThemr.themr)(_identifiers.APP_BAR, _theme2.default)(_AppBar.AppBar);
+
+exports.default = ThemedAppBar;
+exports.AppBar = ThemedAppBar;
\ No newline at end of file
diff --git a/lib/app_bar/theme.scss b/lib/app_bar/theme.scss
new file mode 100644
index 000000000..56147f646
--- /dev/null
+++ b/lib/app_bar/theme.scss
@@ -0,0 +1,38 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.appBar {
+ display: flex;
+ height: $appbar-height;
+ align-items: center;
+ padding: 0 $appbar-h-padding;
+ color: $appbar-contrast;
+ background: $appbar-color;
+
+ @media screen and (max-width: $layout-breakpoint-xxs) and (orientation: portrait) {
+ height: $appbar-height-m-portrait;
+ }
+
+ @media screen and (max-width: $layout-breakpoint-xs) and (orientation: landscape) {
+ height: $appbar-height-m-landscape;
+ }
+
+ &:not(.flat) {
+ z-index: $z-index-high;
+ box-shadow: 0 2px 5px rgba(0,0,0,.26);
+ }
+
+ &.fixed {
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: $z-index-highest;
+ }
+
+ a {
+ color: $appbar-contrast;
+ }
+}
diff --git a/lib/autocomplete/Autocomplete.js b/lib/autocomplete/Autocomplete.js
new file mode 100644
index 000000000..91a25484a
--- /dev/null
+++ b/lib/autocomplete/Autocomplete.js
@@ -0,0 +1,442 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Autocomplete = exports.autocompleteFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactDom = require('react-dom');
+
+var _reactDom2 = _interopRequireDefault(_reactDom);
+
+var _classnames4 = require('classnames');
+
+var _classnames5 = _interopRequireDefault(_classnames4);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Chip = require('../chip/Chip.js');
+
+var _Chip2 = _interopRequireDefault(_Chip);
+
+var _Input = require('../input/Input.js');
+
+var _Input2 = _interopRequireDefault(_Input);
+
+var _events = require('../utils/events.js');
+
+var _events2 = _interopRequireDefault(_events);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var POSITION = {
+ AUTO: 'auto',
+ DOWN: 'down',
+ UP: 'up'
+};
+
+var factory = function factory(Chip, Input) {
+ var Autocomplete = function (_Component) {
+ _inherits(Autocomplete, _Component);
+
+ function Autocomplete() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Autocomplete);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Autocomplete)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ direction: _this.props.direction,
+ focus: false,
+ showAllSuggestions: _this.props.showSuggestionsWhenValueIsSet,
+ query: _this.query(_this.props.value)
+ }, _this.handleChange = function (keys, event) {
+ var key = _this.props.multiple ? keys : keys[0];
+ var query = _this.query(key);
+ if (_this.props.onChange) _this.props.onChange(key, event);
+ _this.setState({ focus: false, query: query, showAllSuggestions: _this.props.showSuggestionsWhenValueIsSet }, function () {
+ _reactDom2.default.findDOMNode(_this).querySelector('input').blur();
+ });
+ }, _this.handleQueryBlur = function () {
+ if (_this.state.focus) _this.setState({ focus: false });
+ }, _this.handleQueryChange = function (value) {
+ _this.setState({ query: value, showAllSuggestions: false });
+ }, _this.handleQueryFocus = function () {
+ _this.refs.suggestions.scrollTop = 0;
+ _this.setState({ active: '', focus: true });
+ }, _this.handleQueryKeyDown = function (event) {
+ // Clear query when pressing backspace and showing all suggestions.
+ var shouldClearQuery = event.which === 8 && _this.props.showSuggestionsWhenValueIsSet && _this.state.showAllSuggestions;
+ if (shouldClearQuery) {
+ _this.setState({ query: '' });
+ }
+ }, _this.handleQueryKeyUp = function (event) {
+ if (event.which === 13 && _this.state.active) _this.select(_this.state.active, event);
+ if (event.which === 27) _reactDom2.default.findDOMNode(_this).querySelector('input').blur();
+ if ([40, 38].indexOf(event.which) !== -1) {
+ var suggestionsKeys = [].concat(_toConsumableArray(_this.suggestions().keys()));
+ var index = suggestionsKeys.indexOf(_this.state.active) + (event.which === 40 ? +1 : -1);
+ if (index < 0) index = suggestionsKeys.length - 1;
+ if (index >= suggestionsKeys.length) index = 0;
+ _this.setState({ active: suggestionsKeys[index] });
+ }
+ }, _this.handleSuggestionHover = function (key) {
+ _this.setState({ active: key });
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Autocomplete, [{
+ key: 'componentWillReceiveProps',
+ value: function componentWillReceiveProps(nextProps) {
+ if (!this.props.multiple) {
+ this.setState({
+ query: this.query(nextProps.value)
+ });
+ }
+ }
+ }, {
+ key: 'shouldComponentUpdate',
+ value: function shouldComponentUpdate(nextProps, nextState) {
+ if (!this.state.focus && nextState.focus && this.props.direction === POSITION.AUTO) {
+ var direction = this.calculateDirection();
+ if (this.state.direction !== direction) {
+ this.setState({ direction: direction });
+ return false;
+ }
+ }
+ return true;
+ }
+ }, {
+ key: 'calculateDirection',
+ value: function calculateDirection() {
+ if (this.props.direction === 'auto') {
+ var client = _reactDom2.default.findDOMNode(this.refs.input).getBoundingClientRect();
+ var screen_height = window.innerHeight || document.documentElement.offsetHeight;
+ var up = client.top > screen_height / 2 + client.height;
+ return up ? 'up' : 'down';
+ } else {
+ return this.props.direction;
+ }
+ }
+ }, {
+ key: 'query',
+ value: function query(key) {
+ return !this.props.multiple && key ? this.source().get(key) : '';
+ }
+ }, {
+ key: 'suggestions',
+ value: function suggestions() {
+ var suggest = new Map();
+ var query = this.state.query.toLowerCase().trim() || '';
+ var values = this.values();
+ var source = this.source();
+
+ // Suggest any non-set value which matches the query
+ if (this.props.multiple) {
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
+ try {
+ for (var _iterator = source[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var _step$value = _slicedToArray(_step.value, 2);
+
+ var key = _step$value[0];
+ var value = _step$value[1];
+
+ if (!values.has(key) && value.toLowerCase().trim().startsWith(query)) {
+ suggest.set(key, value);
+ }
+ }
+
+ // When multiple is false, suggest any value which matches the query if showAllSuggestions is false
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ } else if (query && !this.state.showAllSuggestions) {
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+
+ try {
+ for (var _iterator2 = source[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var _step2$value = _slicedToArray(_step2.value, 2);
+
+ var key = _step2$value[0];
+ var value = _step2$value[1];
+
+ if (value.toLowerCase().trim().startsWith(query)) {
+ suggest.set(key, value);
+ }
+ }
+
+ // When multiple is false, suggest all values when showAllSuggestions is true
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
+ } else {
+ suggest = source;
+ }
+
+ return suggest;
+ }
+ }, {
+ key: 'source',
+ value: function source() {
+ var src = this.props.source;
+
+ if (src.hasOwnProperty('length')) {
+ return new Map(src.map(function (item) {
+ return Array.isArray(item) ? [].concat(_toConsumableArray(item)) : [item, item];
+ }));
+ } else {
+ return new Map(Object.keys(src).map(function (key) {
+ return [key, src[key]];
+ }));
+ }
+ }
+ }, {
+ key: 'values',
+ value: function values() {
+ var valueMap = new Map();
+ var vals = this.props.multiple ? this.props.value : [this.props.value];
+ var _iteratorNormalCompletion3 = true;
+ var _didIteratorError3 = false;
+ var _iteratorError3 = undefined;
+
+ try {
+ for (var _iterator3 = this.source()[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var _step3$value = _slicedToArray(_step3.value, 2);
+
+ var k = _step3$value[0];
+ var v = _step3$value[1];
+
+ if (vals.indexOf(k) !== -1) valueMap.set(k, v);
+ }
+ } catch (err) {
+ _didIteratorError3 = true;
+ _iteratorError3 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion3 && _iterator3.return) {
+ _iterator3.return();
+ }
+ } finally {
+ if (_didIteratorError3) {
+ throw _iteratorError3;
+ }
+ }
+ }
+
+ return valueMap;
+ }
+ }, {
+ key: 'select',
+ value: function select(key, event) {
+ _events2.default.pauseEvent(event);
+ var values = this.values(this.props.value);
+ this.handleChange([key].concat(_toConsumableArray(values.keys())), event);
+ }
+ }, {
+ key: 'unselect',
+ value: function unselect(key, event) {
+ if (!this.props.disabled) {
+ var values = this.values(this.props.value);
+ values.delete(key);
+ this.handleChange([].concat(_toConsumableArray(values.keys())), event);
+ }
+ }
+ }, {
+ key: 'renderSelected',
+ value: function renderSelected() {
+ var _this2 = this;
+
+ if (this.props.multiple) {
+ var selectedItems = [].concat(_toConsumableArray(this.values())).map(function (_ref) {
+ var _ref2 = _slicedToArray(_ref, 2);
+
+ var key = _ref2[0];
+ var value = _ref2[1];
+
+ return _react2.default.createElement(
+ Chip,
+ {
+ key: key,
+ className: _this2.props.theme.value,
+ deletable: true,
+ onDeleteClick: _this2.unselect.bind(_this2, key)
+ },
+ value
+ );
+ });
+
+ return _react2.default.createElement(
+ 'ul',
+ { className: this.props.theme.values },
+ selectedItems
+ );
+ }
+ }
+ }, {
+ key: 'renderSuggestions',
+ value: function renderSuggestions() {
+ var _this3 = this;
+
+ var theme = this.props.theme;
+
+ var suggestions = [].concat(_toConsumableArray(this.suggestions())).map(function (_ref3) {
+ var _ref4 = _slicedToArray(_ref3, 2);
+
+ var key = _ref4[0];
+ var value = _ref4[1];
+
+ var className = (0, _classnames5.default)(theme.suggestion, _defineProperty({}, theme.active, _this3.state.active === key));
+ return _react2.default.createElement(
+ 'li',
+ {
+ key: key,
+ className: className,
+ onMouseDown: _this3.select.bind(_this3, key),
+ onMouseOver: _this3.handleSuggestionHover.bind(_this3, key)
+ },
+ value
+ );
+ });
+
+ var className = (0, _classnames5.default)(theme.suggestions, _defineProperty({}, theme.up, this.state.direction === 'up'));
+ return _react2.default.createElement(
+ 'ul',
+ { ref: 'suggestions', className: className },
+ suggestions
+ );
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var error = _props.error;
+ var label = _props.label;
+ var theme = _props.theme;
+
+ var other = _objectWithoutProperties(_props, ['error', 'label', 'theme']);
+
+ var className = (0, _classnames5.default)(theme.autocomplete, _defineProperty({}, theme.focus, this.state.focus), this.props.className);
+
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'autocomplete', className: className },
+ this.props.selectedPosition === 'above' ? this.renderSelected() : null,
+ _react2.default.createElement(Input, _extends({}, other, {
+ ref: 'input',
+ className: theme.input,
+ error: error,
+ label: label,
+ onBlur: this.handleQueryBlur,
+ onChange: this.handleQueryChange,
+ onFocus: this.handleQueryFocus,
+ onKeyDown: this.handleQueryKeyDown,
+ onKeyUp: this.handleQueryKeyUp,
+ value: this.state.query
+ })),
+ this.renderSuggestions(),
+ this.props.selectedPosition === 'below' ? this.renderSelected() : null
+ );
+ }
+ }]);
+
+ return Autocomplete;
+ }(_react.Component);
+
+ Autocomplete.propTypes = {
+ className: _react.PropTypes.string,
+ direction: _react.PropTypes.oneOf(['auto', 'up', 'down']),
+ disabled: _react.PropTypes.bool,
+ error: _react.PropTypes.string,
+ label: _react.PropTypes.string,
+ multiple: _react.PropTypes.bool,
+ onChange: _react.PropTypes.func,
+ selectedPosition: _react.PropTypes.oneOf(['above', 'below']),
+ showSuggestionsWhenValueIsSet: _react.PropTypes.bool,
+ source: _react.PropTypes.any,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ autocomplete: _react.PropTypes.string,
+ focus: _react.PropTypes.string,
+ input: _react.PropTypes.string,
+ label: _react.PropTypes.string,
+ suggestion: _react.PropTypes.string,
+ suggestions: _react.PropTypes.string,
+ up: _react.PropTypes.string,
+ value: _react.PropTypes.string,
+ values: _react.PropTypes.string
+ }),
+ value: _react.PropTypes.any
+ };
+ Autocomplete.defaultProps = {
+ className: '',
+ direction: 'auto',
+ selectedPosition: 'above',
+ multiple: true,
+ showSuggestionsWhenValueIsSet: false,
+ source: {}
+ };
+
+
+ return Autocomplete;
+};
+
+var Autocomplete = factory(_Chip2.default, _Input2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.AUTOCOMPLETE)(Autocomplete);
+exports.autocompleteFactory = factory;
+exports.Autocomplete = Autocomplete;
\ No newline at end of file
diff --git a/lib/autocomplete/_config.scss b/lib/autocomplete/_config.scss
new file mode 100644
index 000000000..e0280558e
--- /dev/null
+++ b/lib/autocomplete/_config.scss
@@ -0,0 +1,7 @@
+$autocomplete-color-primary-contrast: $color-primary-contrast !default;
+$autocomplete-color-primary: $color-primary !default;
+$autocomplete-overflow-max-height: 45vh !default;
+$autocomplete-suggestion-active-background: $palette-grey-200 !default;
+$autocomplete-suggestion-padding: $unit !default;
+$autocomplete-suggestions-background: $color-white !default;
+$autocomplete-value-margin: $unit * .25 $unit * .5 $unit * .25 0 !default;
diff --git a/lib/autocomplete/index.js b/lib/autocomplete/index.js
new file mode 100644
index 000000000..6720adeae
--- /dev/null
+++ b/lib/autocomplete/index.js
@@ -0,0 +1,32 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Autocomplete = undefined;
+
+var _identifiers = require('../identifiers.js');
+
+var _reactCssThemr = require('react-css-themr');
+
+var _Autocomplete = require('./Autocomplete.js');
+
+var _chip = require('../chip');
+
+var _chip2 = _interopRequireDefault(_chip);
+
+var _input = require('../input');
+
+var _input2 = _interopRequireDefault(_input);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var Autocomplete = (0, _Autocomplete.autocompleteFactory)(_chip2.default, _input2.default);
+var ThemedAutocomplete = (0, _reactCssThemr.themr)(_identifiers.AUTOCOMPLETE, _theme2.default)(Autocomplete);
+
+exports.default = ThemedAutocomplete;
+exports.Autocomplete = ThemedAutocomplete;
\ No newline at end of file
diff --git a/lib/autocomplete/theme.scss b/lib/autocomplete/theme.scss
new file mode 100644
index 000000000..320f8c8ef
--- /dev/null
+++ b/lib/autocomplete/theme.scss
@@ -0,0 +1,85 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+@import "../input/config";
+
+.autocomplete {
+ position: relative;
+ padding: $unit 0;
+ &.focus {
+ .label {
+ color: $autocomplete-color-primary;
+ }
+ .suggestions {
+ max-height: $autocomplete-overflow-max-height;
+ visibility: visible;
+ box-shadow: $zdepth-shadow-1;
+ }
+ }
+}
+
+.label {
+ font-size: $font-size-tiny;
+ color: $color-text-secondary;
+ transition: color $animation-duration $animation-curve-default;
+}
+
+.values {
+ flex-direction: row;
+ flex-wrap: wrap;
+ padding-bottom: $unit / 2;
+}
+
+.value {
+ margin: $autocomplete-value-margin;
+}
+
+.suggestions {
+ @include no-webkit-scrollbar;
+ position: absolute;
+ z-index: $z-index-high;
+ width: 100%;
+ max-height: 0;
+ overflow-x: hidden;
+ overflow-y: auto;
+ visibility: hidden;
+ background-color: $autocomplete-suggestions-background;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transition-property: max-height, box-shadow;
+ &:not(.up) {
+ margin-top: - $input-padding;
+ }
+ &.up {
+ bottom: 0;
+ }
+}
+
+.suggestion {
+ padding: $autocomplete-suggestion-padding;
+ font-size: $input-field-font-size;
+ cursor: pointer;
+ &.active {
+ background-color: $autocomplete-suggestion-active-background;
+ }
+}
+
+.input {
+ position: relative;
+ &:after {
+ $size: ($input-field-height / 7);
+ $border: $size solid transparent;
+ position: absolute;
+ top: 50%;
+ right: $input-chevron-offset;
+ width: 0;
+ height: 0;
+ pointer-events: none;
+ content: "";
+ border-top: $size solid $input-text-bottom-border-color;
+ border-right: $border;
+ border-left: $border;
+ transition: transform $animation-duration $animation-curve-default;
+ }
+}
diff --git a/lib/avatar/Avatar.js b/lib/avatar/Avatar.js
new file mode 100644
index 000000000..6fd7d5256
--- /dev/null
+++ b/lib/avatar/Avatar.js
@@ -0,0 +1,70 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Avatar = exports.avatarFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _FontIcon = require('../font_icon/FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var factory = function factory(FontIcon) {
+ var Avatar = function Avatar(_ref) {
+ var children = _ref.children;
+ var className = _ref.className;
+ var icon = _ref.icon;
+ var image = _ref.image;
+ var theme = _ref.theme;
+ var title = _ref.title;
+
+ var other = _objectWithoutProperties(_ref, ['children', 'className', 'icon', 'image', 'theme', 'title']);
+
+ return _react2.default.createElement(
+ 'div',
+ _extends({ 'data-react-toolbox': 'avatar', className: theme.avatar + ' ' + className }, other),
+ children,
+ typeof image === 'string' ? _react2.default.createElement('img', { className: theme.image, src: image, title: title }) : image,
+ typeof icon === 'string' ? _react2.default.createElement(FontIcon, { className: theme.letter, value: icon }) : icon,
+ title ? _react2.default.createElement(
+ 'span',
+ { className: theme.letter },
+ title[0]
+ ) : null
+ );
+ };
+
+ Avatar.propTypes = {
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ icon: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ image: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ theme: _react.PropTypes.shape({
+ avatar: _react.PropTypes.string,
+ image: _react.PropTypes.string,
+ letter: _react.PropTypes.string
+ }),
+ title: _react.PropTypes.string
+ };
+
+ return Avatar;
+};
+
+var Avatar = factory(_FontIcon2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.AVATAR)(Avatar);
+exports.avatarFactory = factory;
+exports.Avatar = Avatar;
\ No newline at end of file
diff --git a/lib/avatar/_config.scss b/lib/avatar/_config.scss
new file mode 100644
index 000000000..a2922fb60
--- /dev/null
+++ b/lib/avatar/_config.scss
@@ -0,0 +1,4 @@
+$avatar-color: $color-white !default;
+$avatar-background: $palette-grey-500 !default;
+$avatar-size: 4 * $unit !default;
+$avatar-font-size: 2.4 * $unit !default;
diff --git a/lib/avatar/index.js b/lib/avatar/index.js
new file mode 100644
index 000000000..247561ab6
--- /dev/null
+++ b/lib/avatar/index.js
@@ -0,0 +1,28 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Avatar = undefined;
+
+var _identifiers = require('../identifiers.js');
+
+var _reactCssThemr = require('react-css-themr');
+
+var _Avatar = require('./Avatar.js');
+
+var _FontIcon = require('../font_icon/FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var Avatar = (0, _Avatar.avatarFactory)(_FontIcon2.default);
+var ThemedAvatar = (0, _reactCssThemr.themr)(_identifiers.AVATAR, _theme2.default)(Avatar);
+
+exports.default = ThemedAvatar;
+exports.Avatar = ThemedAvatar;
\ No newline at end of file
diff --git a/lib/avatar/theme.scss b/lib/avatar/theme.scss
new file mode 100644
index 000000000..9514782ae
--- /dev/null
+++ b/lib/avatar/theme.scss
@@ -0,0 +1,44 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.avatar {
+ position: relative;
+ display: inline-block;
+ width: $avatar-size;
+ height: $avatar-size;
+ overflow: hidden;
+ font-size: $avatar-font-size;
+ color: $avatar-color;
+ text-align: center;
+ vertical-align: middle;
+ background-color: $avatar-background;
+ border-radius: 50%;
+ > svg {
+ width: 1em;
+ height: $avatar-size;
+ fill: currentColor;
+ }
+ > img {
+ max-width: 100%;
+ height: auto;
+ }
+}
+
+.image {
+ position: absolute;
+ display: block;
+ width: 100%;
+ height: 100%;
+ background-color: transparent;
+ background-position: center;
+ background-size: cover;
+ border-radius: 50%;
+}
+
+.letter {
+ display: block;
+ width: 100%;
+ line-height: $avatar-size;
+}
diff --git a/lib/button/Button.js b/lib/button/Button.js
new file mode 100644
index 000000000..2b9e424f5
--- /dev/null
+++ b/lib/button/Button.js
@@ -0,0 +1,165 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Button = exports.buttonFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _FontIcon = require('../font_icon/FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+var _Ripple = require('../ripple/Ripple.js');
+
+var _Ripple2 = _interopRequireDefault(_Ripple);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(ripple, FontIcon) {
+ var Button = function (_Component) {
+ _inherits(Button, _Component);
+
+ function Button() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Button);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Button)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleMouseUp = function (event) {
+ _this.refs.button.blur();
+ if (_this.props.onMouseUp) _this.props.onMouseUp(event);
+ }, _this.handleMouseLeave = function (event) {
+ _this.refs.button.blur();
+ if (_this.props.onMouseLeave) _this.props.onMouseLeave(event);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Button, [{
+ key: 'render',
+ value: function render() {
+ var _classnames;
+
+ var _props = this.props;
+ var accent = _props.accent;
+ var children = _props.children;
+ var className = _props.className;
+ var flat = _props.flat;
+ var floating = _props.floating;
+ var href = _props.href;
+ var icon = _props.icon;
+ var inverse = _props.inverse;
+ var label = _props.label;
+ var mini = _props.mini;
+ var neutral = _props.neutral;
+ var primary = _props.primary;
+ var theme = _props.theme;
+ var raised = _props.raised;
+
+ var others = _objectWithoutProperties(_props, ['accent', 'children', 'className', 'flat', 'floating', 'href', 'icon', 'inverse', 'label', 'mini', 'neutral', 'primary', 'theme', 'raised']);
+
+ var element = href ? 'a' : 'button';
+ var level = primary ? 'primary' : accent ? 'accent' : 'neutral';
+ var shape = flat ? 'flat' : raised ? 'raised' : floating ? 'floating' : 'flat';
+
+ var classes = (0, _classnames3.default)(theme.button, [theme[shape]], (_classnames = {}, _defineProperty(_classnames, theme[level], neutral), _defineProperty(_classnames, theme.mini, mini), _defineProperty(_classnames, theme.inverse, inverse), _classnames), className);
+
+ var props = _extends({}, others, {
+ href: href,
+ ref: 'button',
+ className: classes,
+ disabled: this.props.disabled,
+ onMouseUp: this.handleMouseUp,
+ onMouseLeave: this.handleMouseLeave,
+ 'data-react-toolbox': 'button'
+ });
+
+ return _react2.default.createElement(element, props, icon ? _react2.default.createElement(FontIcon, { className: theme.icon, value: icon }) : null, label, children);
+ }
+ }]);
+
+ return Button;
+ }(_react.Component);
+
+ Button.propTypes = {
+ accent: _react.PropTypes.bool,
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ flat: _react.PropTypes.bool,
+ floating: _react.PropTypes.bool,
+ href: _react.PropTypes.string,
+ icon: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ inverse: _react.PropTypes.bool,
+ label: _react.PropTypes.string,
+ mini: _react.PropTypes.bool,
+ neutral: _react.PropTypes.bool,
+ onMouseLeave: _react.PropTypes.func,
+ onMouseUp: _react.PropTypes.func,
+ primary: _react.PropTypes.bool,
+ raised: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ accent: _react.PropTypes.string,
+ button: _react.PropTypes.string,
+ flat: _react.PropTypes.string,
+ floating: _react.PropTypes.string,
+ icon: _react.PropTypes.string,
+ inverse: _react.PropTypes.string,
+ mini: _react.PropTypes.string,
+ neutral: _react.PropTypes.string,
+ primary: _react.PropTypes.string,
+ raised: _react.PropTypes.string,
+ rippleWrapper: _react.PropTypes.string,
+ toggle: _react.PropTypes.string
+ }),
+ type: _react.PropTypes.string
+ };
+ Button.defaultProps = {
+ accent: false,
+ className: '',
+ flat: false,
+ floating: false,
+ mini: false,
+ neutral: true,
+ primary: false,
+ raised: false
+ };
+
+
+ return ripple(Button);
+};
+
+var Button = factory((0, _Ripple2.default)({ centered: false }), _FontIcon2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.BUTTON)(Button);
+exports.buttonFactory = factory;
+exports.Button = Button;
\ No newline at end of file
diff --git a/lib/button/IconButton.js b/lib/button/IconButton.js
new file mode 100644
index 000000000..98f6331b6
--- /dev/null
+++ b/lib/button/IconButton.js
@@ -0,0 +1,136 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.IconButton = exports.iconButtonFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _FontIcon = require('../font_icon/FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+var _Ripple = require('../ripple/Ripple.js');
+
+var _Ripple2 = _interopRequireDefault(_Ripple);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(ripple, FontIcon) {
+ var IconButton = function (_Component) {
+ _inherits(IconButton, _Component);
+
+ function IconButton() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, IconButton);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(IconButton)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleMouseUp = function (event) {
+ _this.refs.button.blur();
+ if (_this.props.onMouseUp) _this.props.onMouseUp(event);
+ }, _this.handleMouseLeave = function (event) {
+ _this.refs.button.blur();
+ if (_this.props.onMouseLeave) _this.props.onMouseLeave(event);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(IconButton, [{
+ key: 'render',
+ value: function render() {
+ var _classnames;
+
+ var _props = this.props;
+ var accent = _props.accent;
+ var children = _props.children;
+ var className = _props.className;
+ var href = _props.href;
+ var icon = _props.icon;
+ var inverse = _props.inverse;
+ var neutral = _props.neutral;
+ var primary = _props.primary;
+ var theme = _props.theme;
+
+ var others = _objectWithoutProperties(_props, ['accent', 'children', 'className', 'href', 'icon', 'inverse', 'neutral', 'primary', 'theme']);
+
+ var element = href ? 'a' : 'button';
+ var level = primary ? 'primary' : accent ? 'accent' : 'neutral';
+ var classes = (0, _classnames3.default)([theme.toggle], (_classnames = {}, _defineProperty(_classnames, theme[level], neutral), _defineProperty(_classnames, theme.inverse, inverse), _classnames), className);
+
+ var props = _extends({}, others, {
+ href: href,
+ ref: 'button',
+ className: classes,
+ disabled: this.props.disabled,
+ onMouseUp: this.handleMouseUp,
+ onMouseLeave: this.handleMouseLeave,
+ 'data-react-toolbox': 'button'
+ });
+
+ return _react2.default.createElement(element, props, icon ? _react2.default.createElement(FontIcon, { className: theme.icon, value: icon }) : null, children);
+ }
+ }]);
+
+ return IconButton;
+ }(_react.Component);
+
+ IconButton.propTypes = {
+ accent: _react.PropTypes.bool,
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ href: _react.PropTypes.string,
+ icon: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ inverse: _react.PropTypes.bool,
+ neutral: _react.PropTypes.bool,
+ onMouseLeave: _react.PropTypes.func,
+ onMouseUp: _react.PropTypes.func,
+ primary: _react.PropTypes.bool,
+ theme: _react.PropTypes.object,
+ type: _react.PropTypes.string
+ };
+ IconButton.defaultProps = {
+ accent: false,
+ className: '',
+ neutral: true,
+ primary: false
+ };
+
+
+ return ripple(IconButton);
+};
+
+var IconButton = factory((0, _Ripple2.default)({ centered: true }), _FontIcon2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.BUTTON)(IconButton);
+exports.iconButtonFactory = factory;
+exports.IconButton = IconButton;
\ No newline at end of file
diff --git a/lib/button/__test__/index.spec.js b/lib/button/__test__/index.spec.js
new file mode 100644
index 000000000..67d402c74
--- /dev/null
+++ b/lib/button/__test__/index.spec.js
@@ -0,0 +1,66 @@
+'use strict';
+
+var _expect = require('expect');
+
+var _expect2 = _interopRequireDefault(_expect);
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactDom = require('react-dom');
+
+var _reactDom2 = _interopRequireDefault(_reactDom);
+
+var _reactAddonsTestUtils = require('react-addons-test-utils');
+
+var _reactAddonsTestUtils2 = _interopRequireDefault(_reactAddonsTestUtils);
+
+var _theme = require('../theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+var _Button = require('../Button');
+
+var _Button2 = _interopRequireDefault(_Button);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var getRenderedClassName = function getRenderedClassName(tree, Component) {
+ var rendered = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, Component);
+ return _reactDom2.default.findDOMNode(rendered).getAttribute('class');
+};
+
+describe('Button', function () {
+ describe('#render', function () {
+ it('uses flat and neutral styles by default', function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Button2.default, { theme: _theme2.default }));
+ var className = getRenderedClassName(tree, _Button.Button);
+ (0, _expect2.default)(className).toContain(_theme2.default.flat);
+ (0, _expect2.default)(className).toContain(_theme2.default.neutral);
+ });
+
+ it('renders accent button with accent style', function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Button2.default, { accent: true, theme: _theme2.default }));
+ var className = getRenderedClassName(tree, _Button.Button);
+ (0, _expect2.default)(className).toContain(_theme2.default.flat);
+ (0, _expect2.default)(className).toContain(_theme2.default.accent);
+ });
+
+ it('renders mini button with mini style', function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Button2.default, { floating: true, mini: true, theme: _theme2.default }));
+ var className = getRenderedClassName(tree, _Button.Button);
+ (0, _expect2.default)(className).toContain(_theme2.default.floating);
+ (0, _expect2.default)(className).toContain(_theme2.default.neutral);
+ (0, _expect2.default)(className).toContain(_theme2.default.mini);
+ });
+
+ it('renders mini accented button with both styles', function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Button2.default, { accent: true, mini: true, theme: _theme2.default }));
+ var className = getRenderedClassName(tree, _Button.Button);
+ (0, _expect2.default)(className).toContain(_theme2.default.flat);
+ (0, _expect2.default)(className).toContain(_theme2.default.accent);
+ (0, _expect2.default)(className).toContain(_theme2.default.mini);
+ });
+ });
+});
\ No newline at end of file
diff --git a/lib/button/_config.scss b/lib/button/_config.scss
new file mode 100644
index 000000000..4a37d8b0c
--- /dev/null
+++ b/lib/button/_config.scss
@@ -0,0 +1,21 @@
+$button-neutral-color: $color-white !default;
+$button-neutral-color-contrast: $palette-grey-900 !default;
+$button-neutral-color-hover: rgba($palette-grey-900, .2) !default;
+$button-primary-color-contrast: $color-primary-contrast !default;
+$button-primary-color-hover: rgba($color-primary, .2) !default;
+$button-primary-color: $color-primary !default;
+$button-accent-color-contrast: $color-primary-contrast !default;
+$button-accent-color-hover: rgba($color-accent, .2) !default;
+$button-accent-color: $color-accent !default;
+$button-disabled-text-color: rgba($color-black, 0.26) !default;
+$button-disabled-background-color: rgba($color-black, 0.12) !default;
+$button-border-radius: 0.2 * $unit !default;
+$button-floating-font-size: $unit * 2.4 !default;
+$button-floating-height: $unit * 5.6 !default;
+$button-floating-mini-height: $unit * 4 !default;
+$button-floating-mini-font-size: $button-floating-mini-height / 2.25 !default;
+$button-height: $unit * 3.6 !default;
+$button-squared-icon-margin: $unit * .6 !default;
+$button-squared-min-width: 9 * $unit !default;
+$button-squared-padding: 0 $unit * 1.2 !default;
+$button-toggle-font-size: $unit * 2 !default;
diff --git a/lib/button/_mixins.scss b/lib/button/_mixins.scss
new file mode 100644
index 000000000..305be037a
--- /dev/null
+++ b/lib/button/_mixins.scss
@@ -0,0 +1,17 @@
+@mixin btn-colors($name, $color, $background, $hover) {
+ .#{$name}:not([disabled]) {
+ &.raised, &.floating {
+ color: $color;
+ background: $background;
+ }
+ &.flat, &.toggle {
+ color: $background;
+ &:focus:not(:active) {
+ background: $hover;
+ }
+ }
+ &.flat:hover {
+ background: $hover;
+ }
+ }
+}
diff --git a/lib/button/index.js b/lib/button/index.js
new file mode 100644
index 000000000..60623eae7
--- /dev/null
+++ b/lib/button/index.js
@@ -0,0 +1,37 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.IconButton = exports.Button = undefined;
+
+var _identifiers = require('../identifiers.js');
+
+var _reactCssThemr = require('react-css-themr');
+
+var _Button = require('./Button.js');
+
+var _IconButton = require('./IconButton.js');
+
+var _FontIcon = require('../font_icon/FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+var _ripple = require('../ripple');
+
+var _ripple2 = _interopRequireDefault(_ripple);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var Button = (0, _Button.buttonFactory)((0, _ripple2.default)({ centered: false }), _FontIcon2.default);
+var IconButton = (0, _IconButton.iconButtonFactory)((0, _ripple2.default)({ centered: true }), _FontIcon2.default);
+var ThemedButton = (0, _reactCssThemr.themr)(_identifiers.BUTTON, _theme2.default)(Button);
+var ThemedIconButton = (0, _reactCssThemr.themr)(_identifiers.BUTTON, _theme2.default)(IconButton);
+
+exports.default = ThemedButton;
+exports.Button = ThemedButton;
+exports.IconButton = ThemedIconButton;
\ No newline at end of file
diff --git a/lib/button/theme.scss b/lib/button/theme.scss
new file mode 100644
index 000000000..e84017c08
--- /dev/null
+++ b/lib/button/theme.scss
@@ -0,0 +1,168 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+@import "./mixins";
+
+.button {
+ position: relative;
+}
+
+%button {
+ @include typo-button();
+ position: relative;
+ display: inline-flex;
+ height: $button-height;
+ flex-direction: row;
+ align-content: center;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ text-decoration: none;
+ white-space: nowrap;
+ cursor: pointer;
+ border: 0;
+ outline: none;
+ transition: box-shadow .2s $animation-curve-fast-out-linear-in, background-color .2s $animation-curve-default, color .2s $animation-curve-default;
+ &::-moz-focus-inner {
+ border: 0;
+ }
+ > span:not([data-react-toolbox="tooltip"]) {
+ display: inline-block;
+ line-height: $button-height;
+ vertical-align: middle;
+ }
+ > svg {
+ display: inline-block;
+ width: 1em;
+ height: $button-height;
+ font-size: 120%;
+ vertical-align: middle;
+ fill: currentColor;
+ }
+ > * {
+ pointer-events: none;
+ }
+ > .rippleWrapper {
+ overflow: hidden;
+ }
+ &[disabled] {
+ color: $button-disabled-text-color;
+ pointer-events: none;
+ cursor: auto;
+ }
+}
+
+%squared {
+ min-width: $button-squared-min-width;
+ padding: $button-squared-padding;
+ border-radius: $button-border-radius;
+ .icon {
+ margin-right: $button-squared-icon-margin;
+ font-size: 120%;
+ vertical-align: middle;
+ }
+ > svg {
+ margin-right: .5 * $unit;
+ }
+}
+
+%solid {
+ &[disabled] {
+ @include shadow-2dp();
+ background-color: $button-disabled-background-color;
+ }
+ &:active {
+ @include shadow-4dp();
+ }
+ &:focus:not(:active) {
+ @include focus-shadow();
+ }
+}
+
+.raised {
+ @extend %button;
+ @extend %squared;
+ @extend %solid;
+ @include shadow-2dp();
+}
+
+.flat {
+ @extend %button;
+ @extend %squared;
+ background: transparent;
+}
+
+.floating {
+ @extend %button;
+ @extend %solid;
+ width: $button-floating-height;
+ height: $button-floating-height;
+ font-size: $button-floating-font-size;
+ border-radius: 50%;
+ box-shadow: 0 1px 1.5px 0 rgba(0, 0, 0, .12), 0 1px 1px 0 rgba(0, 0, 0, .24);
+ .icon {
+ line-height: $button-floating-height;
+ }
+ > .rippleWrapper {
+ border-radius: 50%;
+ }
+ &.mini {
+ width: $button-floating-mini-height;
+ height: $button-floating-mini-height;
+ font-size: $button-floating-mini-font-size;
+ .icon {
+ line-height: $button-floating-mini-height;
+ }
+ }
+}
+
+.toggle {
+ @extend %button;
+ width: $button-height;
+ background: transparent;
+ border-radius: 50%;
+ > .icon, svg {
+ font-size: $button-toggle-font-size;
+ line-height: $button-height;
+ vertical-align: middle;
+ }
+ > .rippleWrapper {
+ border-radius: 50%;
+ }
+}
+
+.neutral:not([disabled]) {
+ &.raised, &.floating {
+ color: $button-neutral-color-contrast;
+ background-color: $button-neutral-color;
+ }
+ &.flat, &.toggle {
+ color: $button-neutral-color-contrast;
+ &:focus:not(:active) {
+ background: $button-neutral-color-hover;
+ }
+ }
+ &.flat:hover {
+ background: $button-neutral-color-hover;
+ }
+
+ &.inverse {
+ &.raised, &.floating {
+ color: $button-neutral-color;
+ background-color: $button-neutral-color-contrast;
+ }
+ &.flat, &.toggle {
+ color: $button-neutral-color;
+ &:focus:not(:active) {
+ background: $button-neutral-color-hover;
+ }
+ }
+ &.flat:hover {
+ background: $button-neutral-color-hover;
+ }
+ }
+}
+
+@include btn-colors("primary", $button-primary-color-contrast, $button-primary-color, $button-primary-color-hover);
+@include btn-colors("accent", $button-accent-color-contrast, $button-accent-color, $button-accent-color-hover);
diff --git a/lib/card/Card.js b/lib/card/Card.js
new file mode 100644
index 000000000..2f099b726
--- /dev/null
+++ b/lib/card/Card.js
@@ -0,0 +1,56 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Card = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var Card = function Card(_ref) {
+ var children = _ref.children;
+ var className = _ref.className;
+ var raised = _ref.raised;
+ var theme = _ref.theme;
+
+ var other = _objectWithoutProperties(_ref, ['children', 'className', 'raised', 'theme']);
+
+ var classes = (0, _classnames3.default)(theme.card, _defineProperty({}, theme.raised, raised), className);
+
+ return _react2.default.createElement(
+ 'div',
+ _extends({ 'data-react-toolbox': 'card', className: classes }, other),
+ children
+ );
+};
+
+Card.propTypes = {
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ raised: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ card: _react.PropTypes.string,
+ raised: _react.PropTypes.string
+ })
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.CARD)(Card);
+exports.Card = Card;
\ No newline at end of file
diff --git a/lib/card/CardActions.js b/lib/card/CardActions.js
new file mode 100644
index 000000000..d149b8b45
--- /dev/null
+++ b/lib/card/CardActions.js
@@ -0,0 +1,49 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.CardActions = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var CardActions = function CardActions(_ref) {
+ var children = _ref.children;
+ var className = _ref.className;
+ var theme = _ref.theme;
+
+ var other = _objectWithoutProperties(_ref, ['children', 'className', 'theme']);
+
+ return _react2.default.createElement(
+ 'div',
+ _extends({ className: (0, _classnames2.default)(theme.cardActions, className) }, other),
+ children
+ );
+};
+
+CardActions.propTypes = {
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ theme: _react.PropTypes.shape({
+ cardActions: _react.PropTypes.string
+ })
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.CARD)(CardActions);
+exports.CardActions = CardActions;
\ No newline at end of file
diff --git a/lib/card/CardMedia.js b/lib/card/CardMedia.js
new file mode 100644
index 000000000..7c2cb02a2
--- /dev/null
+++ b/lib/card/CardMedia.js
@@ -0,0 +1,76 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.CardMedia = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _classnames3 = require('classnames');
+
+var _classnames4 = _interopRequireDefault(_classnames3);
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var CardMedia = function CardMedia(_ref) {
+ var aspectRatio = _ref.aspectRatio;
+ var children = _ref.children;
+ var className = _ref.className;
+ var color = _ref.color;
+ var contentOverlay = _ref.contentOverlay;
+ var image = _ref.image;
+ var theme = _ref.theme;
+
+ var other = _objectWithoutProperties(_ref, ['aspectRatio', 'children', 'className', 'color', 'contentOverlay', 'image', 'theme']);
+
+ var classes = (0, _classnames4.default)(theme.cardMedia, _defineProperty({}, theme[aspectRatio], aspectRatio), className);
+
+ var innerClasses = (0, _classnames4.default)(theme.content, _defineProperty({}, theme.contentOverlay, contentOverlay));
+
+ var bgStyle = {
+ backgroundColor: color ? color : undefined,
+ backgroundImage: typeof image === 'string' ? 'url(\'' + image + '\')' : undefined
+ };
+
+ return _react2.default.createElement(
+ 'div',
+ _extends({ style: bgStyle, className: classes }, other),
+ _react2.default.createElement(
+ 'div',
+ { className: innerClasses },
+ children
+ )
+ );
+};
+
+CardMedia.propTypes = {
+ aspectRatio: _react.PropTypes.oneOf(['wide', 'square']),
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ color: _react.PropTypes.string,
+ contentOverlay: _react.PropTypes.bool,
+ image: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ theme: _react.PropTypes.shape({
+ cardMedia: _react.PropTypes.string,
+ content: _react.PropTypes.string,
+ contentOverlay: _react.PropTypes.string,
+ square: _react.PropTypes.string,
+ wide: _react.PropTypes.string
+ })
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.CARD)(CardMedia);
+exports.CardMedia = CardMedia;
\ No newline at end of file
diff --git a/lib/card/CardText.js b/lib/card/CardText.js
new file mode 100644
index 000000000..832511ca7
--- /dev/null
+++ b/lib/card/CardText.js
@@ -0,0 +1,53 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.CardText = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var CardText = function CardText(_ref) {
+ var children = _ref.children;
+ var className = _ref.className;
+ var theme = _ref.theme;
+
+ var other = _objectWithoutProperties(_ref, ['children', 'className', 'theme']);
+
+ return _react2.default.createElement(
+ 'div',
+ _extends({ className: (0, _classnames2.default)(theme.cardText, className) }, other),
+ typeof children === 'string' ? _react2.default.createElement(
+ 'p',
+ null,
+ children
+ ) : children
+ );
+};
+
+CardText.propTypes = {
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ theme: _react.PropTypes.shape({
+ cardText: _react.PropTypes.string
+ })
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.CARD)(CardText);
+exports.CardText = CardText;
\ No newline at end of file
diff --git a/lib/card/CardTitle.js b/lib/card/CardTitle.js
new file mode 100644
index 000000000..fbf2b62e1
--- /dev/null
+++ b/lib/card/CardTitle.js
@@ -0,0 +1,94 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.cardTitleFactory = exports.CardTitle = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Avatar = require('../avatar/Avatar.js');
+
+var _Avatar2 = _interopRequireDefault(_Avatar);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var factory = function factory(Avatar) {
+ var CardTitle = function CardTitle(_ref) {
+ var _classnames;
+
+ var avatar = _ref.avatar;
+ var children = _ref.children;
+ var className = _ref.className;
+ var subtitle = _ref.subtitle;
+ var theme = _ref.theme;
+ var title = _ref.title;
+
+ var other = _objectWithoutProperties(_ref, ['avatar', 'children', 'className', 'subtitle', 'theme', 'title']);
+
+ var classes = (0, _classnames3.default)(theme.cardTitle, (_classnames = {}, _defineProperty(_classnames, theme.small, avatar), _defineProperty(_classnames, theme.large, !avatar), _classnames), className);
+
+ return _react2.default.createElement(
+ 'div',
+ _extends({ className: classes }, other),
+ typeof avatar === 'string' ? _react2.default.createElement(Avatar, { image: avatar }) : avatar,
+ _react2.default.createElement(
+ 'div',
+ null,
+ title && _react2.default.createElement(
+ 'h5',
+ { className: theme.title },
+ title
+ ),
+ children && typeof children === 'string' && _react2.default.createElement(
+ 'h5',
+ { className: theme.title },
+ children
+ ),
+ subtitle && _react2.default.createElement(
+ 'p',
+ { className: theme.subtitle },
+ subtitle
+ ),
+ children && typeof children !== 'string' && children
+ )
+ );
+ };
+
+ CardTitle.propTypes = {
+ avatar: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ children: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element, _react.PropTypes.array]),
+ className: _react.PropTypes.string,
+ subtitle: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ theme: _react.PropTypes.shape({
+ large: _react.PropTypes.string,
+ title: _react.PropTypes.string,
+ small: _react.PropTypes.string,
+ subtitle: _react.PropTypes.string
+ }),
+ title: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element])
+ };
+
+ return CardTitle;
+};
+
+var CardTitle = factory(_Avatar2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.CARD)(CardTitle);
+exports.CardTitle = CardTitle;
+exports.cardTitleFactory = factory;
\ No newline at end of file
diff --git a/lib/card/_config.scss b/lib/card/_config.scss
new file mode 100644
index 000000000..c89b3cb53
--- /dev/null
+++ b/lib/card/_config.scss
@@ -0,0 +1,7 @@
+$card-color-white: $color-white !default;
+$card-text-overlay: rgba($color-black, 0.35) !default;
+$card-background-color: $card-color-white !default;
+$card-padding-sm: .8 * $unit !default;
+$card-padding: 1.6 * $unit !default;
+$card-padding-lg: 2 * $unit !default;
+$card-font-size: $font-size-small !default;
diff --git a/lib/card/index.js b/lib/card/index.js
new file mode 100644
index 000000000..d124b681e
--- /dev/null
+++ b/lib/card/index.js
@@ -0,0 +1,44 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.CardTitle = exports.CardText = exports.CardMedia = exports.CardActions = exports.Card = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Card = require('./Card.js');
+
+var _CardActions = require('./CardActions.js');
+
+var _CardMedia = require('./CardMedia.js');
+
+var _CardText = require('./CardText.js');
+
+var _CardTitle = require('./CardTitle.js');
+
+var _avatar = require('../avatar');
+
+var _avatar2 = _interopRequireDefault(_avatar);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var CardTitle = (0, _CardTitle.cardTitleFactory)(_avatar2.default);
+var ThemedCard = (0, _reactCssThemr.themr)(_identifiers.CARD, _theme2.default)(_Card.Card);
+var ThemedCardActions = (0, _reactCssThemr.themr)(_identifiers.CARD, _theme2.default)(_CardActions.CardActions);
+var ThemedCardMedia = (0, _reactCssThemr.themr)(_identifiers.CARD, _theme2.default)(_CardMedia.CardMedia);
+var ThemedCardText = (0, _reactCssThemr.themr)(_identifiers.CARD, _theme2.default)(_CardText.CardText);
+var ThemedCardTitle = (0, _reactCssThemr.themr)(_identifiers.CARD, _theme2.default)(CardTitle);
+
+exports.default = ThemedCard;
+exports.Card = ThemedCard;
+exports.CardActions = ThemedCardActions;
+exports.CardMedia = ThemedCardMedia;
+exports.CardText = ThemedCardText;
+exports.CardTitle = ThemedCardTitle;
\ No newline at end of file
diff --git a/lib/card/theme.scss b/lib/card/theme.scss
new file mode 100644
index 000000000..1fbdef151
--- /dev/null
+++ b/lib/card/theme.scss
@@ -0,0 +1,122 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.card {
+ @include shadow-2dp();
+ display: flex;
+ width: 100%;
+ flex-direction: column;
+ overflow: hidden;
+ font-size: $card-font-size;
+ background: $card-background-color;
+ border-radius: .2 * $unit;
+ &.raised {
+ @include shadow-8dp();
+ }
+ [data-react-toolbox="avatar"] {
+ display: block;
+ }
+}
+
+.cardMedia {
+ position: relative;
+ background-repeat: no-repeat;
+ background-position: center center;
+ background-size: cover;
+ &.wide, &.square {
+ width: 100%;
+ height: 0;
+ .content {
+ position: absolute;
+ height: 100%;
+ }
+ .content > iframe, .content > video, .content > img {
+ max-width: 100%;
+ }
+ }
+ &.wide {
+ padding-top: 56.25%;
+ }
+ &.square {
+ padding-top: 100%;
+ }
+ .content {
+ position: relative;
+ top: 0;
+ left: 0;
+ display: flex;
+ width: 100%;
+ flex-direction: column;
+ justify-content: flex-end;
+ overflow: hidden;
+ }
+ .contentOverlay {
+ .cardTitle, .cardActions, .cardText {
+ background-color: $card-text-overlay;
+ }
+ }
+}
+
+.cardTitle {
+ display: flex;
+ align-items: center;
+ [data-react-toolbox="avatar"] {
+ margin-right: 1.3 * $unit;
+ }
+ .subtitle {
+ color: $color-text-secondary;
+ }
+ &.large {
+ padding: $card-padding-lg $card-padding ($card-padding - .2 * $unit);
+ .title {
+ @include typo-headline();
+ line-height: 1.25;
+ }
+ }
+ &.small {
+ padding: $card-padding;
+ .title {
+ @include typo-body-2(false, true);
+ line-height: 1.4;
+ }
+ .subtitle {
+ font-weight: 500;
+ line-height: 1.4;
+ }
+ }
+ .cardMedia & {
+ .title, .subtitle {
+ color: $card-color-white;
+ }
+ }
+}
+
+.cardTitle, .cardText {
+ padding: ($card-padding - .2 * $unit) $card-padding;
+ &:last-child {
+ padding-bottom: $card-padding-lg;
+ }
+ + .cardText {
+ padding-top: 0;
+ }
+}
+
+.cardActions {
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ padding: $card-padding-sm;
+ [data-react-toolbox="button"] {
+ min-width: 0;
+ padding: 0 $card-padding-sm;
+ margin: 0 $card-padding-sm / 2;
+ &:first-child {
+ margin-left: 0;
+ }
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+}
diff --git a/lib/checkbox/Check.js b/lib/checkbox/Check.js
new file mode 100644
index 000000000..8e7de820c
--- /dev/null
+++ b/lib/checkbox/Check.js
@@ -0,0 +1,49 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+var factory = function factory(ripple) {
+ var Check = function Check(_ref) {
+ var checked = _ref.checked;
+ var children = _ref.children;
+ var onMouseDown = _ref.onMouseDown;
+ var theme = _ref.theme;
+ return _react2.default.createElement(
+ 'div',
+ {
+ 'data-react-toolbox': 'check',
+ className: (0, _classnames3.default)(theme.check, _defineProperty({}, theme.checked, checked)),
+ onMouseDown: onMouseDown
+ },
+ children
+ );
+ };
+
+ Check.propTypes = {
+ checked: _react.PropTypes.bool,
+ children: _react.PropTypes.any,
+ onMouseDown: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ check: _react.PropTypes.string,
+ checked: _react.PropTypes.string
+ })
+ };
+
+ return ripple(Check);
+};
+
+exports.default = factory;
\ No newline at end of file
diff --git a/lib/checkbox/Checkbox.js b/lib/checkbox/Checkbox.js
new file mode 100644
index 000000000..a929997b4
--- /dev/null
+++ b/lib/checkbox/Checkbox.js
@@ -0,0 +1,144 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Checkbox = exports.checkboxFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Ripple = require('../ripple/Ripple.js');
+
+var _Ripple2 = _interopRequireDefault(_Ripple);
+
+var _Check = require('./Check.js');
+
+var _Check2 = _interopRequireDefault(_Check);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(Check) {
+ var Checkbox = function (_Component) {
+ _inherits(Checkbox, _Component);
+
+ function Checkbox() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Checkbox);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Checkbox)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleToggle = function (event) {
+ if (event.pageX !== 0 && event.pageY !== 0) _this.blur();
+ if (!_this.props.disabled && _this.props.onChange) {
+ _this.props.onChange(!_this.props.checked, event);
+ }
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Checkbox, [{
+ key: 'blur',
+ value: function blur() {
+ this.refs.input.blur();
+ }
+ }, {
+ key: 'focus',
+ value: function focus() {
+ this.refs.input.focus();
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var onChange = _props.onChange;
+ var theme = _props.theme;
+
+ var others = _objectWithoutProperties(_props, ['onChange', 'theme']); //eslint-disable-line no-unused-vars
+
+
+ var className = (0, _classnames3.default)(theme.field, _defineProperty({}, theme.disabled, this.props.disabled), this.props.className);
+
+ return _react2.default.createElement(
+ 'label',
+ { 'data-react-toolbox': 'checkbox', className: className },
+ _react2.default.createElement('input', _extends({}, others, {
+ className: theme.input,
+ onClick: this.handleToggle,
+ readOnly: true,
+ ref: 'input',
+ type: 'checkbox'
+ })),
+ _react2.default.createElement(Check, {
+ checked: this.props.checked,
+ disabled: this.props.disabled,
+ rippleClassName: theme.ripple,
+ theme: this.props.theme
+ }),
+ this.props.label ? _react2.default.createElement(
+ 'span',
+ { 'data-react-toolbox': 'label', className: theme.text },
+ this.props.label
+ ) : null
+ );
+ }
+ }]);
+
+ return Checkbox;
+ }(_react.Component);
+
+ Checkbox.propTypes = {
+ checked: _react.PropTypes.bool,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ label: _react.PropTypes.any,
+ onChange: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ disabled: _react.PropTypes.string,
+ field: _react.PropTypes.string,
+ input: _react.PropTypes.string,
+ ripple: _react.PropTypes.string
+ })
+ };
+ Checkbox.defaultProps = {
+ checked: false,
+ className: '',
+ disabled: false
+ };
+
+
+ return Checkbox;
+};
+
+var Check = (0, _Check2.default)((0, _Ripple2.default)({ centered: true, spread: 2.6 }));
+var Checkbox = factory(Check);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.CHECKBOX)(Checkbox);
+exports.checkboxFactory = factory;
+exports.Checkbox = Checkbox;
\ No newline at end of file
diff --git a/lib/checkbox/_config.scss b/lib/checkbox/_config.scss
new file mode 100644
index 000000000..18266e498
--- /dev/null
+++ b/lib/checkbox/_config.scss
@@ -0,0 +1,12 @@
+$checkbox-color: $color-primary !default;
+$checkbox-disabled-color: rgba($color-black, 0.26) !default;
+$checkbox-field-margin-bottom: 1.5 * $unit !default;
+$checkbox-focus-checked-color: rgba($color-primary, 0.26) !default;
+$checkbox-ripple-duration: 650ms !default;
+$checkbox-size: 1.8 * $unit !default;
+$checkbox-focus-color: rgba($color-black, 0.1) !default;
+$checkbox-focus-size: $checkbox-size * 2.3 !default;
+$checkbox-text-color: $color-black !default;
+$checkbox-text-font-size: $font-size-small !default;
+$checkbox-total-height: 1.8 * $unit !default;
+$checkbox-transition-duration: .2s !default;
diff --git a/lib/checkbox/index.js b/lib/checkbox/index.js
new file mode 100644
index 000000000..5efa6e16c
--- /dev/null
+++ b/lib/checkbox/index.js
@@ -0,0 +1,32 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Checkbox = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _ripple = require('../ripple');
+
+var _ripple2 = _interopRequireDefault(_ripple);
+
+var _Checkbox = require('./Checkbox.js');
+
+var _Check = require('./Check.js');
+
+var _Check2 = _interopRequireDefault(_Check);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ThemedCheck = (0, _Check2.default)((0, _ripple2.default)({ centered: true, spread: 2.6 }));
+var ThemedCheckbox = (0, _reactCssThemr.themr)(_identifiers.CHECKBOX, _theme2.default)((0, _Checkbox.checkboxFactory)(ThemedCheck));
+
+exports.default = ThemedCheckbox;
+exports.Checkbox = ThemedCheckbox;
\ No newline at end of file
diff --git a/lib/checkbox/theme.scss b/lib/checkbox/theme.scss
new file mode 100644
index 000000000..3c77203b8
--- /dev/null
+++ b/lib/checkbox/theme.scss
@@ -0,0 +1,120 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.field {
+ position: relative;
+ display: block;
+ height: $checkbox-size;
+ margin-bottom: $checkbox-field-margin-bottom;
+ white-space: nowrap;
+ vertical-align: middle;
+ .ripple {
+ background-color: $checkbox-color;
+ opacity: .3;
+ transition-duration: $checkbox-ripple-duration;
+ }
+}
+
+.text {
+ display: inline-block;
+ padding-left: $unit;
+ font-size: $checkbox-text-font-size;
+ line-height: $checkbox-size;
+ color: $checkbox-text-color;
+ white-space: nowrap;
+ vertical-align: top;
+}
+
+.input {
+ width: 0;
+ height: 0;
+ overflow: hidden;
+ opacity: 0;
+ &:focus ~ .check {
+ &:before {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ 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%;
+ }
+ &.checked:before {
+ background-color: $checkbox-focus-checked-color;
+ }
+ }
+}
+
+.check {
+ position: relative;
+ display: inline-block;
+ width: $checkbox-size;
+ height: $checkbox-size;
+ vertical-align: top;
+ cursor: pointer;
+ border-color: $checkbox-text-color;
+ border-style: solid;
+ border-width: 2px;
+ border-radius: 2px;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $checkbox-transition-duration;
+ transition-property: background-color;
+ &.checked {
+ 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: $color-background;
+ 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;
+ }
+ }
+}
+
+.disabled {
+ > .text {
+ 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/lib/chip/Chip.js b/lib/chip/Chip.js
new file mode 100644
index 000000000..a463c501b
--- /dev/null
+++ b/lib/chip/Chip.js
@@ -0,0 +1,98 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Chip = exports.chipFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Avatar = require('../avatar/Avatar.js');
+
+var _Avatar2 = _interopRequireDefault(_Avatar);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var factory = function factory(Avatar) {
+ var Chip = function Chip(_ref) {
+ var _classnames;
+
+ var children = _ref.children;
+ var className = _ref.className;
+ var deletable = _ref.deletable;
+ var onDeleteClick = _ref.onDeleteClick;
+ var theme = _ref.theme;
+
+ var other = _objectWithoutProperties(_ref, ['children', 'className', 'deletable', 'onDeleteClick', 'theme']);
+
+ var hasAvatar = false;
+ if (_react2.default.Children.count(children)) {
+ var firstChild = children[0];
+ hasAvatar = firstChild && firstChild.type && firstChild.type === Avatar;
+ }
+
+ var classes = (0, _classnames3.default)(theme.chip, (_classnames = {}, _defineProperty(_classnames, theme.deletable, !!deletable), _defineProperty(_classnames, theme.avatar, !!hasAvatar), _classnames), className);
+
+ return _react2.default.createElement(
+ 'div',
+ _extends({ 'data-react-toolbox': 'chip', className: classes }, other),
+ typeof children === 'string' ? _react2.default.createElement(
+ 'span',
+ null,
+ children
+ ) : children,
+ deletable ? _react2.default.createElement(
+ 'span',
+ { className: theme.delete, onClick: onDeleteClick },
+ _react2.default.createElement(
+ 'svg',
+ { viewBox: '0 0 40 40', className: theme.deleteIcon },
+ _react2.default.createElement('path', { className: theme.deleteX, d: 'M 12,12 L 28,28 M 28,12 L 12,28' })
+ )
+ ) : null
+ );
+ };
+
+ Chip.propTypes = {
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ deletable: _react.PropTypes.bool,
+ onDeleteClick: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ avatar: _react.PropTypes.string,
+ chip: _react.PropTypes.string,
+ deletable: _react.PropTypes.string,
+ delete: _react.PropTypes.string,
+ deleteIcon: _react.PropTypes.string,
+ deleteX: _react.PropTypes.string
+ })
+ };
+
+ Chip.defaultProps = {
+ className: '',
+ deletable: false
+ };
+
+ return Chip;
+};
+
+var Chip = factory(_Avatar2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.CHIP)(Chip);
+exports.chipFactory = factory;
+exports.Chip = Chip;
\ No newline at end of file
diff --git a/lib/chip/_config.scss b/lib/chip/_config.scss
new file mode 100644
index 000000000..a335b2f66
--- /dev/null
+++ b/lib/chip/_config.scss
@@ -0,0 +1,17 @@
+$chip-height: 3.2 * $unit !default;
+$chip-padding: 1.2 * $unit !default;
+$chip-margin-right: 0.25 * $unit !default;
+$chip-background: $palette-grey-200 !default;
+
+$chip-icon-font-size: 2 * $unit !default;
+$chip-icon-margin-right: 0.8 * $unit !default;
+
+$chip-color: $color-text-secondary !default;
+$chip-font-size: $font-size-small !default;
+
+$chip-remove-size: 2.4 * $unit !default;
+$chip-remove-margin: 0.4 * $unit !default;
+$chip-remove-stroke-width: 0.4 * $unit !default;
+$chip-remove-background: $palette-grey-400 !default;
+$chip-remove-background-hover: $palette-grey-500 !default;
+$chip-remove-color: $color-white !default;
diff --git a/lib/chip/index.js b/lib/chip/index.js
new file mode 100644
index 000000000..5a62d8219
--- /dev/null
+++ b/lib/chip/index.js
@@ -0,0 +1,28 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Chip = undefined;
+
+var _identifiers = require('../identifiers.js');
+
+var _reactCssThemr = require('react-css-themr');
+
+var _Chip = require('./Chip.js');
+
+var _avatar = require('../avatar');
+
+var _avatar2 = _interopRequireDefault(_avatar);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var Chip = (0, _Chip.chipFactory)(_avatar2.default);
+var ThemedChip = (0, _reactCssThemr.themr)(_identifiers.CHIP, _theme2.default)(Chip);
+
+exports.default = ThemedChip;
+exports.Chip = ThemedChip;
\ No newline at end of file
diff --git a/lib/chip/theme.scss b/lib/chip/theme.scss
new file mode 100644
index 000000000..4baa8f9c9
--- /dev/null
+++ b/lib/chip/theme.scss
@@ -0,0 +1,68 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.chip {
+ position: relative;
+ display: inline-block;
+ max-width: 100%;
+ padding: 0 $chip-padding;
+ margin-right: $chip-margin-right;
+ overflow: hidden;
+ font-size: $chip-font-size;
+ line-height: $chip-height;
+ color: $chip-color;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ background-color: $chip-background;
+ border-radius: $chip-height;
+}
+
+.avatar {
+ padding-left: 0;
+
+ > [data-react-toolbox="avatar"] {
+ width: $chip-height;
+ height: $chip-height;
+ margin-right: $chip-icon-margin-right;
+ vertical-align: middle;
+
+ > span {
+ font-size: $chip-icon-font-size;
+ line-height: $chip-height;
+ }
+ }
+}
+
+.deletable {
+ padding-right: $chip-remove-size + 2 * $chip-remove-margin;
+}
+
+.delete {
+ position: absolute;
+ right: 0;
+ display: inline-block;
+ width: $chip-remove-size;
+ height: $chip-remove-size;
+ padding: $chip-remove-margin;
+ margin: $chip-remove-margin;
+ vertical-align: middle;
+ cursor: pointer;
+}
+
+.delete:hover .deleteIcon {
+ background: $chip-remove-background-hover;
+}
+
+.deleteIcon {
+ vertical-align: top;
+ background: $chip-remove-background;
+ border-radius: $chip-remove-size;
+
+ .deleteX {
+ fill: transparent;
+ stroke-width: $chip-remove-stroke-width;
+ stroke: $chip-remove-color;
+ }
+}
diff --git a/lib/commons.scss b/lib/commons.scss
new file mode 100644
index 000000000..10c0ced55
--- /dev/null
+++ b/lib/commons.scss
@@ -0,0 +1,90 @@
+@import "./colors";
+@import "./globals";
+@import "./mixins";
+@import "~normalize.css";
+
+html {
+ font-size: 62.5%;
+}
+
+body {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ font-family: Roboto, sans-serif;
+ font-size: 1.6rem;
+ -webkit-touch-callout: none;
+ * {
+ -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+ }
+}
+
+a, abbr, address, article, aside, audio, b, blockquote, body, caption, cite,
+code, dd, del, dfn, dialog, div, dl, dt, em, fieldset, figure, footer, form, h1,
+h2, h3, h4, h5, h6, header, hgroup, hr, html, i, iframe, img, ins, kbd, label,
+legend, li, mark, menu, nav, object, ol, p, pre, q, samp, section, small, span,
+strong, sub, sup, table, tbody, td, tfoot, th, thead, time, tr, ul, var, video {
+ padding: 0;
+ margin: 0;
+ border: 0;
+ outline: 0;
+}
+
+*, *:before, *:after {
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+}
+
+h1, h2, h3, h4, h5, h6, label, p, button, abbr, a, span, small {
+ font-smoothing: antialiased;
+ -webkit-font-smoothing: antialiased;
+ text-size-adjust: 100%;
+}
+
+a {
+ text-decoration: none;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+input:not([type="checkbox"]):not([type="radio"]), button {
+ outline: none;
+ appearance: none;
+ -webkit-touch-callout: none;
+ -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+}
+
+// -- Material design font sizes
+h1 small, h2 small, h3 small, h4 small, h5 small, h6 small {
+ @include typo-display-3($color-contrast: true);
+}
+
+h1 {
+ @include typo-display-3;
+}
+
+h2 {
+ @include typo-display-2;
+}
+
+h3 {
+ @include typo-display-1;
+}
+
+h4 {
+ @include typo-headline;
+}
+
+h5 {
+ @include typo-title;
+}
+
+h6 {
+ @include typo-subhead;
+}
+
+p {
+ @include typo-body-1;
+}
diff --git a/lib/date_picker/Calendar.js b/lib/date_picker/Calendar.js
new file mode 100644
index 000000000..b401f2ea8
--- /dev/null
+++ b/lib/date_picker/Calendar.js
@@ -0,0 +1,178 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactAddonsCssTransitionGroup = require('react-addons-css-transition-group');
+
+var _reactAddonsCssTransitionGroup2 = _interopRequireDefault(_reactAddonsCssTransitionGroup);
+
+var _animations = require('../animations');
+
+var _time = require('../utils/time.js');
+
+var _time2 = _interopRequireDefault(_time);
+
+var _utils = require('../utils/utils.js');
+
+var _utils2 = _interopRequireDefault(_utils);
+
+var _CalendarMonth = require('./CalendarMonth.js');
+
+var _CalendarMonth2 = _interopRequireDefault(_CalendarMonth);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(IconButton) {
+ var Calendar = function (_Component) {
+ _inherits(Calendar, _Component);
+
+ function Calendar() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Calendar);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Calendar)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ viewDate: _this.props.selectedDate
+ }, _this.handleDayClick = function (day) {
+ _this.props.onChange(_time2.default.setDay(_this.state.viewDate, day), true);
+ }, _this.handleYearClick = function (year) {
+ var viewDate = _time2.default.setYear(_this.props.selectedDate, year);
+ _this.setState({ viewDate: viewDate });
+ _this.props.onChange(viewDate, false);
+ }, _this.changeViewMonth = function (direction, step) {
+ _this.setState({
+ direction: direction,
+ viewDate: _time2.default.addMonths(_this.state.viewDate, step)
+ });
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Calendar, [{
+ key: 'componentDidUpdate',
+ value: function componentDidUpdate() {
+ if (this.refs.activeYear) {
+ this.scrollToActive();
+ }
+ }
+ }, {
+ key: 'scrollToActive',
+ value: function scrollToActive() {
+ this.refs.years.scrollTop = this.refs.activeYear.offsetTop - this.refs.years.offsetHeight / 2 + this.refs.activeYear.offsetHeight / 2;
+ }
+ }, {
+ key: 'renderYear',
+ value: function renderYear(year) {
+ var props = {
+ className: year === this.state.viewDate.getFullYear() ? this.props.theme.active : '',
+ key: year,
+ onClick: this.handleYearClick.bind(this, year)
+ };
+
+ if (year === this.state.viewDate.getFullYear()) {
+ props.ref = 'activeYear';
+ }
+
+ return _react2.default.createElement(
+ 'li',
+ props,
+ year
+ );
+ }
+ }, {
+ key: 'renderYears',
+ value: function renderYears() {
+ var _this2 = this;
+
+ return _react2.default.createElement(
+ 'ul',
+ { 'data-react-toolbox': 'years', ref: 'years', className: this.props.theme.years },
+ _utils2.default.range(1900, 2100).map(function (i) {
+ return _this2.renderYear(i);
+ })
+ );
+ }
+ }, {
+ key: 'renderMonths',
+ value: function renderMonths() {
+ var theme = this.props.theme;
+
+ var animation = this.state.direction === 'left' ? _animations.SlideLeft : _animations.SlideRight;
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'calendar' },
+ _react2.default.createElement(IconButton, { className: theme.prev, icon: 'chevron_left', onClick: this.changeViewMonth.bind(this, 'left', -1) }),
+ _react2.default.createElement(IconButton, { className: theme.next, icon: 'chevron_right', onClick: this.changeViewMonth.bind(this, 'right', 1) }),
+ _react2.default.createElement(
+ _reactAddonsCssTransitionGroup2.default,
+ { transitionName: animation, transitionEnterTimeout: 350, transitionLeaveTimeout: 350 },
+ _react2.default.createElement(_CalendarMonth2.default, {
+ key: this.state.viewDate.getMonth(),
+ maxDate: this.props.maxDate,
+ minDate: this.props.minDate,
+ onDayClick: this.handleDayClick,
+ selectedDate: this.props.selectedDate,
+ theme: this.props.theme,
+ viewDate: this.state.viewDate
+ })
+ )
+ );
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ return _react2.default.createElement(
+ 'div',
+ { className: this.props.theme.calendar },
+ this.props.display === 'months' ? this.renderMonths() : this.renderYears()
+ );
+ }
+ }]);
+
+ return Calendar;
+ }(_react.Component);
+
+ Calendar.propTypes = {
+ display: _react.PropTypes.oneOf(['months', 'years']),
+ maxDate: _react.PropTypes.object,
+ minDate: _react.PropTypes.object,
+ onChange: _react.PropTypes.func,
+ selectedDate: _react.PropTypes.object,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ calendar: _react.PropTypes.string,
+ next: _react.PropTypes.string,
+ prev: _react.PropTypes.string,
+ years: _react.PropTypes.string
+ }),
+ viewDate: _react.PropTypes.object
+ };
+ Calendar.defaultProps = {
+ display: 'months',
+ selectedDate: new Date()
+ };
+
+
+ return Calendar;
+};
+
+exports.default = factory;
\ No newline at end of file
diff --git a/lib/date_picker/CalendarDay.js b/lib/date_picker/CalendarDay.js
new file mode 100644
index 000000000..257880a19
--- /dev/null
+++ b/lib/date_picker/CalendarDay.js
@@ -0,0 +1,91 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _time = require('../utils/time.js');
+
+var _time2 = _interopRequireDefault(_time);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var Day = function (_Component) {
+ _inherits(Day, _Component);
+
+ function Day() {
+ _classCallCheck(this, Day);
+
+ return _possibleConstructorReturn(this, Object.getPrototypeOf(Day).apply(this, arguments));
+ }
+
+ _createClass(Day, [{
+ key: 'dayStyle',
+ value: function dayStyle() {
+ if (this.props.day === 1) {
+ return {
+ marginLeft: _time2.default.getFirstWeekDay(this.props.viewDate) * 100 / 7 + '%'
+ };
+ }
+ }
+ }, {
+ key: 'isSelected',
+ value: function isSelected() {
+ var sameYear = this.props.viewDate.getFullYear() === this.props.selectedDate.getFullYear();
+ var sameMonth = this.props.viewDate.getMonth() === this.props.selectedDate.getMonth();
+ var sameDay = this.props.day === this.props.selectedDate.getDate();
+ return sameYear && sameMonth && sameDay;
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _classnames;
+
+ var className = (0, _classnames3.default)(this.props.theme.day, (_classnames = {}, _defineProperty(_classnames, this.props.theme.active, this.isSelected()), _defineProperty(_classnames, this.props.theme.disabled, this.props.disabled), _classnames));
+
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'day', className: className, style: this.dayStyle() },
+ _react2.default.createElement(
+ 'span',
+ { onClick: this.props.onClick },
+ this.props.day
+ )
+ );
+ }
+ }]);
+
+ return Day;
+}(_react.Component);
+
+Day.propTypes = {
+ day: _react.PropTypes.number,
+ disabled: _react.PropTypes.bool,
+ onClick: _react.PropTypes.func,
+ selectedDate: _react.PropTypes.object,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ day: _react.PropTypes.string,
+ disabled: _react.PropTypes.string
+ }),
+ viewDate: _react.PropTypes.object
+};
+exports.default = Day;
\ No newline at end of file
diff --git a/lib/date_picker/CalendarMonth.js b/lib/date_picker/CalendarMonth.js
new file mode 100644
index 000000000..4d874b57b
--- /dev/null
+++ b/lib/date_picker/CalendarMonth.js
@@ -0,0 +1,126 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _time = require('../utils/time.js');
+
+var _time2 = _interopRequireDefault(_time);
+
+var _utils = require('../utils/utils.js');
+
+var _utils2 = _interopRequireDefault(_utils);
+
+var _CalendarDay = require('./CalendarDay.js');
+
+var _CalendarDay2 = _interopRequireDefault(_CalendarDay);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var Month = function (_Component) {
+ _inherits(Month, _Component);
+
+ function Month() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Month);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Month)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleDayClick = function (day) {
+ if (_this.props.onDayClick) _this.props.onDayClick(day);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Month, [{
+ key: 'renderWeeks',
+ value: function renderWeeks() {
+ return _utils2.default.range(0, 7).map(function (i) {
+ return _react2.default.createElement(
+ 'span',
+ { key: i },
+ _time2.default.getFullDayOfWeek(i).charAt(0)
+ );
+ });
+ }
+ }, {
+ key: 'renderDays',
+ value: function renderDays() {
+ var _this2 = this;
+
+ return _utils2.default.range(1, _time2.default.getDaysInMonth(this.props.viewDate) + 1).map(function (i) {
+ var date = new Date(_this2.props.viewDate.getFullYear(), _this2.props.viewDate.getMonth(), i);
+ var disabled = _time2.default.dateOutOfRange(date, _this2.props.minDate, _this2.props.maxDate);
+
+ return _react2.default.createElement(_CalendarDay2.default, {
+ key: i,
+ day: i,
+ disabled: disabled,
+ onClick: !disabled ? _this2.handleDayClick.bind(_this2, i) : null,
+ selectedDate: _this2.props.selectedDate,
+ theme: _this2.props.theme,
+ viewDate: _this2.props.viewDate
+ });
+ });
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'month', className: this.props.theme.month },
+ _react2.default.createElement(
+ 'span',
+ { className: this.props.theme.title },
+ _time2.default.getFullMonth(this.props.viewDate),
+ ' ',
+ this.props.viewDate.getFullYear()
+ ),
+ _react2.default.createElement(
+ 'div',
+ { className: this.props.theme.week },
+ this.renderWeeks()
+ ),
+ _react2.default.createElement(
+ 'div',
+ { className: this.props.theme.days },
+ this.renderDays()
+ )
+ );
+ }
+ }]);
+
+ return Month;
+}(_react.Component);
+
+Month.propTypes = {
+ maxDate: _react.PropTypes.object,
+ minDate: _react.PropTypes.object,
+ onDayClick: _react.PropTypes.func,
+ selectedDate: _react.PropTypes.object,
+ theme: _react.PropTypes.shape({
+ days: _react.PropTypes.string,
+ month: _react.PropTypes.string,
+ title: _react.PropTypes.string,
+ week: _react.PropTypes.string
+ }),
+ viewDate: _react.PropTypes.object
+};
+exports.default = Month;
\ No newline at end of file
diff --git a/lib/date_picker/DatePicker.js b/lib/date_picker/DatePicker.js
new file mode 100644
index 000000000..64e7983ee
--- /dev/null
+++ b/lib/date_picker/DatePicker.js
@@ -0,0 +1,157 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.DatePicker = exports.datePickerFactory = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _events = require('../utils/events.js');
+
+var _events2 = _interopRequireDefault(_events);
+
+var _time = require('../utils/time.js');
+
+var _time2 = _interopRequireDefault(_time);
+
+var _IconButton = require('../button/IconButton.js');
+
+var _IconButton2 = _interopRequireDefault(_IconButton);
+
+var _Input = require('../input/Input.js');
+
+var _Input2 = _interopRequireDefault(_Input);
+
+var _Dialog = require('../dialog/Dialog.js');
+
+var _Dialog2 = _interopRequireDefault(_Dialog);
+
+var _Calendar = require('./Calendar.js');
+
+var _Calendar2 = _interopRequireDefault(_Calendar);
+
+var _DatePickerDialog = require('./DatePickerDialog.js');
+
+var _DatePickerDialog2 = _interopRequireDefault(_DatePickerDialog);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(Input, DatePickerDialog) {
+ var DatePicker = function (_Component) {
+ _inherits(DatePicker, _Component);
+
+ function DatePicker() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, DatePicker);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(DatePicker)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ active: false
+ }, _this.handleDismiss = function () {
+ _this.setState({ active: false });
+ }, _this.handleInputMouseDown = function (event) {
+ _events2.default.pauseEvent(event);
+ _this.setState({ active: true });
+ }, _this.handleSelect = function (value, event) {
+ if (_this.props.onChange) _this.props.onChange(value, event);
+ _this.setState({ active: false });
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(DatePicker, [{
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var inputClassName = _props.inputClassName;
+ var value = _props.value;
+
+ var inputFormat = this.props.inputFormat || _time2.default.formatDate;
+ var date = Object.prototype.toString.call(value) === '[object Date]' ? value : undefined;
+ var formattedDate = date === undefined ? '' : inputFormat(value);
+
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'date-picker' },
+ _react2.default.createElement(Input, {
+ className: (0, _classnames3.default)(this.props.theme.input, _defineProperty({}, inputClassName, inputClassName)),
+ error: this.props.error,
+ onMouseDown: this.handleInputMouseDown,
+ label: this.props.label,
+ readOnly: true,
+ type: 'text',
+ icon: this.props.icon,
+ value: formattedDate
+ }),
+ _react2.default.createElement(DatePickerDialog, {
+ autoOk: this.props.autoOk,
+ active: this.state.active,
+ className: this.props.className,
+ maxDate: this.props.maxDate,
+ minDate: this.props.minDate,
+ onDismiss: this.handleDismiss,
+ onSelect: this.handleSelect,
+ theme: this.props.theme,
+ value: date
+ })
+ );
+ }
+ }]);
+
+ return DatePicker;
+ }(_react.Component);
+
+ DatePicker.propTypes = {
+ autoOk: _react.PropTypes.bool,
+ className: _react.PropTypes.string,
+ error: _react.PropTypes.string,
+ icon: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ inputClassName: _react.PropTypes.string,
+ inputFormat: _react.PropTypes.func,
+ label: _react.PropTypes.string,
+ maxDate: _react.PropTypes.object,
+ minDate: _react.PropTypes.object,
+ onChange: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ input: _react.PropTypes.string
+ }),
+ value: _react.PropTypes.oneOfType([_react.PropTypes.instanceOf(Date), _react.PropTypes.string])
+ };
+
+
+ return DatePicker;
+};
+
+var Calendar = (0, _Calendar2.default)(_IconButton2.default);
+var DatePickerDialog = (0, _DatePickerDialog2.default)(_Dialog2.default, Calendar);
+var DatePicker = factory(_Input2.default, DatePickerDialog);
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.DATE_PICKER)(DatePicker);
+exports.datePickerFactory = factory;
+exports.DatePicker = DatePicker;
\ No newline at end of file
diff --git a/lib/date_picker/DatePickerDialog.js b/lib/date_picker/DatePickerDialog.js
new file mode 100644
index 000000000..c446e1620
--- /dev/null
+++ b/lib/date_picker/DatePickerDialog.js
@@ -0,0 +1,157 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _time = require('../utils/time.js');
+
+var _time2 = _interopRequireDefault(_time);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(Dialog, Calendar) {
+ var CalendarDialog = function (_Component) {
+ _inherits(CalendarDialog, _Component);
+
+ function CalendarDialog() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, CalendarDialog);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(CalendarDialog)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ display: 'months',
+ date: _this.props.value
+ }, _this.handleCalendarChange = function (value, dayClick) {
+ var state = { display: 'months', date: value };
+ if (_time2.default.dateOutOfRange(value, _this.props.minDate, _this.props.maxDate)) {
+ state.date = _this.props.maxDate || _this.props.minDate;
+ }
+ _this.setState(state);
+ if (dayClick && _this.props.autoOk && _this.props.onSelect) {
+ _this.props.onSelect(value);
+ }
+ }, _this.handleSelect = function (event) {
+ if (_this.props.onSelect) _this.props.onSelect(_this.state.date, event);
+ }, _this.handleSwitchDisplay = function (display) {
+ _this.setState({ display: display });
+ }, _this.updateStateDate = function (date) {
+ if (Object.prototype.toString.call(date) === '[object Date]') {
+ _this.setState({
+ date: date
+ });
+ }
+ }, _this.actions = [{ label: 'Cancel', className: _this.props.theme.button, onClick: _this.props.onDismiss }, { label: 'Ok', className: _this.props.theme.button, onClick: _this.handleSelect }], _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(CalendarDialog, [{
+ key: 'componentWillMount',
+ value: function componentWillMount() {
+ this.updateStateDate(this.props.value);
+ }
+ }, {
+ key: 'componentWillReceiveProps',
+ value: function componentWillReceiveProps(nextProps) {
+ this.updateStateDate(nextProps.value);
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var theme = this.props.theme;
+
+ var display = this.state.display + 'Display';
+ var className = (0, _classnames2.default)(theme.dialog, this.props.className);
+ var headerClassName = (0, _classnames2.default)(theme.header, theme[display]);
+
+ return _react2.default.createElement(
+ Dialog,
+ { active: this.props.active, type: 'custom', className: className, actions: this.actions },
+ _react2.default.createElement(
+ 'header',
+ { className: headerClassName },
+ _react2.default.createElement(
+ 'span',
+ { className: theme.year, onClick: this.handleSwitchDisplay.bind(this, 'years') },
+ this.state.date.getFullYear()
+ ),
+ _react2.default.createElement(
+ 'h3',
+ { className: theme.date, onClick: this.handleSwitchDisplay.bind(this, 'months') },
+ _time2.default.getShortDayOfWeek(this.state.date.getDay()),
+ ', ',
+ _time2.default.getShortMonth(this.state.date),
+ ' ',
+ this.state.date.getDate()
+ )
+ ),
+ _react2.default.createElement(
+ 'div',
+ { className: theme.calendarWrapper },
+ _react2.default.createElement(Calendar, {
+ display: this.state.display,
+ maxDate: this.props.maxDate,
+ minDate: this.props.minDate,
+ onChange: this.handleCalendarChange,
+ selectedDate: this.state.date,
+ theme: this.props.theme })
+ )
+ );
+ }
+ }]);
+
+ return CalendarDialog;
+ }(_react.Component);
+
+ CalendarDialog.propTypes = {
+ active: _react.PropTypes.bool,
+ autoOk: _react.PropTypes.bool,
+ className: _react.PropTypes.string,
+ maxDate: _react.PropTypes.object,
+ minDate: _react.PropTypes.object,
+ onDismiss: _react.PropTypes.func,
+ onSelect: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ button: _react.PropTypes.string,
+ calendarWrapper: _react.PropTypes.string,
+ date: _react.PropTypes.string,
+ dialog: _react.PropTypes.string,
+ header: _react.PropTypes.string,
+ monthsDisplay: _react.PropTypes.string,
+ year: _react.PropTypes.string,
+ yearsDisplay: _react.PropTypes.string
+ }),
+ value: _react.PropTypes.object
+ };
+ CalendarDialog.defaultProps = {
+ active: false,
+ className: '',
+ value: new Date()
+ };
+
+
+ return CalendarDialog;
+};
+
+exports.default = factory;
\ No newline at end of file
diff --git a/lib/date_picker/_config.scss b/lib/date_picker/_config.scss
new file mode 100644
index 000000000..c7925211a
--- /dev/null
+++ b/lib/date_picker/_config.scss
@@ -0,0 +1,30 @@
+$datepicker-primary: $color-primary !default;
+$datepicker-primary-contrast: $color-primary-contrast !default;
+$datepicker-primary-dark: $color-primary-dark !default;
+$datepicker-primary-color: $datepicker-primary !default;
+$datepicker-primary-hover-color: rgba($datepicker-primary, .2) !default;
+$datepicker-primary-contrast-color: $datepicker-primary-contrast !default;
+$datepicker-primary-dark-color: $datepicker-primary-dark !default;
+$datepicker-dialog-width: 33 * $unit !default;
+$datepicker-inactive-opacity: .6 !default;
+$datepicker-weekday-line-height: 2 * $unit !default;
+$datepicker-weekday-font-size: $font-size-small !default;
+$datepicker-month-font-size: $font-size-big !default;
+$datepicker-day-font-size: 5 * $unit !default;
+$datepicker-day-line-height: 4 * $unit !default;
+$datepicker-year-font-size: $font-size-small !default;
+
+$calendar-primary: $color-primary !default;
+$calendar-primary-contrast: $color-primary-contrast !default;
+$calendar-primary-color: $calendar-primary !default;
+$calendar-primary-contrast-color: $calendar-primary-contrast !default;
+$calendar-primary-hover-color: rgba($calendar-primary, .21) !default;
+$calendar-arrows-color: $palette-grey-600 !default;
+$calendar-arrows-font-size: 2 * $unit !default;
+$calendar-year-font-size: 2.4 !default;
+$calendar-day-font-size: 1.3 * $unit !default;
+$calendar-day-disable-opacity: 0.25 !default;
+$calendar-row-height: 3 * $unit !default;
+$calendar-day-padding: .2 * $unit !default;
+$calendar-title-height: 3.6 * $unit !default;
+$calendar-total-height: $calendar-row-height * 7 + $calendar-title-height + $calendar-day-padding * 12 !default;
diff --git a/lib/date_picker/index.js b/lib/date_picker/index.js
new file mode 100644
index 000000000..1b6ed4eab
--- /dev/null
+++ b/lib/date_picker/index.js
@@ -0,0 +1,51 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.DatePicker = exports.default = undefined;
+
+var _DatePicker = require('./DatePicker');
+
+var _DatePicker2 = _interopRequireDefault(_DatePicker);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _DatePicker3 = require('./DatePicker.js');
+
+var _DatePickerDialog = require('./DatePickerDialog.js');
+
+var _DatePickerDialog2 = _interopRequireDefault(_DatePickerDialog);
+
+var _Calendar = require('./Calendar.js');
+
+var _Calendar2 = _interopRequireDefault(_Calendar);
+
+var _button = require('../button');
+
+var _input = require('../input');
+
+var _input2 = _interopRequireDefault(_input);
+
+var _dialog = require('../dialog');
+
+var _dialog2 = _interopRequireDefault(_dialog);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+exports.default = _DatePicker2.default;
+
+
+var Calendar = (0, _Calendar2.default)(_button.IconButton);
+var DatePickerDialog = (0, _DatePickerDialog2.default)(_dialog2.default, Calendar);
+var DatePicker = (0, _DatePicker3.datePickerFactory)(_input2.default, DatePickerDialog);
+
+var ThemedDatePicker = (0, _reactCssThemr.themr)(_identifiers.DATE_PICKER, _theme2.default)(DatePicker);
+exports.default = ThemedDatePicker;
+exports.DatePicker = ThemedDatePicker;
\ No newline at end of file
diff --git a/lib/date_picker/theme.scss b/lib/date_picker/theme.scss
new file mode 100644
index 000000000..e4dbbac76
--- /dev/null
+++ b/lib/date_picker/theme.scss
@@ -0,0 +1,155 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.input > [role="input"] {
+ cursor: pointer;
+}
+
+.header {
+ padding: 1.6 * $unit 2 * $unit;
+ color: $datepicker-primary-contrast-color;
+ cursor: pointer;
+ background-color: $datepicker-primary-color;
+}
+
+.year {
+ display: inline-block;
+ font-size: $datepicker-year-font-size;
+ transition: opacity, font-size $animation-duration $animation-curve-default;
+}
+
+.date {
+ display: block;
+ font-weight: $font-weight-semi-bold;
+ text-transform: capitalize;
+ transition: opacity $animation-duration $animation-curve-default;
+}
+
+.calendarWrapper {
+ padding: $unit .5 * $unit 0;
+}
+
+.yearsDisplay {
+ .date {
+ opacity: $datepicker-inactive-opacity;
+ }
+ .year {
+ font-size: $font-size-normal;
+ }
+}
+
+.monthsDisplay {
+ .year {
+ opacity: $datepicker-inactive-opacity;
+ }
+}
+
+.dialog {
+ width: $datepicker-dialog-width;
+ > [role="body"] {
+ padding: 0;
+ }
+ > [role="navigation"] > .button {
+ color: $datepicker-primary-color;
+ &:hover {
+ background: $datepicker-primary-hover-color;
+ }
+ &:focus:not(:active) {
+ background: $datepicker-primary-hover-color;
+ }
+ }
+}
+
+.calendar {
+ position: relative;
+ height: $calendar-total-height;
+ overflow: hidden;
+ font-size: $font-size-small;
+ line-height: $calendar-row-height;
+ text-align: center;
+ background: $calendar-primary-contrast-color;
+ .prev, .next {
+ position: absolute;
+ top: 0;
+ z-index: $z-index-high;
+ height: 3.6 * $unit;
+ cursor: pointer;
+ opacity: .7;
+ }
+ .prev {
+ left: 0;
+ }
+ .next {
+ right: 0;
+ }
+}
+
+.title {
+ display: inline-block;
+ font-weight: 500;
+ line-height: $calendar-row-height;
+}
+
+.years {
+ height: 100%;
+ overflow-y: auto;
+ font-size: $font-size-big;
+ > li {
+ line-height: 2.4;
+ cursor: pointer;
+ &.active {
+ font-size: $calendar-year-font-size;
+ color: $calendar-primary-color;
+ }
+ }
+}
+
+.week {
+ display: flex;
+ height: $calendar-row-height;
+ flex-wrap: wrap;
+ font-size: $calendar-day-font-size;
+ line-height: $calendar-row-height;
+ opacity: .5;
+ > span {
+ flex: 0 0 (100% / 7);
+ }
+}
+
+.days {
+ display: flex;
+ flex-wrap: wrap;
+ font-size: $calendar-day-font-size;
+}
+
+.day {
+ flex: 0 0 (100% / 7);
+ padding: $calendar-day-padding;
+ > span {
+ display: inline-block;
+ width: $calendar-row-height;
+ height: $calendar-row-height;
+ line-height: $calendar-row-height;
+ border-radius: 50%;
+ }
+ &:hover:not(.active):not(.disabled) > span {
+ color: $calendar-primary-contrast-color;
+ background: $calendar-primary-hover-color;
+ }
+ &.active > span {
+ color: $calendar-primary-contrast-color;
+ background: $calendar-primary-color;
+ }
+ &:hover:not(.disabled) > span {
+ cursor: pointer;
+ }
+ &.disabled {
+ opacity: $calendar-day-disable-opacity;
+ }
+}
+
+.month {
+ background-color: $calendar-primary-contrast-color;
+}
diff --git a/lib/dialog/Dialog.js b/lib/dialog/Dialog.js
new file mode 100644
index 000000000..ddb21880b
--- /dev/null
+++ b/lib/dialog/Dialog.js
@@ -0,0 +1,113 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.dialogFactory = exports.Dialog = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _classnames3 = require('classnames');
+
+var _classnames4 = _interopRequireDefault(_classnames3);
+
+var _identifiers = require('../identifiers.js');
+
+var _ActivableRenderer = require('../hoc/ActivableRenderer.js');
+
+var _ActivableRenderer2 = _interopRequireDefault(_ActivableRenderer);
+
+var _Button = require('../button/Button.js');
+
+var _Button2 = _interopRequireDefault(_Button);
+
+var _Overlay = require('../overlay/Overlay.js');
+
+var _Overlay2 = _interopRequireDefault(_Overlay);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+var factory = function factory(Overlay, Button) {
+ var Dialog = function Dialog(props) {
+ var actions = props.actions.map(function (action, idx) {
+ var className = (0, _classnames4.default)(props.theme.button, _defineProperty({}, action.className, action.className));
+ return _react2.default.createElement(Button, _extends({ key: idx }, action, { className: className }));
+ });
+
+ var className = (0, _classnames4.default)([props.theme.dialog, props.theme[props.type]], _defineProperty({}, props.theme.active, props.active), props.className);
+
+ return _react2.default.createElement(
+ Overlay,
+ {
+ active: props.active,
+ onClick: props.onOverlayClick,
+ onMouseDown: props.onOverlayMouseDown,
+ onMouseUp: props.onOverlayMouseUp,
+ onMouseMove: props.onOverlayMouseMove,
+ onEscKeyDown: props.onEscKeyDown
+ },
+ _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'dialog', className: className },
+ _react2.default.createElement(
+ 'section',
+ { role: 'body', className: props.theme.body },
+ props.title ? _react2.default.createElement(
+ 'h6',
+ { className: props.theme.title },
+ props.title
+ ) : null,
+ props.children
+ ),
+ actions.length ? _react2.default.createElement(
+ 'nav',
+ { role: 'navigation', className: props.theme.navigation },
+ actions
+ ) : null
+ )
+ );
+ };
+
+ Dialog.propTypes = {
+ actions: _react.PropTypes.array,
+ active: _react.PropTypes.bool,
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ onEscKeyDown: _react.PropTypes.func,
+ onOverlayClick: _react.PropTypes.func,
+ onOverlayMouseDown: _react.PropTypes.func,
+ onOverlayMouseMove: _react.PropTypes.func,
+ onOverlayMouseUp: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ body: _react.PropTypes.string,
+ button: _react.PropTypes.string,
+ dialog: _react.PropTypes.string,
+ navigation: _react.PropTypes.string,
+ title: _react.PropTypes.string
+ }),
+ title: _react.PropTypes.string,
+ type: _react.PropTypes.string
+ };
+
+ Dialog.defaultProps = {
+ actions: [],
+ active: false,
+ type: 'normal'
+ };
+
+ return (0, _ActivableRenderer2.default)()(Dialog);
+};
+
+var Dialog = factory(_Overlay2.default, _Button2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.DIALOG)(Dialog);
+exports.Dialog = Dialog;
+exports.dialogFactory = factory;
\ No newline at end of file
diff --git a/lib/dialog/_config.scss b/lib/dialog/_config.scss
new file mode 100644
index 000000000..c522075c9
--- /dev/null
+++ b/lib/dialog/_config.scss
@@ -0,0 +1,6 @@
+$dialog-border-radius: .2 * $unit !default;
+$dialog-color-title: $color-black !default;
+$dialog-color-white: $color-white !default;
+$dialog-content-padding: 2.4 * $unit !default;
+$dialog-navigation-padding: .8 * $unit !default;
+$dialog-translate-y: 4 * $unit !default;
diff --git a/lib/dialog/index.js b/lib/dialog/index.js
new file mode 100644
index 000000000..5a81c7e2c
--- /dev/null
+++ b/lib/dialog/index.js
@@ -0,0 +1,32 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Dialog = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Dialog = require('./Dialog.js');
+
+var _overlay = require('../overlay');
+
+var _overlay2 = _interopRequireDefault(_overlay);
+
+var _button = require('../button');
+
+var _button2 = _interopRequireDefault(_button);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var Dialog = (0, _Dialog.dialogFactory)(_overlay2.default, _button2.default);
+var ThemedDialog = (0, _reactCssThemr.themr)(_identifiers.DIALOG, _theme2.default)(Dialog);
+
+exports.default = ThemedDialog;
+exports.Dialog = ThemedDialog;
\ No newline at end of file
diff --git a/lib/dialog/theme.scss b/lib/dialog/theme.scss
new file mode 100644
index 000000000..62e7388db
--- /dev/null
+++ b/lib/dialog/theme.scss
@@ -0,0 +1,75 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.dialog {
+ display: flex;
+ max-width: 96vw;
+ max-height: 96vh;
+ flex-direction: column;
+ background-color: $dialog-color-white;
+ border-radius: $dialog-border-radius;
+ box-shadow: $zdepth-shadow-5;
+ opacity: 0;
+ transition-delay: $animation-delay;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transition-property: opacity, transform;
+ transform: translateY(-$dialog-translate-y);
+ &.active {
+ opacity: 1;
+ transform: translateY(0%);
+ }
+}
+
+.small {
+ width: 30vw;
+
+ @media screen and (max-width: $layout-breakpoint-sm-tablet) {
+ width: 50vw;
+ }
+
+ @media screen and (max-width: $layout-breakpoint-xs) {
+ width: 75vw;
+ }
+}
+
+.normal {
+ width: 50vw;
+
+ @media screen and (max-width: $layout-breakpoint-xs) {
+ width: 96vw;
+ }
+}
+
+.large {
+ width: 96vw;
+}
+
+.title {
+ @include typo-title();
+ flex-grow: 0;
+ margin-bottom: 1.6 * $unit;
+ color: $dialog-color-title;
+}
+
+.body {
+ flex-grow: 2;
+ padding: $dialog-content-padding;
+ overflow-y: auto;
+ color: $color-text-secondary;
+}
+
+.navigation {
+ flex-grow: 0;
+ padding: $dialog-navigation-padding;
+ text-align: right;
+}
+
+.button {
+ min-width: 0;
+ padding-right: $dialog-navigation-padding;
+ padding-left: $dialog-navigation-padding;
+ margin-left: $dialog-navigation-padding;
+}
diff --git a/lib/drawer/Drawer.js b/lib/drawer/Drawer.js
new file mode 100644
index 000000000..cc101cc5a
--- /dev/null
+++ b/lib/drawer/Drawer.js
@@ -0,0 +1,85 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Drawer = exports.drawerFactory = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _identifiers = require('../identifiers.js');
+
+var _ActivableRenderer = require('../hoc/ActivableRenderer.js');
+
+var _ActivableRenderer2 = _interopRequireDefault(_ActivableRenderer);
+
+var _Overlay = require('../overlay/Overlay.js');
+
+var _Overlay2 = _interopRequireDefault(_Overlay);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+var factory = function factory(Overlay) {
+ var Drawer = function Drawer(_ref) {
+ var active = _ref.active;
+ var children = _ref.children;
+ var className = _ref.className;
+ var onOverlayClick = _ref.onOverlayClick;
+ var theme = _ref.theme;
+ var type = _ref.type;
+
+ var _className = (0, _classnames3.default)([theme.drawer, theme[type]], _defineProperty({}, theme.active, active), className);
+
+ return _react2.default.createElement(
+ Overlay,
+ { active: active, onClick: onOverlayClick },
+ _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'drawer', className: _className },
+ _react2.default.createElement(
+ 'aside',
+ { className: theme.content },
+ children
+ )
+ )
+ );
+ };
+
+ Drawer.propTypes = {
+ active: _react.PropTypes.bool,
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ onOverlayClick: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ content: _react.PropTypes.string,
+ drawer: _react.PropTypes.string,
+ left: _react.PropTypes.string,
+ right: _react.PropTypes.string
+ }),
+ type: _react.PropTypes.oneOf(['left', 'right'])
+ };
+
+ Drawer.defaultProps = {
+ active: false,
+ className: '',
+ type: 'left'
+ };
+
+ return (0, _ActivableRenderer2.default)()(Drawer);
+};
+
+var Drawer = factory(_Overlay2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.DRAWER)(Drawer);
+exports.drawerFactory = factory;
+exports.Drawer = Drawer;
\ No newline at end of file
diff --git a/lib/drawer/_config.scss b/lib/drawer/_config.scss
new file mode 100644
index 000000000..2401bc696
--- /dev/null
+++ b/lib/drawer/_config.scss
@@ -0,0 +1,4 @@
+$drawer-background-color: $palette-grey-50 !default;
+$drawer-border-color: $palette-grey-300 !default;
+$drawer-text-color: $palette-grey-800 !default;
+$drawer-width: 24 * $unit !default;
diff --git a/lib/drawer/index.js b/lib/drawer/index.js
new file mode 100644
index 000000000..71d9a5ecc
--- /dev/null
+++ b/lib/drawer/index.js
@@ -0,0 +1,26 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Drawer = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _overlay = require('../overlay');
+
+var _Drawer = require('./Drawer.js');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var Drawer = (0, _Drawer.drawerFactory)(_overlay.Overlay);
+var ThemedDrawer = (0, _reactCssThemr.themr)(_identifiers.DRAWER, _theme2.default)(Drawer);
+
+exports.default = ThemedDrawer;
+exports.Drawer = ThemedDrawer;
\ No newline at end of file
diff --git a/lib/drawer/theme.scss b/lib/drawer/theme.scss
new file mode 100644
index 000000000..a089640a3
--- /dev/null
+++ b/lib/drawer/theme.scss
@@ -0,0 +1,43 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.drawer {
+ @include shadow-2dp();
+ position: absolute;
+ top: 0;
+ display: block;
+ width: $drawer-width;
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: auto;
+ color: $drawer-text-color;
+ pointer-events: none;
+ background-color: $drawer-background-color;
+ transition-delay: 0s;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transition-property: transform;
+ transform-style: preserve-3d;
+ will-change: transform;
+ &.active {
+ pointer-events: all;
+ transition-delay: $animation-delay;
+ transform: translateX(0);
+ }
+ &.right {
+ right: 0;
+ border-left: 1px solid $drawer-border-color;
+ &:not(.active) {
+ transform: translateX(100%);
+ }
+ }
+ &.left {
+ left: 0;
+ border-right: 1px solid $drawer-border-color;
+ &:not(.active) {
+ transform: translateX(- 100%);
+ }
+ }
+}
diff --git a/lib/dropdown/Dropdown.js b/lib/dropdown/Dropdown.js
new file mode 100644
index 000000000..367a187dc
--- /dev/null
+++ b/lib/dropdown/Dropdown.js
@@ -0,0 +1,261 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Dropdown = exports.dropdownFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactDom = require('react-dom');
+
+var _reactDom2 = _interopRequireDefault(_reactDom);
+
+var _classnames3 = require('classnames');
+
+var _classnames4 = _interopRequireDefault(_classnames3);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Input = require('../input/Input.js');
+
+var _Input2 = _interopRequireDefault(_Input);
+
+var _events = require('../utils/events.js');
+
+var _events2 = _interopRequireDefault(_events);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(Input) {
+ var Dropdown = function (_Component) {
+ _inherits(Dropdown, _Component);
+
+ function Dropdown() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Dropdown);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Dropdown)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ active: false,
+ up: false
+ }, _this.close = function () {
+ if (_this.state.active) {
+ _this.setState({ active: false });
+ }
+ }, _this.handleDocumentClick = function (event) {
+ if (_this.state.active && !_events2.default.targetIsDescendant(event, _reactDom2.default.findDOMNode(_this))) {
+ _this.setState({ active: false });
+ }
+ }, _this.handleMouseDown = function (event) {
+ _events2.default.pauseEvent(event);
+ var client = event.target.getBoundingClientRect();
+ var screen_height = window.innerHeight || document.documentElement.offsetHeight;
+ var up = _this.props.auto ? client.top > screen_height / 2 + client.height : false;
+ if (_this.props.onFocus) _this.props.onFocus(event);
+ _this.setState({ active: true, up: up });
+ }, _this.handleSelect = function (item, event) {
+ if (_this.props.onBlur) _this.props.onBlur(event);
+ if (!_this.props.disabled && _this.props.onChange) {
+ _this.props.onChange(item, event);
+ _this.setState({ active: false });
+ }
+ }, _this.getSelectedItem = function () {
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
+ try {
+ for (var _iterator = _this.props.source[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var item = _step.value;
+
+ if (item.value === _this.props.value) return item;
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+
+ if (!_this.props.allowBlank) {
+ return _this.props.source[0];
+ }
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Dropdown, [{
+ key: 'componentWillUpdate',
+ value: function componentWillUpdate(nextProps, nextState) {
+ if (!this.state.active && nextState.active) {
+ _events2.default.addEventsToDocument({ click: this.handleDocumentClick });
+ }
+ }
+ }, {
+ key: 'componentDidUpdate',
+ value: function componentDidUpdate(prevProps, prevState) {
+ if (prevState.active && !this.state.active) {
+ _events2.default.removeEventsFromDocument({ click: this.handleDocumentClick });
+ }
+ }
+ }, {
+ key: 'componentWillUnmount',
+ value: function componentWillUnmount() {
+ if (this.state.active) {
+ _events2.default.removeEventsFromDocument({ click: this.handleDocumentClick });
+ }
+ }
+ }, {
+ key: 'renderTemplateValue',
+ value: function renderTemplateValue(selected) {
+ var _classnames;
+
+ var theme = this.props.theme;
+
+ var className = (0, _classnames4.default)(theme.field, (_classnames = {}, _defineProperty(_classnames, theme.errored, this.props.error), _defineProperty(_classnames, theme.disabled, this.props.disabled), _classnames));
+
+ return _react2.default.createElement(
+ 'div',
+ { className: className, onMouseDown: this.handleMouseDown },
+ _react2.default.createElement(
+ 'div',
+ { className: theme.templateValue + ' ' + theme.value },
+ this.props.template(selected)
+ ),
+ this.props.label ? _react2.default.createElement(
+ 'label',
+ { className: theme.label },
+ this.props.label
+ ) : null,
+ this.props.error ? _react2.default.createElement(
+ 'span',
+ { className: theme.error },
+ this.props.error
+ ) : null
+ );
+ }
+ }, {
+ key: 'renderValue',
+ value: function renderValue(item, idx) {
+ var theme = this.props.theme;
+
+ var className = item.value === this.props.value ? theme.selected : null;
+ return _react2.default.createElement(
+ 'li',
+ { key: idx, className: className, onMouseDown: this.handleSelect.bind(this, item.value) },
+ this.props.template ? this.props.template(item) : item.label
+ );
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _classnames2;
+
+ var _props = this.props;
+ var template = _props.template;
+ var theme = _props.theme;
+ var source = _props.source;
+
+ var others = _objectWithoutProperties(_props, ['template', 'theme', 'source']);
+
+ var selected = this.getSelectedItem();
+ var className = (0, _classnames4.default)(theme.dropdown, (_classnames2 = {}, _defineProperty(_classnames2, theme.up, this.state.up), _defineProperty(_classnames2, theme.active, this.state.active), _defineProperty(_classnames2, theme.disabled, this.props.disabled), _classnames2), this.props.className);
+
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'dropdown', className: className },
+ _react2.default.createElement(Input, _extends({}, others, {
+ className: theme.value,
+ onMouseDown: this.handleMouseDown,
+ readOnly: true,
+ type: template && selected ? 'hidden' : null,
+ value: selected && selected.label
+ })),
+ template && selected ? this.renderTemplateValue(selected) : null,
+ _react2.default.createElement(
+ 'ul',
+ { className: theme.values, ref: 'values' },
+ source.map(this.renderValue.bind(this))
+ )
+ );
+ }
+ }]);
+
+ return Dropdown;
+ }(_react.Component);
+
+ Dropdown.propTypes = {
+ allowBlank: _react.PropTypes.bool,
+ auto: _react.PropTypes.bool,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ error: _react.PropTypes.string,
+ label: _react.PropTypes.string,
+ onBlur: _react.PropTypes.func,
+ onChange: _react.PropTypes.func,
+ onFocus: _react.PropTypes.func,
+ source: _react.PropTypes.array.isRequired,
+ template: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ disabled: _react.PropTypes.string,
+ dropdown: _react.PropTypes.string,
+ error: _react.PropTypes.string,
+ errored: _react.PropTypes.string,
+ field: _react.PropTypes.string,
+ label: _react.PropTypes.string,
+ selected: _react.PropTypes.string,
+ templateValue: _react.PropTypes.string,
+ up: _react.PropTypes.string,
+ value: _react.PropTypes.string,
+ values: _react.PropTypes.string
+ }),
+ value: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.number])
+ };
+ Dropdown.defaultProps = {
+ auto: true,
+ className: '',
+ allowBlank: true,
+ disabled: false
+ };
+
+
+ return Dropdown;
+};
+
+var Dropdown = factory(_Input2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.DROPDOWN)(Dropdown);
+exports.dropdownFactory = factory;
+exports.Dropdown = Dropdown;
\ No newline at end of file
diff --git a/lib/dropdown/_config.scss b/lib/dropdown/_config.scss
new file mode 100644
index 000000000..364815c0b
--- /dev/null
+++ b/lib/dropdown/_config.scss
@@ -0,0 +1,6 @@
+$dropdown-color-white: $color-white !default;
+$dropdown-color-primary: $color-primary !default;
+$dropdown-color-primary-contrast: $color-primary-contrast !default;
+$dropdown-value-hover-background: $palette-grey-200 !default;
+$dropdown-overflow-max-height: 45vh !default;
+$dropdown-value-border-radius: .2 * $unit !default;
diff --git a/lib/dropdown/index.js b/lib/dropdown/index.js
new file mode 100644
index 000000000..b2de898e7
--- /dev/null
+++ b/lib/dropdown/index.js
@@ -0,0 +1,26 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Dropdown = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Dropdown = require('./Dropdown.js');
+
+var _input = require('../input');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var Dropdown = (0, _Dropdown.dropdownFactory)(_input.Input);
+var ThemedDropdown = (0, _reactCssThemr.themr)(_identifiers.DROPDOWN, _theme2.default)(Dropdown);
+
+exports.default = ThemedDropdown;
+exports.Dropdown = ThemedDropdown;
\ No newline at end of file
diff --git a/lib/dropdown/theme.scss b/lib/dropdown/theme.scss
new file mode 100644
index 000000000..a128b2cdc
--- /dev/null
+++ b/lib/dropdown/theme.scss
@@ -0,0 +1,131 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "../input/config";
+@import "./config";
+
+.dropdown {
+ position: relative;
+ &:not(.active) {
+ > .values {
+ max-height: 0;
+ visibility: hidden;
+ }
+ }
+ &.active {
+ > .label, > .value {
+ opacity: .5;
+ }
+ > .values {
+ max-height: $dropdown-overflow-max-height;
+ visibility: visible;
+ box-shadow: $zdepth-shadow-1;
+ }
+ }
+ &:not(.up) > .values {
+ top: 0;
+ bottom: auto;
+ }
+ &.up > .values {
+ top: auto;
+ bottom: 0;
+ }
+ &.disabled {
+ pointer-events: none;
+ cursor: normal;
+ }
+}
+
+.value {
+ > input {
+ cursor: pointer;
+ }
+ &:after {
+ $size: ($input-field-height / 7);
+ $border: $size solid transparent;
+ position: absolute;
+ top: 50%;
+ right: $input-chevron-offset;
+ width: 0;
+ height: 0;
+ pointer-events: none;
+ content: "";
+ border-top: $size solid $input-text-bottom-border-color;
+ border-right: $border;
+ border-left: $border;
+ transition: transform $animation-duration $animation-curve-default;
+ }
+}
+.field {
+ position: relative;
+ padding: $input-padding 0;
+ cursor: pointer;
+ &.errored {
+ padding-bottom: 0;
+ > .label {
+ color: $input-text-error-color;
+ }
+ > .templateValue {
+ border-bottom: 1px solid $input-text-error-color;
+ }
+ }
+ &.disabled {
+ pointer-events: none;
+ cursor: normal;
+ > .templateValue {
+ border-bottom-style: dotted;
+ opacity: .7;
+ }
+ }
+}
+
+.templateValue {
+ position: relative;
+ min-height: $input-field-height;
+ padding: $input-field-padding 0;
+ color: $color-text;
+ background-color: $input-text-background-color;
+ border-bottom: 1px solid $input-text-bottom-border-color;
+}
+
+.label {
+ position: absolute;
+ top: $input-focus-label-top;
+ left: 0;
+ font-size: $input-label-font-size;
+ line-height: $input-field-font-size;
+ color: $input-text-label-color;
+}
+
+.error {
+ margin-bottom: - $input-underline-height;
+ font-size: $input-label-font-size;
+ line-height: $input-underline-height;
+ color: $input-text-error-color;
+}
+
+.values {
+ @include no-webkit-scrollbar;
+ position: absolute;
+ z-index: $z-index-high;
+ width: 100%;
+ overflow-y: auto;
+ list-style: none;
+ background-color: $dropdown-color-white;
+ border-radius: $dropdown-value-border-radius;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transition-property: max-height, box-shadow;
+ > * {
+ position: relative;
+ padding: $unit;
+ overflow: hidden;
+ cursor: pointer;
+ &:hover {
+ background-color: $dropdown-value-hover-background;
+ }
+ &.selected {
+ color: $dropdown-color-primary;
+ }
+ }
+}
diff --git a/lib/font_icon/FontIcon.js b/lib/font_icon/FontIcon.js
new file mode 100644
index 000000000..779960142
--- /dev/null
+++ b/lib/font_icon/FontIcon.js
@@ -0,0 +1,51 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.FontIcon = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var FontIcon = function FontIcon(_ref) {
+ var children = _ref.children;
+ var className = _ref.className;
+ var value = _ref.value;
+
+ var other = _objectWithoutProperties(_ref, ['children', 'className', 'value']);
+
+ return _react2.default.createElement(
+ 'span',
+ _extends({
+ 'data-react-toolbox': 'font-icon',
+ className: (0, _classnames2.default)({ 'material-icons': typeof value === 'string' }, className)
+ }, other),
+ value,
+ children
+ );
+};
+
+FontIcon.propTypes = {
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ value: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element])
+};
+
+FontIcon.defaultProps = {
+ className: ''
+};
+
+exports.default = FontIcon;
+exports.FontIcon = FontIcon;
\ No newline at end of file
diff --git a/lib/font_icon/index.js b/lib/font_icon/index.js
new file mode 100644
index 000000000..8f7ca8346
--- /dev/null
+++ b/lib/font_icon/index.js
@@ -0,0 +1,15 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.FontIcon = undefined;
+
+var _FontIcon = require('./FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+exports.default = _FontIcon2.default;
+exports.FontIcon = _FontIcon2.default;
\ No newline at end of file
diff --git a/lib/form/Form.js b/lib/form/Form.js
new file mode 100644
index 000000000..7de399c99
--- /dev/null
+++ b/lib/form/Form.js
@@ -0,0 +1,151 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Form = exports.formFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _Autocomplete = require('../autocomplete/Autocomplete.js');
+
+var _Autocomplete2 = _interopRequireDefault(_Autocomplete);
+
+var _Button = require('../button/Button.js');
+
+var _Button2 = _interopRequireDefault(_Button);
+
+var _Checkbox = require('../checkbox/Checkbox.js');
+
+var _Checkbox2 = _interopRequireDefault(_Checkbox);
+
+var _DatePicker = require('../date_picker/DatePicker.js');
+
+var _DatePicker2 = _interopRequireDefault(_DatePicker);
+
+var _Dropdown = require('../dropdown/Dropdown.js');
+
+var _Dropdown2 = _interopRequireDefault(_Dropdown);
+
+var _Input = require('../input/Input.js');
+
+var _Input2 = _interopRequireDefault(_Input);
+
+var _RadioGroup = require('../radio/RadioGroup.js');
+
+var _RadioGroup2 = _interopRequireDefault(_RadioGroup);
+
+var _Slider = require('../slider/Slider.js');
+
+var _Slider2 = _interopRequireDefault(_Slider);
+
+var _Switch = require('../switch/Switch.js');
+
+var _Switch2 = _interopRequireDefault(_Switch);
+
+var _TimePicker = require('../time_picker/TimePicker.js');
+
+var _TimePicker2 = _interopRequireDefault(_TimePicker);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(Autocomplete, Button, Checkbox, DatePicker, Dropdown, Input, RadioGroup, Slider, Switch, TimePicker) {
+
+ var COMPONENTS = {
+ 'autocomplete': Autocomplete,
+ 'button': Button,
+ 'checkbox': Checkbox,
+ 'datepicker': DatePicker,
+ 'dropdown': Dropdown,
+ 'input': Input,
+ 'radioGroup': RadioGroup,
+ 'slider': Slider,
+ 'switch': Switch,
+ 'timepicker': TimePicker
+ };
+
+ var Form = function (_Component) {
+ _inherits(Form, _Component);
+
+ function Form() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Form);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Form)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.onSubmit = function (event) {
+ event.preventDefault();
+ if (_this.props.onSubmit) _this.props.onSubmit(event);
+ }, _this.onChange = function (field, value, event) {
+ if (_this.props.onChange) _this.props.onChange(field, value, event);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Form, [{
+ key: 'renderFields',
+ value: function renderFields() {
+ var _this2 = this;
+
+ return Object.keys(this.props.model).map(function (field, index) {
+ var properties = _this2.props.model[field];
+ var Field = COMPONENTS[properties.kind.toLowerCase()];
+ return _react2.default.createElement(Field, _extends({ key: index }, properties, { onChange: _this2.onChange.bind(_this2, field) }));
+ });
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ return _react2.default.createElement(
+ 'form',
+ { 'data-react-toolbox': 'form', className: this.props.className, onSubmit: this.onSubmit },
+ this.renderFields(),
+ this.props.children
+ );
+ }
+ }]);
+
+ return Form;
+ }(_react.Component);
+
+ Form.propTypes = {
+ attributes: _react.PropTypes.array,
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ model: _react.PropTypes.object,
+ onChange: _react.PropTypes.func,
+ onError: _react.PropTypes.func,
+ onSubmit: _react.PropTypes.func,
+ onValid: _react.PropTypes.func,
+ storage: _react.PropTypes.string
+ };
+ Form.defaultProps = {
+ attributes: [],
+ className: ''
+ };
+
+
+ return Form;
+};
+
+var Form = factory(_Autocomplete2.default, _Button2.default, _Checkbox2.default, _DatePicker2.default, _Dropdown2.default, _Input2.default, _RadioGroup2.default, _Slider2.default, _Switch2.default, _TimePicker2.default);
+
+exports.default = Form;
+exports.formFactory = factory;
+exports.Form = Form;
\ No newline at end of file
diff --git a/lib/form/index.js b/lib/form/index.js
new file mode 100644
index 000000000..ee5d2741d
--- /dev/null
+++ b/lib/form/index.js
@@ -0,0 +1,55 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Form = undefined;
+
+var _Form = require('./Form.js');
+
+var _autocomplete = require('../autocomplete');
+
+var _autocomplete2 = _interopRequireDefault(_autocomplete);
+
+var _button = require('../button');
+
+var _button2 = _interopRequireDefault(_button);
+
+var _checkbox = require('../checkbox');
+
+var _checkbox2 = _interopRequireDefault(_checkbox);
+
+var _date_picker = require('../date_picker');
+
+var _date_picker2 = _interopRequireDefault(_date_picker);
+
+var _dropdown = require('../dropdown');
+
+var _dropdown2 = _interopRequireDefault(_dropdown);
+
+var _input = require('../input');
+
+var _input2 = _interopRequireDefault(_input);
+
+var _radio = require('../radio');
+
+var _radio2 = _interopRequireDefault(_radio);
+
+var _slider = require('../slider');
+
+var _slider2 = _interopRequireDefault(_slider);
+
+var _switch = require('../switch');
+
+var _switch2 = _interopRequireDefault(_switch);
+
+var _time_picker = require('../time_picker');
+
+var _time_picker2 = _interopRequireDefault(_time_picker);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ThemedForm = (0, _Form.formFactory)(_autocomplete2.default, _button2.default, _checkbox2.default, _date_picker2.default, _dropdown2.default, _input2.default, _radio2.default, _slider2.default, _switch2.default, _time_picker2.default);
+
+exports.default = ThemedForm;
+exports.Form = ThemedForm;
\ No newline at end of file
diff --git a/lib/hoc/ActivableRenderer.js b/lib/hoc/ActivableRenderer.js
new file mode 100644
index 000000000..e64654f71
--- /dev/null
+++ b/lib/hoc/ActivableRenderer.js
@@ -0,0 +1,108 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var ActivableRendererFactory = function ActivableRendererFactory() {
+ var options = arguments.length <= 0 || arguments[0] === undefined ? { delay: 500 } : arguments[0];
+ return function (ActivableComponent) {
+ var _class, _temp2;
+
+ return _temp2 = _class = function (_Component) {
+ _inherits(ActivableRenderer, _Component);
+
+ function ActivableRenderer() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, ActivableRenderer);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(ActivableRenderer)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ active: _this.props.active,
+ rendered: _this.props.active
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(ActivableRenderer, [{
+ key: 'componentWillReceiveProps',
+ value: function componentWillReceiveProps(nextProps) {
+ if (nextProps.active && !this.props.active) this.renderAndActivate();
+ if (!nextProps.active && this.props.active) this.deactivateAndUnrender();
+ }
+ }, {
+ key: 'renderAndActivate',
+ value: function renderAndActivate() {
+ var _this2 = this;
+
+ if (this.unrenderTimeout) clearTimeout(this.unrenderTimeout);
+ this.setState({ rendered: true, active: false }, function () {
+ setTimeout(function () {
+ return _this2.setState({ active: true });
+ }, 20);
+ });
+ }
+ }, {
+ key: 'deactivateAndUnrender',
+ value: function deactivateAndUnrender() {
+ var _this3 = this;
+
+ this.setState({ rendered: true, active: false }, function () {
+ _this3.unrenderTimeout = setTimeout(function () {
+ _this3.setState({ rendered: false });
+ _this3.unrenderTimeout = null;
+ }, _this3.props.delay);
+ });
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var delay = _props.delay;
+
+ var others = _objectWithoutProperties(_props, ['delay']); // eslint-disable-line no-unused-vars
+
+
+ var _state = this.state;
+ var active = _state.active;
+ var rendered = _state.rendered;
+
+ return rendered ? _react2.default.createElement(ActivableComponent, _extends({}, others, { active: active })) : null;
+ }
+ }]);
+
+ return ActivableRenderer;
+ }(_react.Component), _class.propTypes = {
+ active: _react.PropTypes.bool.isRequired,
+ children: _react.PropTypes.any,
+ delay: _react.PropTypes.number
+ }, _class.defaultProps = {
+ delay: options.delay
+ }, _temp2;
+ };
+};
+
+exports.default = ActivableRendererFactory;
\ No newline at end of file
diff --git a/lib/hoc/Portal.js b/lib/hoc/Portal.js
new file mode 100644
index 000000000..bf12d9039
--- /dev/null
+++ b/lib/hoc/Portal.js
@@ -0,0 +1,145 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactDom = require('react-dom');
+
+var _reactDom2 = _interopRequireDefault(_reactDom);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var Portal = function (_Component) {
+ _inherits(Portal, _Component);
+
+ function Portal() {
+ _classCallCheck(this, Portal);
+
+ return _possibleConstructorReturn(this, Object.getPrototypeOf(Portal).apply(this, arguments));
+ }
+
+ _createClass(Portal, [{
+ key: 'componentDidMount',
+ value: function componentDidMount() {
+ this._renderOverlay();
+ }
+ }, {
+ key: 'componentWillReceiveProps',
+ value: function componentWillReceiveProps(nextProps) {
+ if (this._overlayTarget && nextProps.container !== this.props.container) {
+ this._portalContainerNode.removeChild(this._overlayTarget);
+ this._portalContainerNode = getContainer(nextProps.container);
+ this._portalContainerNode.appendChild(this._overlayTarget);
+ }
+ }
+ }, {
+ key: 'componentDidUpdate',
+ value: function componentDidUpdate() {
+ this._renderOverlay();
+ }
+ }, {
+ key: 'componentWillUnmount',
+ value: function componentWillUnmount() {
+ this._unrenderOverlay();
+ this._unmountOverlayTarget();
+ }
+ }, {
+ key: '_mountOverlayTarget',
+ value: function _mountOverlayTarget() {
+ if (!this._overlayTarget) {
+ this._overlayTarget = document.createElement('div');
+ this._portalContainerNode = getContainer(this.props.container);
+ this._portalContainerNode.appendChild(this._overlayTarget);
+ }
+ }
+ }, {
+ key: '_unmountOverlayTarget',
+ value: function _unmountOverlayTarget() {
+ if (this._overlayTarget) {
+ this._portalContainerNode.removeChild(this._overlayTarget);
+ this._overlayTarget = null;
+ }
+ this._portalContainerNode = null;
+ }
+ }, {
+ key: '_renderOverlay',
+ value: function _renderOverlay() {
+ var overlay = !this.props.children ? null : _react2.default.Children.only(this.props.children);
+
+ if (overlay !== null) {
+ this._mountOverlayTarget();
+ this._overlayInstance = _reactDom2.default.unstable_renderSubtreeIntoContainer(this, overlay, this._overlayTarget);
+ } else {
+ this._unrenderOverlay();
+ this._unmountOverlayTarget();
+ }
+ }
+ }, {
+ key: '_unrenderOverlay',
+ value: function _unrenderOverlay() {
+ if (this._overlayTarget) {
+ _reactDom2.default.unmountComponentAtNode(this._overlayTarget);
+ this._overlayInstance = null;
+ }
+ }
+ }, {
+ key: 'getMountNode',
+ value: function getMountNode() {
+ return this._overlayTarget;
+ }
+ }, {
+ key: 'getOverlayDOMNode',
+ value: function getOverlayDOMNode() {
+ if (!this.isMounted()) {
+ throw new Error('getOverlayDOMNode(): A component must be mounted to have a DOM node.');
+ }
+
+ if (this._overlayInstance) {
+ if (this._overlayInstance.getWrappedDOMNode) {
+ return this._overlayInstance.getWrappedDOMNode();
+ } else {
+ return _reactDom2.default.findDOMNode(this._overlayInstance);
+ }
+ }
+
+ return null;
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ return null;
+ }
+ }]);
+
+ return Portal;
+}(_react.Component);
+
+Portal.propTypes = {
+ children: _react.PropTypes.any,
+ container: _react.PropTypes.any,
+ lockBody: _react.PropTypes.bool
+};
+Portal.defaultProps = {
+ lockBody: true
+};
+
+
+function getContainer(container) {
+ var _container = typeof container === 'function' ? container() : container;
+ return _reactDom2.default.findDOMNode(_container) || document.body;
+}
+
+exports.default = Portal;
\ No newline at end of file
diff --git a/lib/identifiers.js b/lib/identifiers.js
new file mode 100644
index 000000000..5bc7ebe24
--- /dev/null
+++ b/lib/identifiers.js
@@ -0,0 +1,30 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+var APP_BAR = exports.APP_BAR = 'RTAppBar';
+var AUTOCOMPLETE = exports.AUTOCOMPLETE = 'RTAutocomplete';
+var AVATAR = exports.AVATAR = 'RTAvatar';
+var BUTTON = exports.BUTTON = 'RTButton';
+var CARD = exports.CARD = 'RTCard';
+var CHIP = exports.CHIP = 'RTChip';
+var DATE_PICKER = exports.DATE_PICKER = 'RTDatePicker';
+var DIALOG = exports.DIALOG = 'RTDialog';
+var DROPDOWN = exports.DROPDOWN = 'RTDropdown';
+var INPUT = exports.INPUT = 'RTInput';
+var LAYOUT = exports.LAYOUT = 'RTLayout';
+var LINK = exports.LINK = 'RTLink';
+var LIST = exports.LIST = 'RTList';
+var NAVIGATION = exports.NAVIGATION = 'RTNavigation';
+var OVERLAY = exports.OVERLAY = 'RTOverlay';
+var PROGRESS_BAR = exports.PROGRESS_BAR = 'RTProgressBar';
+var RADIO = exports.RADIO = 'RTRadio';
+var RIPPLE = exports.RIPPLE = 'RTRipple';
+var SLIDER = exports.SLIDER = 'RTSlider';
+var SNACKBAR = exports.SNACKBAR = 'RTSnackbar';
+var SWITCH = exports.SWITCH = 'RTSwitch';
+var TABLE = exports.TABLE = 'RTTable';
+var TABS = exports.TABS = 'RTTabs';
+var TOOLTIP = exports.TOOLTIP = 'RTTooltip';
+var TIMEPICKER = exports.TIMEPICKER = 'RTTimePicker';
\ No newline at end of file
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 000000000..87f0e6b4b
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,206 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.TimePicker = exports.Tooltip = exports.Table = exports.Switch = exports.Snackbar = exports.Slider = exports.Ripple = exports.ProgressBar = exports.Navigation = exports.Link = exports.Input = exports.Form = exports.FontIcon = exports.Dropdown = exports.Drawer = exports.Dialog = exports.DatePicker = exports.Checkbox = exports.Chip = exports.Avatar = exports.Autocomplete = exports.AppBar = undefined;
+
+var _button = require('./button');
+
+Object.keys(_button).forEach(function (key) {
+ if (key === "default") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function get() {
+ return _button[key];
+ }
+ });
+});
+
+var _card = require('./card');
+
+Object.keys(_card).forEach(function (key) {
+ if (key === "default") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function get() {
+ return _card[key];
+ }
+ });
+});
+
+var _layout = require('./layout');
+
+Object.keys(_layout).forEach(function (key) {
+ if (key === "default") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function get() {
+ return _layout[key];
+ }
+ });
+});
+
+var _list = require('./list');
+
+Object.keys(_list).forEach(function (key) {
+ if (key === "default") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function get() {
+ return _list[key];
+ }
+ });
+});
+
+var _menu = require('./menu');
+
+Object.keys(_menu).forEach(function (key) {
+ if (key === "default") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function get() {
+ return _menu[key];
+ }
+ });
+});
+
+var _radio = require('./radio');
+
+Object.keys(_radio).forEach(function (key) {
+ if (key === "default") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function get() {
+ return _radio[key];
+ }
+ });
+});
+
+var _tabs = require('./tabs');
+
+Object.keys(_tabs).forEach(function (key) {
+ if (key === "default") return;
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ get: function get() {
+ return _tabs[key];
+ }
+ });
+});
+
+require('./utils/polyfills');
+
+var _app_bar = require('./app_bar');
+
+var _app_bar2 = _interopRequireDefault(_app_bar);
+
+var _autocomplete = require('./autocomplete');
+
+var _autocomplete2 = _interopRequireDefault(_autocomplete);
+
+var _avatar = require('./avatar');
+
+var _avatar2 = _interopRequireDefault(_avatar);
+
+var _chip = require('./chip');
+
+var _chip2 = _interopRequireDefault(_chip);
+
+var _checkbox = require('./checkbox');
+
+var _checkbox2 = _interopRequireDefault(_checkbox);
+
+var _date_picker = require('./date_picker');
+
+var _date_picker2 = _interopRequireDefault(_date_picker);
+
+var _dialog = require('./dialog');
+
+var _dialog2 = _interopRequireDefault(_dialog);
+
+var _drawer = require('./drawer');
+
+var _drawer2 = _interopRequireDefault(_drawer);
+
+var _dropdown = require('./dropdown');
+
+var _dropdown2 = _interopRequireDefault(_dropdown);
+
+var _font_icon = require('./font_icon');
+
+var _font_icon2 = _interopRequireDefault(_font_icon);
+
+var _form = require('./form');
+
+var _form2 = _interopRequireDefault(_form);
+
+var _input = require('./input');
+
+var _input2 = _interopRequireDefault(_input);
+
+var _link = require('./link');
+
+var _link2 = _interopRequireDefault(_link);
+
+var _navigation = require('./navigation');
+
+var _navigation2 = _interopRequireDefault(_navigation);
+
+var _progress_bar = require('./progress_bar');
+
+var _progress_bar2 = _interopRequireDefault(_progress_bar);
+
+var _ripple = require('./ripple');
+
+var _ripple2 = _interopRequireDefault(_ripple);
+
+var _slider = require('./slider');
+
+var _slider2 = _interopRequireDefault(_slider);
+
+var _snackbar = require('./snackbar');
+
+var _snackbar2 = _interopRequireDefault(_snackbar);
+
+var _switch = require('./switch');
+
+var _switch2 = _interopRequireDefault(_switch);
+
+var _table = require('./table');
+
+var _table2 = _interopRequireDefault(_table);
+
+var _tooltip = require('./tooltip');
+
+var _tooltip2 = _interopRequireDefault(_tooltip);
+
+var _time_picker = require('./time_picker');
+
+var _time_picker2 = _interopRequireDefault(_time_picker);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+exports.AppBar = _app_bar2.default; // Import polyfills for IE11
+
+exports.Autocomplete = _autocomplete2.default;
+exports.Avatar = _avatar2.default;
+exports.Chip = _chip2.default;
+exports.Checkbox = _checkbox2.default;
+exports.DatePicker = _date_picker2.default;
+exports.Dialog = _dialog2.default;
+exports.Drawer = _drawer2.default;
+exports.Dropdown = _dropdown2.default;
+exports.FontIcon = _font_icon2.default;
+exports.Form = _form2.default;
+exports.Input = _input2.default;
+exports.Link = _link2.default;
+exports.Navigation = _navigation2.default;
+exports.ProgressBar = _progress_bar2.default;
+exports.Ripple = _ripple2.default;
+exports.Slider = _slider2.default;
+exports.Snackbar = _snackbar2.default;
+exports.Switch = _switch2.default;
+exports.Table = _table2.default;
+exports.Tooltip = _tooltip2.default;
+exports.TimePicker = _time_picker2.default;
\ No newline at end of file
diff --git a/lib/input/Input.js b/lib/input/Input.js
new file mode 100644
index 000000000..455348aac
--- /dev/null
+++ b/lib/input/Input.js
@@ -0,0 +1,202 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Input = exports.inputFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames4 = require('classnames');
+
+var _classnames5 = _interopRequireDefault(_classnames4);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _FontIcon = require('../font_icon/FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(FontIcon) {
+ var Input = function (_React$Component) {
+ _inherits(Input, _React$Component);
+
+ function Input() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Input);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Input)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleChange = function (event) {
+ if (_this.props.onChange) _this.props.onChange(event.target.value, event);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Input, [{
+ key: 'blur',
+ value: function blur() {
+ this.refs.input.blur();
+ }
+ }, {
+ key: 'focus',
+ value: function focus() {
+ this.refs.input.focus();
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _classnames2;
+
+ var _props = this.props;
+ var children = _props.children;
+ var disabled = _props.disabled;
+ var error = _props.error;
+ var floating = _props.floating;
+ var hint = _props.hint;
+ var icon = _props.icon;
+ var labelText = _props.label;
+ var maxLength = _props.maxLength;
+ var multiline = _props.multiline;
+ var required = _props.required;
+ var theme = _props.theme;
+ var type = _props.type;
+ var value = _props.value;
+
+ var others = _objectWithoutProperties(_props, ['children', 'disabled', 'error', 'floating', 'hint', 'icon', 'label', 'maxLength', 'multiline', 'required', 'theme', 'type', 'value']);
+
+ var length = maxLength && value ? value.length : 0;
+ var labelClassName = (0, _classnames5.default)(theme.label, _defineProperty({}, theme.fixed, !floating));
+
+ var className = (0, _classnames5.default)(theme.input, (_classnames2 = {}, _defineProperty(_classnames2, theme.disabled, disabled), _defineProperty(_classnames2, theme.errored, error), _defineProperty(_classnames2, theme.hidden, type === 'hidden'), _defineProperty(_classnames2, theme.withIcon, icon), _classnames2), this.props.className);
+
+ var valuePresent = value !== null && value !== undefined && value !== '' && !Number.isNaN(value);
+
+ var InputElement = _react2.default.createElement(multiline ? 'textarea' : 'input', _extends({}, others, {
+ className: (0, _classnames5.default)(theme.inputElement, _defineProperty({}, theme.filled, valuePresent)),
+ onChange: this.handleChange,
+ ref: 'input',
+ role: 'input',
+ disabled: disabled,
+ required: required,
+ type: type,
+ value: value,
+ maxLength: maxLength
+ }));
+
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'input', className: className },
+ InputElement,
+ icon ? _react2.default.createElement(FontIcon, { className: theme.icon, value: icon }) : null,
+ _react2.default.createElement('span', { className: theme.bar }),
+ labelText ? _react2.default.createElement(
+ 'label',
+ { className: labelClassName },
+ labelText,
+ required ? _react2.default.createElement(
+ 'span',
+ { className: theme.required },
+ ' * '
+ ) : null
+ ) : null,
+ hint ? _react2.default.createElement(
+ 'span',
+ { className: theme.hint },
+ hint
+ ) : null,
+ error ? _react2.default.createElement(
+ 'span',
+ { className: theme.error },
+ error
+ ) : null,
+ maxLength ? _react2.default.createElement(
+ 'span',
+ { className: theme.counter },
+ length,
+ '/',
+ maxLength
+ ) : null,
+ children
+ );
+ }
+ }]);
+
+ return Input;
+ }(_react2.default.Component);
+
+ Input.propTypes = {
+ children: _react2.default.PropTypes.any,
+ className: _react2.default.PropTypes.string,
+ disabled: _react2.default.PropTypes.bool,
+ error: _react2.default.PropTypes.string,
+ floating: _react2.default.PropTypes.bool,
+ hint: _react2.default.PropTypes.string,
+ icon: _react2.default.PropTypes.oneOfType([_react2.default.PropTypes.string, _react2.default.PropTypes.element]),
+ label: _react2.default.PropTypes.string,
+ maxLength: _react2.default.PropTypes.number,
+ multiline: _react2.default.PropTypes.bool,
+ onBlur: _react2.default.PropTypes.func,
+ onChange: _react2.default.PropTypes.func,
+ onFocus: _react2.default.PropTypes.func,
+ onKeyPress: _react2.default.PropTypes.func,
+ required: _react2.default.PropTypes.bool,
+ theme: _react2.default.PropTypes.shape({
+ bar: _react2.default.PropTypes.string,
+ counter: _react2.default.PropTypes.string,
+ disabled: _react2.default.PropTypes.string,
+ error: _react2.default.PropTypes.string,
+ errored: _react2.default.PropTypes.string,
+ hidden: _react2.default.PropTypes.string,
+ hint: _react2.default.PropTypes.string,
+ icon: _react2.default.PropTypes.string,
+ input: _react2.default.PropTypes.string,
+ inputElement: _react2.default.PropTypes.string,
+ required: _react2.default.PropTypes.string,
+ withIcon: _react2.default.PropTypes.string
+ }),
+ type: _react2.default.PropTypes.string,
+ value: _react2.default.PropTypes.any
+ };
+ Input.defaultProps = {
+ className: '',
+ hint: '',
+ disabled: false,
+ floating: true,
+ multiline: false,
+ required: false,
+ type: 'text'
+ };
+
+
+ return Input;
+};
+
+var Input = factory(_FontIcon2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.INPUT)(Input);
+exports.inputFactory = factory;
+exports.Input = Input;
\ No newline at end of file
diff --git a/lib/input/_config.scss b/lib/input/_config.scss
new file mode 100644
index 000000000..ff9f58cda
--- /dev/null
+++ b/lib/input/_config.scss
@@ -0,0 +1,20 @@
+$input-padding: 2 * $unit !default;
+$input-field-padding: .8 * $unit !default;
+$input-field-font-size: 1.6 * $unit !default;
+$input-field-height: $input-field-padding * 2 + $input-field-font-size * 1.4 !default;
+$input-label-font-size: 1.2 * $unit !default;
+$input-focus-label-top: .6 * $unit !default;
+$input-text-background-color: transparent !default;
+$input-text-label-color: rgba($color-black, 0.26) !default;
+$input-text-bottom-border-color: rgba($color-black, 0.12) !default;
+$input-text-highlight-color: $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: rgb(222, 50, 38) !default;
+$input-text-required-color: rgb(222, 50, 38) !default;
+$input-underline-height: 2 * $unit !default;
+$input-icon-font-size: 2.4 * $unit !default;
+$input-icon-size: 2 * $input-icon-font-size !default;
+$input-icon-offset: 1.6 * $unit !default;
+$input-chevron-offset: .8 * $unit !default;
+$input-hint-opacity: 1 !default;
diff --git a/lib/input/index.js b/lib/input/index.js
new file mode 100644
index 000000000..5bb8e30b1
--- /dev/null
+++ b/lib/input/index.js
@@ -0,0 +1,28 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Input = undefined;
+
+var _identifiers = require('../identifiers.js');
+
+var _reactCssThemr = require('react-css-themr');
+
+var _Input = require('./Input.js');
+
+var _FontIcon = require('../font_icon/FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var Input = (0, _Input.inputFactory)(_FontIcon2.default);
+var ThemedInput = (0, _reactCssThemr.themr)(_identifiers.INPUT, _theme2.default)(Input);
+
+exports.default = ThemedInput;
+exports.Input = ThemedInput;
\ No newline at end of file
diff --git a/lib/input/theme.scss b/lib/input/theme.scss
new file mode 100644
index 000000000..739de903e
--- /dev/null
+++ b/lib/input/theme.scss
@@ -0,0 +1,152 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.input {
+ position: relative;
+ padding: $input-padding 0;
+ &.withIcon {
+ margin-left: $input-icon-size;
+ }
+}
+
+.icon {
+ position: absolute;
+ top: $input-icon-offset;
+ left: -$input-icon-size;
+ display: block;
+ width: $input-icon-size;
+ height: $input-icon-size;
+ font-size: $input-icon-font-size !important;
+ line-height: $input-icon-size !important;
+ color: $input-text-label-color;
+ text-align: center;
+ transition: color $animation-duration $animation-curve-default;
+}
+
+.inputElement {
+ display: block;
+ width: 100%;
+ padding: $input-field-padding 0;
+ font-size: $input-field-font-size;
+ color: $color-text;
+ background-color: $input-text-background-color;
+ border: 0;
+ border-bottom: 1px solid $input-text-bottom-border-color;
+ outline: none;
+ &:focus {
+ ~ .bar:before, ~ .bar:after {
+ width: 50%;
+ }
+ ~ .label:not(.fixed) {
+ color: $input-text-highlight-color;
+ }
+ ~ .label > .required {
+ color: $input-text-required-color;
+ }
+ ~ .hint {
+ opacity: $input-hint-opacity;
+ }
+ ~ .icon {
+ color: $input-text-highlight-color;
+ }
+ }
+ &:focus, &.filled, &[type="date"], &[type="time"] {
+ ~ .label:not(.fixed) {
+ top: $input-focus-label-top;
+ font-size: $input-label-font-size;
+ }
+ }
+ &.filled ~ .label.fixed, &.filled ~ .hint {
+ display: none;
+ }
+}
+
+.label {
+ position: absolute;
+ top: $input-padding + (1.5 * $input-field-padding);
+ left: 0;
+ font-size: $input-field-font-size;
+ line-height: $input-field-font-size;
+ color: $input-text-label-color;
+ pointer-events: none;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transition-property: top, font-size, color;
+ &.fixed ~ .hint {
+ display: none;
+ }
+}
+
+.hint {
+ position: absolute;
+ top: $input-padding + (1.5 * $input-field-padding);
+ left: 0;
+ font-size: $input-field-font-size;
+ line-height: $input-field-font-size;
+ color: $input-text-label-color;
+ pointer-events: none;
+ opacity: 0;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transition-property: opacity;
+}
+
+.bar {
+ position: relative;
+ display: block;
+ width: 100%;
+ &:before, &:after {
+ @include material-animation-default();
+ position: absolute;
+ bottom: 0;
+ width: 0;
+ height: 2px;
+ content: "";
+ background-color: $input-text-highlight-color;
+ transition-property: width, background-color;
+ }
+ &:before {
+ left: 50%;
+ }
+ &:after {
+ right: 50%;
+ }
+}
+
+.error, .counter {
+ margin-bottom: - $input-underline-height;
+ font-size: $input-label-font-size;
+ line-height: $input-underline-height;
+ color: $input-text-error-color;
+}
+
+.counter {
+ position: absolute;
+ right: 0;
+ color: $input-text-label-color;
+}
+
+.disabled > .inputElement {
+ color: $input-text-disabled-text-color;
+ border-bottom-style: dotted;
+}
+
+.errored {
+ padding-bottom: 0;
+ > .inputElement {
+ margin-top: 1px;
+ border-bottom-color: $input-text-error-color;
+ }
+ > .counter, > .label {
+ color: $input-text-error-color;
+ }
+ > .label > .required {
+ color: $input-text-required-color;
+ }
+}
+
+.hidden {
+ display: none;
+}
diff --git a/lib/layout/Layout.js b/lib/layout/Layout.js
new file mode 100644
index 000000000..6ce2b8922
--- /dev/null
+++ b/lib/layout/Layout.js
@@ -0,0 +1,70 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Layout = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var Layout = function Layout(_ref) {
+ var className = _ref.className;
+ var children = _ref.children;
+ var theme = _ref.theme;
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'layout', className: (0, _classnames2.default)(theme.layout, className) },
+ children
+ );
+};
+
+var ALLOWED_THEMED = ['Themed Panel', 'Themed NavDrawer|Themed Panel', 'Themed NavDrawer|Themed Panel|Themed Sidebar', 'Themed Panel|Themed Sidebar'];
+
+function getChildName(child) {
+ if (child.type) {
+ return child.type.displayName || child.type.name || child.type;
+ }
+ if (!child.constructor || !child.constructor.name) {
+ return 'UNKNOWN';
+ }
+ return child.constructor.name;
+}
+
+Layout.propTypes = {
+ children: function children(props, propName, componentName) {
+ // Accept only [NavDrawer]Pane[Sidebar]
+ var children = props[propName];
+ if (_react2.default.Children.count(children) > 3) {
+ return new Error('`' + componentName + '` ' + 'should have a Pane for a child, optionally preceded and/or followed by a Drawer.');
+ }
+
+ var names = _react2.default.Children.map(children, getChildName).join('|');
+ if (! ~ALLOWED_THEMED.indexOf(names)) {
+ return new Error('`' + componentName + '` ' + 'should have a Panel for a child, optionally preceded by a NavDrawer and/or followed by a Sidebar.');
+ }
+ },
+
+ className: _react.PropTypes.string,
+ theme: _react.PropTypes.shape({
+ layout: _react.PropTypes.string
+ })
+};
+
+Layout.defaultProps = {
+ className: ''
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LAYOUT)(Layout);
+exports.Layout = Layout;
\ No newline at end of file
diff --git a/lib/layout/NavDrawer.js b/lib/layout/NavDrawer.js
new file mode 100644
index 000000000..9401ffa5e
--- /dev/null
+++ b/lib/layout/NavDrawer.js
@@ -0,0 +1,90 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.NavDrawer = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames3 = require('classnames');
+
+var _classnames4 = _interopRequireDefault(_classnames3);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+var NavDrawer = function NavDrawer(_ref) {
+ var _classnames;
+
+ var active = _ref.active;
+ var children = _ref.children;
+ var className = _ref.className;
+ var onOverlayClick = _ref.onOverlayClick;
+ var permanentAt = _ref.permanentAt;
+ var pinned = _ref.pinned;
+ var scrollY = _ref.scrollY;
+ var theme = _ref.theme;
+ var width = _ref.width;
+
+ var rootClasses = (0, _classnames4.default)([theme.navDrawer], (_classnames = {}, _defineProperty(_classnames, theme[permanentAt + 'Permanent'], permanentAt), _defineProperty(_classnames, theme.wide, width === 'wide'), _defineProperty(_classnames, theme.active, active), _defineProperty(_classnames, theme.pinned, pinned), _classnames), className);
+
+ var drawerClasses = (0, _classnames4.default)(theme.drawerContent, _defineProperty({}, theme.scrollY, scrollY));
+
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'nav-drawer', className: rootClasses, onClick: onOverlayClick },
+ _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'nav-drawer-scrim', className: theme.scrim },
+ _react2.default.createElement(
+ 'aside',
+ { 'data-react-toolbox': 'nav-drawer-content', className: drawerClasses },
+ children
+ )
+ )
+ );
+};
+
+NavDrawer.propTypes = {
+ active: _react.PropTypes.bool,
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ onOverlayClick: _react.PropTypes.func,
+ permanentAt: _react.PropTypes.oneOf(['sm', 'md', 'lg', 'xl', 'xxl', 'xxxl']),
+ pinned: _react.PropTypes.bool,
+ scrollY: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ drawerContent: _react.PropTypes.string,
+ lgPermanent: _react.PropTypes.string,
+ mdPermanent: _react.PropTypes.string,
+ navDrawer: _react.PropTypes.string,
+ pinned: _react.PropTypes.string,
+ scrim: _react.PropTypes.string,
+ scrollY: _react.PropTypes.string,
+ smPermanent: _react.PropTypes.string,
+ wide: _react.PropTypes.string,
+ xlPermanent: _react.PropTypes.string,
+ xxlPermanent: _react.PropTypes.string,
+ xxxlPermanent: _react.PropTypes.string
+ }),
+ width: _react.PropTypes.oneOf(['normal', 'wide'])
+};
+
+NavDrawer.defaultProps = {
+ active: false,
+ className: '',
+ scrollY: false,
+ width: 'normal'
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LAYOUT)(NavDrawer);
+exports.NavDrawer = NavDrawer;
\ No newline at end of file
diff --git a/lib/layout/Panel.js b/lib/layout/Panel.js
new file mode 100644
index 000000000..bbdb2dfe3
--- /dev/null
+++ b/lib/layout/Panel.js
@@ -0,0 +1,55 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Panel = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+var Panel = function Panel(_ref) {
+ var children = _ref.children;
+ var className = _ref.className;
+ var scrollY = _ref.scrollY;
+ var theme = _ref.theme;
+
+ var _className = (0, _classnames3.default)(theme.panel, _defineProperty({}, theme.scrollY, scrollY), className);
+
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'panel', className: _className },
+ children
+ );
+};
+
+Panel.propTypes = {
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ scrollY: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ panel: _react.PropTypes.string,
+ scrollY: _react.PropTypes.string
+ })
+};
+
+Panel.defaultProps = {
+ className: '',
+ scrollY: false
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LAYOUT)(Panel);
+exports.Panel = Panel;
\ No newline at end of file
diff --git a/lib/layout/Sidebar.js b/lib/layout/Sidebar.js
new file mode 100644
index 000000000..13e171218
--- /dev/null
+++ b/lib/layout/Sidebar.js
@@ -0,0 +1,69 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Sidebar = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames3 = require('classnames');
+
+var _classnames4 = _interopRequireDefault(_classnames3);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+var Sidebar = function Sidebar(_ref) {
+ var children = _ref.children;
+ var className = _ref.className;
+ var pinned = _ref.pinned;
+ var scrollY = _ref.scrollY;
+ var theme = _ref.theme;
+ var width = _ref.width;
+
+ var wrapperClasses = (0, _classnames4.default)(theme.sidebar, theme['width-' + width], _defineProperty({}, theme.pinned, pinned), className);
+
+ var innerClasses = (0, _classnames4.default)(theme.sidebarContent, _defineProperty({}, theme.scrollY, scrollY));
+
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'sidebar', className: wrapperClasses },
+ _react2.default.createElement(
+ 'aside',
+ { 'data-react-toolbox': 'sidebar-content', className: innerClasses },
+ children
+ )
+ );
+};
+
+Sidebar.propTypes = {
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ pinned: _react.PropTypes.bool,
+ scrollY: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ pinned: _react.PropTypes.string,
+ scrollY: _react.PropTypes.string,
+ sidebar: _react.PropTypes.string,
+ sidebarContent: _react.PropTypes.string
+ }),
+ width: _react.PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 25, 33, 50, 66, 75, 100])
+};
+
+Sidebar.defaultProps = {
+ className: '',
+ pinned: false,
+ scrollY: false,
+ width: 5
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LAYOUT)(Sidebar);
+exports.Sidebar = Sidebar;
\ No newline at end of file
diff --git a/lib/layout/_config.scss b/lib/layout/_config.scss
new file mode 100644
index 000000000..a1b318f7a
--- /dev/null
+++ b/lib/layout/_config.scss
@@ -0,0 +1,18 @@
+$drawer-background-color: $palette-grey-50 !default;
+$drawer-border-color: $palette-grey-300 !default;
+$drawer-text-color: $palette-grey-800 !default;
+
+$drawer-overlay-color: $color-black !default;
+$drawer-overlay-opacity: .6 !default;
+
+// from: https://www.google.com/design/spec/layout/structure.html#structure-side-nav
+$navigation-drawer-desktop-width: 5 * $standard-increment-desktop !default;
+$navigation-drawer-max-desktop-width: 40 * $unit !default;
+
+// Mobile:
+// Width = Screen width − 56 dp
+// Maximum width: 320dp
+$navigation-drawer-mobile-width: 5 * $standard-increment-mobile !default;
+
+// sass doesn't like use of variable here: calc(100% - $standard-increment-mobile);
+$navigation-drawer-max-mobile-width: calc(100% - 5.6rem) !default;
diff --git a/lib/layout/_mixins.scss b/lib/layout/_mixins.scss
new file mode 100644
index 000000000..1ace8de42
--- /dev/null
+++ b/lib/layout/_mixins.scss
@@ -0,0 +1,29 @@
+@mixin open() {
+ transition-delay: $animation-delay;
+ & > .scrim {
+ & > .drawerContent {
+ pointer-events: all;
+ transition-delay: $animation-delay;
+ transform: translateX(0);
+ }
+ }
+}
+
+@mixin permanent() {
+ @include open();
+
+ width: $navigation-drawer-desktop-width;
+ max-width: $navigation-drawer-desktop-width;
+
+ &.wide {
+ width: $navigation-drawer-max-desktop-width;
+ max-width: $navigation-drawer-max-desktop-width;
+ }
+
+ &.active {
+ & > .scrim {
+ width: 0;
+ background-color: rgba($drawer-overlay-color, 0);
+ }
+ }
+}
diff --git a/lib/layout/index.js b/lib/layout/index.js
new file mode 100644
index 000000000..47bfa886a
--- /dev/null
+++ b/lib/layout/index.js
@@ -0,0 +1,34 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Sidebar = exports.NavDrawer = exports.Panel = exports.Layout = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Layout = require('./Layout.js');
+
+var _Panel = require('./Panel.js');
+
+var _NavDrawer = require('./NavDrawer.js');
+
+var _Sidebar = require('./Sidebar.js');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ThemedLayout = (0, _reactCssThemr.themr)(_identifiers.LAYOUT, _theme2.default)(_Layout.Layout);
+var ThemedPanel = (0, _reactCssThemr.themr)(_identifiers.LAYOUT, _theme2.default)(_Panel.Panel);
+var ThemedNavDrawer = (0, _reactCssThemr.themr)(_identifiers.LAYOUT, _theme2.default)(_NavDrawer.NavDrawer);
+var ThemedSidebar = (0, _reactCssThemr.themr)(_identifiers.LAYOUT, _theme2.default)(_Sidebar.Sidebar);
+
+exports.Layout = ThemedLayout;
+exports.Panel = ThemedPanel;
+exports.NavDrawer = ThemedNavDrawer;
+exports.Sidebar = ThemedSidebar;
\ No newline at end of file
diff --git a/lib/layout/theme.scss b/lib/layout/theme.scss
new file mode 100644
index 000000000..a07c02924
--- /dev/null
+++ b/lib/layout/theme.scss
@@ -0,0 +1,311 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+@import "./mixins";
+
+.layout {
+ position: relative;
+ display: flex;
+ width: 100%;
+ height: 100%;
+ flex-direction: row;
+ align-items: stretch;
+ justify-content: space-between;
+ overflow-y: hidden;
+
+ .navDrawer {
+ width: 0;
+ min-width: 0;
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transition-property: width, min-width;
+
+ .scrim {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ z-index: $z-index-highest;
+ width: 0;
+ height: 100%;
+ background-color: rgba($drawer-overlay-color, 0);
+ transition: background-color $animation-duration $animation-curve-default, width 10ms linear $animation-duration;
+ }
+
+ .drawerContent {
+ @include shadow-2dp();
+ display: flex;
+ width: $navigation-drawer-mobile-width;
+ max-width: $navigation-drawer-max-mobile-width;
+ height: 100%;
+ flex-direction: column;
+ align-items: stretch;
+ justify-content: space-between;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ color: $drawer-text-color;
+ pointer-events: none;
+ background-color: $drawer-background-color;
+ border-right: 1px solid $drawer-border-color;
+ transition: transform $animation-duration $animation-curve-default;
+ transform: translateX(-100%);
+
+ &.scrollY {
+ overflow-y: auto;
+ }
+ }
+
+ &.pinned {
+ @include open();
+ width: $navigation-drawer-mobile-width;
+ max-width: $navigation-drawer-max-mobile-width;
+ }
+
+ &.active {
+ &:not(.pinned) {
+ @include open();
+ .scrim {
+ width: 100%;
+ background-color: rgba($drawer-overlay-color, $drawer-overlay-opacity);
+ transition: background-color $animation-duration $animation-curve-default;
+ }
+ }
+ }
+
+ // Larger than mobile width drawer
+ @media screen and (min-width: $layout-breakpoint-xs) {
+ &.pinned {
+ width: $navigation-drawer-desktop-width;
+ max-width: $navigation-drawer-desktop-width;
+ }
+
+ .drawerContent {
+ width: $navigation-drawer-desktop-width;
+ max-width: $navigation-drawer-desktop-width;
+ }
+
+ &.wide {
+ &.pinned {
+ width: $navigation-drawer-max-desktop-width;
+ max-width: $navigation-drawer-max-desktop-width;
+ }
+
+ .drawerContent {
+ width: $navigation-drawer-max-desktop-width;
+ max-width: $navigation-drawer-max-desktop-width;
+ }
+ }
+ }
+
+ // Permanent screen, ignore .active and make part of layout
+ @media screen and (min-width: $layout-breakpoint-sm) {
+ &.smPermanent {
+ @include permanent();
+ }
+ }
+
+ @media screen and (min-width: $layout-breakpoint-md) {
+ &.mdPermanent {
+ @include permanent();
+ }
+ }
+
+ @media screen and (min-width: $layout-breakpoint-lg) {
+ &.lgPermanent {
+ @include permanent();
+ }
+ }
+
+ @media screen and (min-width: $layout-breakpoint-xl) {
+ &.xlPermanent {
+ @include permanent();
+ }
+ }
+
+ @media screen and (min-width: $layout-breakpoint-xxl) {
+ &.xxlPermanent {
+ @include permanent();
+ }
+ }
+
+ @media screen and (min-width: $layout-breakpoint-xxxl) {
+ &.xxxlPermanent {
+ @include permanent();
+ }
+ }
+ }
+
+ & .layout {
+ .scrim {
+ z-index: $z-index-highest - 1;
+ }
+ & .layout {
+ .scrim {
+ z-index: $z-index-highest - 2;
+ }
+ }
+ }
+
+ .panel {
+ position: relative;
+ display: flex;
+ height: 100%;
+ flex: 1;
+ flex-direction: column;
+ align-items: stretch;
+ justify-content: space-between;
+ overflow-y: hidden;
+
+ &.scrollY {
+ overflow-y: auto;
+ }
+ }
+
+ .sidebar {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ z-index: $z-index-highest - 1;
+ width: 0;
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ color: $drawer-text-color;
+ background-color: $drawer-background-color;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transition-property: width;
+
+ .sidebarContent {
+ display: flex;
+ height: 100%;
+ flex-direction: column;
+ align-items: stretch;
+ justify-content: space-between;
+ overflow-y: hidden;
+
+ &.scrollY {
+ overflow-y: auto;
+ }
+ }
+
+ $increments: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
+
+ @each $increment in $increments {
+ &.width-#{$increment} {
+ $mobile: $standard-increment-mobile * $increment;
+ $desktop: $standard-increment-desktop * $increment;
+
+ .sidebarContent {
+ min-width: 100%;
+ }
+
+ &.pinned {
+ width: 100%;
+ }
+
+ @if $increment < 10 {
+ @media screen and (min-width: $layout-breakpoint-xs) and (orientation: landscape) {
+ position: relative;
+
+ .sidebarContent {
+ min-width: $mobile;
+ }
+
+ &.pinned {
+ width: $mobile;
+ }
+ }
+
+ @media screen and (min-width: $layout-breakpoint-xs) and (orientation: portrait) {
+ position: relative;
+
+ .sidebarContent {
+ min-width: $desktop;
+ }
+
+ &.pinned {
+ width: $desktop;
+ }
+ }
+ }
+
+ @if $increment < 11 {
+ @media screen and (min-width: $layout-breakpoint-sm-tablet) {
+ position: relative;
+
+ .sidebarContent {
+ min-width: $desktop;
+ }
+
+ &.pinned {
+ width: $desktop;
+ }
+ }
+ }
+
+ @media screen and (min-width: $layout-breakpoint-sm) {
+ position: relative;
+
+ .sidebarContent {
+ min-width: $desktop;
+ }
+
+ &.pinned {
+ width: $desktop;
+ }
+ }
+ }
+ }
+
+
+ $percentages: (25, 33, 50, 66, 75);
+
+ &.width-100 {
+ position: absolute;
+
+ .sidebarContent {
+ min-width: 100%;
+ }
+
+ &.pinned {
+ width: 100%;
+ }
+ }
+
+ @each $pct in $percentages {
+ &.width-#{$pct} {
+ position: absolute;
+
+ .sidebarContent {
+ min-width: 100%;
+ }
+
+ &.pinned {
+ width: 100%;
+ }
+ }
+ }
+
+ @media screen and (min-width: $layout-breakpoint-sm-tablet) {
+ @each $pct in $percentages {
+ &.width-#{$pct} {
+ position: relative;
+
+ .sidebarContent {
+ min-width: $pct * 1%;
+ }
+
+ &.pinned {
+ width: $pct * 1%;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/link/Link.js b/lib/link/Link.js
new file mode 100644
index 000000000..8a6f4b738
--- /dev/null
+++ b/lib/link/Link.js
@@ -0,0 +1,83 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Link = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _FontIcon = require('../font_icon/FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var Link = function Link(_ref) {
+ var active = _ref.active;
+ var children = _ref.children;
+ var className = _ref.className;
+ var count = _ref.count;
+ var icon = _ref.icon;
+ var label = _ref.label;
+ var theme = _ref.theme;
+
+ var others = _objectWithoutProperties(_ref, ['active', 'children', 'className', 'count', 'icon', 'label', 'theme']);
+
+ var _className = (0, _classnames3.default)(theme.link, _defineProperty({}, theme.active, active), className);
+
+ return _react2.default.createElement(
+ 'a',
+ _extends({ 'data-react-toolbox': 'link', className: _className }, others),
+ icon ? _react2.default.createElement(_FontIcon2.default, { className: theme.icon, value: icon }) : null,
+ label ? _react2.default.createElement(
+ 'abbr',
+ null,
+ label
+ ) : null,
+ count && parseInt(count) !== 0 ? _react2.default.createElement(
+ 'small',
+ null,
+ count
+ ) : null,
+ children
+ );
+};
+
+Link.propTypes = {
+ active: _react.PropTypes.bool,
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ count: _react.PropTypes.number,
+ icon: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ label: _react.PropTypes.string,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ icon: _react.PropTypes.string,
+ link: _react.PropTypes.string
+ })
+};
+
+Link.defaultProps = {
+ active: false,
+ className: ''
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LINK)(Link);
+exports.Link = Link;
\ No newline at end of file
diff --git a/lib/link/index.js b/lib/link/index.js
new file mode 100644
index 000000000..a0e50a865
--- /dev/null
+++ b/lib/link/index.js
@@ -0,0 +1,23 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Link = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Link = require('./Link.js');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ThemedLink = (0, _reactCssThemr.themr)(_identifiers.LINK, _theme2.default)(_Link.Link);
+
+exports.default = ThemedLink;
+exports.Link = ThemedLink;
\ No newline at end of file
diff --git a/lib/link/theme.scss b/lib/link/theme.scss
new file mode 100644
index 000000000..11eb90844
--- /dev/null
+++ b/lib/link/theme.scss
@@ -0,0 +1,37 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+
+.icon {
+ margin-right: $unit;
+ font-size: 1.8 * $unit;
+}
+
+.link {
+ position: relative;
+ display: flex;
+ flex-direction: row;
+ align-content: center;
+ align-items: center;
+ justify-content: center;
+ line-height: 1.5;
+ cursor: pointer;
+ transition: opacity $animation-duration $animation-curve-default;
+ &:not(.active) {
+ opacity: .5;
+ }
+ &:hover, &:active {
+ opacity: 1;
+ }
+ > * {
+ vertical-align: middle;
+ }
+ > abbr {
+ text-transform: capitalize;
+ }
+ > small {
+ margin-left: .8 * $unit;
+ font-size: $font-size-tiny;
+ text-align: center;
+ }
+}
diff --git a/lib/list/List.js b/lib/list/List.js
new file mode 100644
index 000000000..cd315c954
--- /dev/null
+++ b/lib/list/List.js
@@ -0,0 +1,96 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.List = exports.listFactory = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _ListItem = require('./ListItem.js');
+
+var _ListItem2 = _interopRequireDefault(_ListItem);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(ListItem) {
+ var List = function (_Component) {
+ _inherits(List, _Component);
+
+ function List() {
+ _classCallCheck(this, List);
+
+ return _possibleConstructorReturn(this, Object.getPrototypeOf(List).apply(this, arguments));
+ }
+
+ _createClass(List, [{
+ key: 'renderItems',
+ value: function renderItems() {
+ var _this2 = this;
+
+ return _react2.default.Children.map(this.props.children, function (item) {
+ if (item.type === ListItem) {
+ return _react2.default.cloneElement(item, {
+ ripple: _this2.props.ripple,
+ selectable: _this2.props.selectable
+ });
+ } else {
+ return _react2.default.cloneElement(item);
+ }
+ });
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ return _react2.default.createElement(
+ 'ul',
+ { 'data-react-toolbox': 'list', className: (0, _classnames2.default)(this.props.theme.list, this.props.className) },
+ this.renderItems()
+ );
+ }
+ }]);
+
+ return List;
+ }(_react.Component);
+
+ List.propTypes = {
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ ripple: _react.PropTypes.bool,
+ selectable: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ list: _react.PropTypes.string
+ })
+ };
+ List.defaultProps = {
+ className: '',
+ ripple: false,
+ selectable: false
+ };
+
+
+ return List;
+};
+
+var List = factory(_ListItem2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LIST)(List);
+exports.listFactory = factory;
+exports.List = List;
\ No newline at end of file
diff --git a/lib/list/ListCheckbox.js b/lib/list/ListCheckbox.js
new file mode 100644
index 000000000..ca8a5f287
--- /dev/null
+++ b/lib/list/ListCheckbox.js
@@ -0,0 +1,93 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ListCheckbox = exports.listCheckboxFactory = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Checkbox = require('../checkbox/Checkbox.js');
+
+var _Checkbox2 = _interopRequireDefault(_Checkbox);
+
+var _ListItemContent = require('./ListItemContent.js');
+
+var _ListItemContent2 = _interopRequireDefault(_ListItemContent);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+var factory = function factory(Checkbox, ListItemContent) {
+ var ListCheckbox = function ListCheckbox(_ref) {
+ var caption = _ref.caption;
+ var checked = _ref.checked;
+ var className = _ref.className;
+ var disabled = _ref.disabled;
+ var legend = _ref.legend;
+ var name = _ref.name;
+ var onBlur = _ref.onBlur;
+ var onChange = _ref.onChange;
+ var onFocus = _ref.onFocus;
+ var theme = _ref.theme;
+
+ var _className = (0, _classnames3.default)(theme.item, theme.checkboxItem, _defineProperty({}, theme.disabled, disabled), className);
+
+ return _react2.default.createElement(
+ 'li',
+ { className: _className },
+ _react2.default.createElement(Checkbox, {
+ checked: checked,
+ className: theme.checkbox,
+ disabled: disabled,
+ label: _react2.default.createElement(ListItemContent, { caption: caption, legend: legend }),
+ name: name,
+ onBlur: onBlur,
+ onChange: onChange,
+ onFocus: onFocus
+ })
+ );
+ };
+
+ ListCheckbox.propTypes = {
+ caption: _react.PropTypes.string,
+ checked: _react.PropTypes.bool,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ legend: _react.PropTypes.string,
+ name: _react.PropTypes.string,
+ onBlur: _react.PropTypes.func,
+ onChange: _react.PropTypes.func,
+ onFocus: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ checkbox: _react.PropTypes.string,
+ checkboxItem: _react.PropTypes.string,
+ disabled: _react.PropTypes.string,
+ item: _react.PropTypes.string
+ })
+ };
+
+ ListCheckbox.defaultProps = {
+ checked: false,
+ disabled: false
+ };
+
+ return ListCheckbox;
+};
+
+var ListCheckbox = factory(_Checkbox2.default, _ListItemContent2.default);
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LIST)(ListCheckbox);
+exports.listCheckboxFactory = factory;
+exports.ListCheckbox = ListCheckbox;
\ No newline at end of file
diff --git a/lib/list/ListDivider.js b/lib/list/ListDivider.js
new file mode 100644
index 000000000..b9f71c064
--- /dev/null
+++ b/lib/list/ListDivider.js
@@ -0,0 +1,37 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ListDivider = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ListDivider = function ListDivider(_ref) {
+ var inset = _ref.inset;
+ var theme = _ref.theme;
+ return _react2.default.createElement('hr', { className: inset ? theme.divider + ' ' + theme.inset : theme.divider });
+};
+
+ListDivider.propTypes = {
+ inset: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ divider: _react.PropTypes.string,
+ inset: _react.PropTypes.string
+ })
+};
+
+ListDivider.defaultProps = {
+ inset: false
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LIST)(ListDivider);
+exports.ListDivider = ListDivider;
\ No newline at end of file
diff --git a/lib/list/ListItem.js b/lib/list/ListItem.js
new file mode 100644
index 000000000..174454028
--- /dev/null
+++ b/lib/list/ListItem.js
@@ -0,0 +1,149 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ListItem = exports.listItemFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _ListItemContent = require('./ListItemContent.js');
+
+var _ListItemContent2 = _interopRequireDefault(_ListItemContent);
+
+var _ListItemLayout = require('./ListItemLayout.js');
+
+var _ListItemLayout2 = _interopRequireDefault(_ListItemLayout);
+
+var _Ripple = require('../ripple/Ripple.js');
+
+var _Ripple2 = _interopRequireDefault(_Ripple);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(ripple, ListItemLayout, ListItemContent) {
+ var ListItem = function (_Component) {
+ _inherits(ListItem, _Component);
+
+ function ListItem() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, ListItem);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(ListItem)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleClick = function (event) {
+ if (_this.props.onClick && !_this.props.disabled) {
+ _this.props.onClick(event);
+ }
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(ListItem, [{
+ key: 'groupChildren',
+ value: function groupChildren() {
+ var children = {
+ leftActions: [],
+ rightActions: [],
+ ignored: []
+ };
+
+ _react2.default.Children.forEach(this.props.children, function (child, i) {
+ if (!_react2.default.isValidElement(child)) {
+ return;
+ }
+ if (child.props.listItemIgnore) {
+ children.ignored.push(child);
+ return;
+ }
+ if (child.type === ListItemContent) {
+ children.itemContent = child;
+ return;
+ }
+ var bucket = children.itemContent ? 'rightActions' : 'leftActions';
+ children[bucket].push(_extends({}, child, { key: i }));
+ });
+
+ return children;
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var className = _props.className;
+ var onMouseDown = _props.onMouseDown;
+ var to = _props.to;
+ var onClick = _props.onClick;
+ var hasRipple = _props.ripple;
+ var theme = _props.theme;
+
+ var other = _objectWithoutProperties(_props, ['className', 'onMouseDown', 'to', 'onClick', 'ripple', 'theme']); //eslint-disable-line no-unused-vars
+
+
+ var children = this.groupChildren();
+ var content = _react2.default.createElement(ListItemLayout, _extends({}, children, other));
+ return _react2.default.createElement(
+ 'li',
+ { className: theme.listItem + ' ' + className, onClick: this.handleClick, onMouseDown: onMouseDown },
+ to ? _react2.default.createElement(
+ 'a',
+ { href: this.props.to },
+ content
+ ) : content,
+ children.ignored
+ );
+ }
+ }]);
+
+ return ListItem;
+ }(_react.Component);
+
+ ListItem.propTypes = {
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ onClick: _react.PropTypes.func,
+ ripple: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ listItem: _react.PropTypes.string
+ }),
+ to: _react.PropTypes.string
+ };
+ ListItem.defaultProps = {
+ className: '',
+ disabled: false,
+ ripple: false
+ };
+
+
+ return ripple(ListItem);
+};
+
+var ripple = (0, _Ripple2.default)({ centered: false, listItemIgnore: true });
+var ListItem = factory(ripple, _ListItemLayout2.default, _ListItemContent2.default);
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LIST)(ListItem);
+exports.listItemFactory = factory;
+exports.ListItem = ListItem;
\ No newline at end of file
diff --git a/lib/list/ListItemAction.js b/lib/list/ListItemAction.js
new file mode 100644
index 000000000..66e6532da
--- /dev/null
+++ b/lib/list/ListItemAction.js
@@ -0,0 +1,44 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ListItemAction = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ListItemAction = function ListItemAction(_ref) {
+ var action = _ref.action;
+ var theme = _ref.theme;
+ var _action$props = action.props;
+ var onClick = _action$props.onClick;
+ var onMouseDown = _action$props.onMouseDown;
+
+ var stopRipple = onClick && !onMouseDown;
+ var stop = function stop(e) {
+ return e.stopPropagation();
+ };
+ return _react2.default.createElement(
+ 'span',
+ { className: theme.itemAction, onMouseDown: stopRipple && stop, onClick: onClick && stop },
+ action
+ );
+};
+
+ListItemAction.propTypes = {
+ action: _react.PropTypes.object,
+ theme: _react.PropTypes.shape({
+ itemAction: _react.PropTypes.string
+ })
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LIST)(ListItemAction);
+exports.ListItemAction = ListItemAction;
\ No newline at end of file
diff --git a/lib/list/ListItemActions.js b/lib/list/ListItemActions.js
new file mode 100644
index 000000000..f04cbdf21
--- /dev/null
+++ b/lib/list/ListItemActions.js
@@ -0,0 +1,56 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ListItemActions = exports.listItemActionsFactory = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _ListItemAction = require('./ListItemAction.js');
+
+var _ListItemAction2 = _interopRequireDefault(_ListItemAction);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var factory = function factory(ListItemAction) {
+ var ListItemActions = function ListItemActions(_ref) {
+ var type = _ref.type;
+ var children = _ref.children;
+ var theme = _ref.theme;
+
+ var validChildren = _react2.default.Children.toArray(children).filter(function (c) {
+ return _react2.default.isValidElement(c);
+ });
+
+ return _react2.default.createElement(
+ 'span',
+ { className: theme[type] },
+ validChildren.map(function (action, i) {
+ return _react2.default.createElement(ListItemAction, { key: i, action: action });
+ })
+ );
+ };
+
+ ListItemActions.propTypes = {
+ children: _react.PropTypes.any,
+ theme: _react.PropTypes.shape({
+ left: _react.PropTypes.string,
+ right: _react.PropTypes.string
+ }),
+ type: _react.PropTypes.oneOf(['left', 'right'])
+ };
+
+ return ListItemActions;
+};
+
+var ListItemActions = factory(_ListItemAction2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LIST)(ListItemActions);
+exports.listItemActionsFactory = factory;
+exports.ListItemActions = ListItemActions;
\ No newline at end of file
diff --git a/lib/list/ListItemContent.js b/lib/list/ListItemContent.js
new file mode 100644
index 000000000..9202ea828
--- /dev/null
+++ b/lib/list/ListItemContent.js
@@ -0,0 +1,116 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ListItemContent = exports.listItemContentFactory = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _ListItemText = require('./ListItemText.js');
+
+var _ListItemText2 = _interopRequireDefault(_ListItemText);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var types = ['auto', 'normal', 'large'];
+
+var factory = function factory(ListItemText) {
+ var ListItemContent = function (_Component) {
+ _inherits(ListItemContent, _Component);
+
+ function ListItemContent() {
+ _classCallCheck(this, ListItemContent);
+
+ return _possibleConstructorReturn(this, Object.getPrototypeOf(ListItemContent).apply(this, arguments));
+ }
+
+ _createClass(ListItemContent, [{
+ key: 'getType',
+ value: function getType() {
+ var _props = this.props;
+ var type = _props.type;
+ var children = _props.children;
+ var caption = _props.caption;
+ var legend = _props.legend;
+
+
+ var count = _react2.default.Children.count(children);
+ [caption, legend].forEach(function (s) {
+ count += s ? 1 : 0;
+ });
+ var typeIndex = Math.min(count, types.length);
+
+ return type || types[typeIndex];
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props2 = this.props;
+ var children = _props2.children;
+ var caption = _props2.caption;
+ var legend = _props2.legend;
+ var theme = _props2.theme;
+
+ var className = (0, _classnames3.default)(theme.itemContentRoot, _defineProperty({}, theme[this.getType()], theme[this.getType()]));
+
+ return _react2.default.createElement(
+ 'span',
+ { className: className },
+ caption && _react2.default.createElement(
+ ListItemText,
+ { primary: true },
+ caption
+ ),
+ legend && _react2.default.createElement(
+ ListItemText,
+ null,
+ legend
+ ),
+ children
+ );
+ }
+ }]);
+
+ return ListItemContent;
+ }(_react.Component);
+
+ ListItemContent.propTypes = {
+ caption: _react.PropTypes.string,
+ children: _react.PropTypes.any,
+ legend: _react.PropTypes.string,
+ theme: _react.PropTypes.shape({
+ itemContentRoot: _react.PropTypes.string,
+ large: _react.PropTypes.string
+ }),
+ type: _react.PropTypes.oneOf(types)
+ };
+
+
+ return ListItemContent;
+};
+
+var ListItemContent = (0, _reactCssThemr.themr)(_identifiers.LIST)(_ListItemText2.default);
+exports.default = ListItemContent;
+exports.listItemContentFactory = factory;
+exports.ListItemContent = ListItemContent;
\ No newline at end of file
diff --git a/lib/list/ListItemLayout.js b/lib/list/ListItemLayout.js
new file mode 100644
index 000000000..46ea8a5bc
--- /dev/null
+++ b/lib/list/ListItemLayout.js
@@ -0,0 +1,104 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ListItemLayout = exports.listItemLayoutFactory = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _FontIcon = require('../font_icon/FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+var _Avatar = require('../avatar/Avatar.js');
+
+var _Avatar2 = _interopRequireDefault(_Avatar);
+
+var _ListItemContent = require('./ListItemContent.js');
+
+var _ListItemContent2 = _interopRequireDefault(_ListItemContent);
+
+var _ListItemActions = require('./ListItemActions.js');
+
+var _ListItemActions2 = _interopRequireDefault(_ListItemActions);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+var factory = function factory(Avatar, ListItemContent, ListItemActions) {
+ var ListItemLayout = function ListItemLayout(props) {
+ var _classnames;
+
+ var className = (0, _classnames3.default)(props.theme.item, (_classnames = {}, _defineProperty(_classnames, props.theme.disabled, props.disabled), _defineProperty(_classnames, props.theme.selectable, props.selectable), _classnames), props.className);
+
+ var leftActions = [props.leftIcon && _react2.default.createElement(_FontIcon2.default, { value: props.leftIcon, key: 'leftIcon' }), props.avatar && _react2.default.createElement(Avatar, { image: props.avatar, key: 'avatar' })].concat(_toConsumableArray(props.leftActions));
+ var rightActions = [props.rightIcon && _react2.default.createElement(_FontIcon2.default, { value: props.rightIcon, key: 'rightIcon' })].concat(_toConsumableArray(props.rightActions));
+ var content = props.itemContent || _react2.default.createElement(ListItemContent, { caption: props.caption, legend: props.legend });
+ var emptyActions = function emptyActions(item) {
+ return !item[0] && !item[1] && !item[2];
+ };
+
+ return _react2.default.createElement(
+ 'span',
+ { className: className },
+ !emptyActions(leftActions) > 0 && _react2.default.createElement(
+ ListItemActions,
+ { type: 'left' },
+ leftActions
+ ),
+ content,
+ !emptyActions(rightActions) > 0 && _react2.default.createElement(
+ ListItemActions,
+ { type: 'right' },
+ rightActions
+ )
+ );
+ };
+
+ ListItemLayout.propTypes = {
+ avatar: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ caption: _react.PropTypes.string,
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ itemContent: _react.PropTypes.element,
+ leftActions: _react.PropTypes.array,
+ leftIcon: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ legend: _react.PropTypes.string,
+ rightActions: _react.PropTypes.array,
+ rightIcon: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ selectable: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ disabled: _react.PropTypes.string,
+ item: _react.PropTypes.string,
+ selectable: _react.PropTypes.string
+ }),
+ to: _react.PropTypes.string
+ };
+
+ ListItemLayout.defaultProps = {
+ disabled: false,
+ selectable: false
+ };
+
+ return ListItemLayout;
+};
+
+var ListItemLayout = factory(_Avatar2.default, _ListItemContent2.default, _ListItemActions2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LIST)(ListItemLayout);
+exports.listItemLayoutFactory = factory;
+exports.ListItemLayout = ListItemLayout;
\ No newline at end of file
diff --git a/lib/list/ListItemText.js b/lib/list/ListItemText.js
new file mode 100644
index 000000000..278fc2e99
--- /dev/null
+++ b/lib/list/ListItemText.js
@@ -0,0 +1,59 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ListItemText = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var ListItemText = function ListItemText(_ref) {
+ var className = _ref.className;
+ var primary = _ref.primary;
+ var children = _ref.children;
+ var theme = _ref.theme;
+
+ var other = _objectWithoutProperties(_ref, ['className', 'primary', 'children', 'theme']);
+
+ var _className = (0, _classnames3.default)(theme.itemText, _defineProperty({}, theme.primary, primary), className);
+ return _react2.default.createElement(
+ 'span',
+ _extends({ 'data-react-toolbox': 'list-item-text', className: _className }, other),
+ children
+ );
+};
+
+ListItemText.propTypes = {
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ primary: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ itemText: _react.PropTypes.string,
+ primary: _react.PropTypes.string
+ })
+};
+
+ListItemText.defaultProps = {
+ primary: false
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LIST)(ListItemText);
+exports.ListItemText = ListItemText;
\ No newline at end of file
diff --git a/lib/list/ListSubHeader.js b/lib/list/ListSubHeader.js
new file mode 100644
index 000000000..a0a96355b
--- /dev/null
+++ b/lib/list/ListSubHeader.js
@@ -0,0 +1,44 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ListSubHeader = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ListSubHeader = function ListSubHeader(_ref) {
+ var caption = _ref.caption;
+ var className = _ref.className;
+ var theme = _ref.theme;
+ return _react2.default.createElement(
+ 'h5',
+ { className: (0, _classnames2.default)(theme.subheader, className) },
+ caption
+ );
+};
+
+ListSubHeader.propTypes = {
+ caption: _react.PropTypes.string,
+ className: _react.PropTypes.string,
+ theme: _react.PropTypes.object
+};
+
+ListSubHeader.defaultProps = {
+ className: ''
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.LIST)(ListSubHeader);
+exports.ListSubHeader = ListSubHeader;
\ No newline at end of file
diff --git a/lib/list/_config.scss b/lib/list/_config.scss
new file mode 100644
index 000000000..a8814551f
--- /dev/null
+++ b/lib/list/_config.scss
@@ -0,0 +1,18 @@
+$list-vertical-padding: .8 * $unit !default;
+$list-horizontal-padding: 1.6 * $unit !default;
+$list-content-left-spacing: 7.2 * $unit !default;
+$list-subheader-height: 4.8 * $unit !default;
+$list-subheader-font-size: 1.4 * $unit !default;
+$list-subheader-font-weight: 500 !default;
+$list-divider-height: .1 * $unit !default;
+$list-item-min-height: 4.8 * $unit !default;
+$list-item-min-height-legend: 7.2 * $unit !default;
+$list-item-hover-color: $palette-grey-200 !default;
+$list-item-legend-margin-top: .3 * $unit !default;
+$list-item-icon-font-size: 2.4 * $unit !default;
+$list-item-icon-size: 1.8 * $unit !default;
+$list-item-right-icon-margin: $list-content-left-spacing - 2 * $list-horizontal-padding - $list-item-icon-size !default;
+$list-item-right-checkbox-margin: $list-item-right-icon-margin + $list-horizontal-padding !default;
+$list-item-avatar-height: 4 * $unit !default;
+$list-item-avatar-margin: .8 * $unit !default;
+$list-item-child-margin: .8 * $unit !default;
diff --git a/lib/list/index.js b/lib/list/index.js
new file mode 100644
index 000000000..a7445462f
--- /dev/null
+++ b/lib/list/index.js
@@ -0,0 +1,69 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.List = exports.ListItem = exports.ListDivider = exports.ListCheckbox = exports.ListItemText = exports.ListSubHeader = exports.ListItemLayout = exports.ListItemContent = exports.ListItemActions = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _avatar = require('../avatar');
+
+var _checkbox = require('../checkbox');
+
+var _ListItemText = require('./ListItemText.js');
+
+var _ListItemAction = require('./ListItemAction.js');
+
+var _ListSubHeader = require('./ListSubHeader.js');
+
+var _ListDivider = require('./ListDivider.js');
+
+var _List = require('./List.js');
+
+var _ListItem = require('./ListItem.js');
+
+var _ListCheckbox = require('./ListCheckbox.js');
+
+var _ListItemActions = require('./ListItemActions.js');
+
+var _ListItemContent = require('./ListItemContent.js');
+
+var _ListItemLayout = require('./ListItemLayout.js');
+
+var _ripple = require('../ripple');
+
+var _ripple2 = _interopRequireDefault(_ripple);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var applyTheme = function applyTheme(Component) {
+ return (0, _reactCssThemr.themr)(_identifiers.LIST, _theme2.default)(Component);
+};
+var ripple = (0, _ripple2.default)({ centered: false, listItemIgnore: true });
+var ThemedListItemAction = applyTheme(_ListItemAction.ListItemAction);
+var ThemedListSubHeader = applyTheme(_ListSubHeader.ListSubHeader);
+var ThemedListItemText = applyTheme(_ListItemText.ListItemText);
+var ThemedListDivider = applyTheme(_ListDivider.ListDivider);
+var ThemedListItemContent = applyTheme((0, _ListItemContent.listItemContentFactory)(ThemedListItemText));
+var ThemedListItemActions = applyTheme((0, _ListItemActions.listItemActionsFactory)(ThemedListItemAction));
+var ThemedListItemLayout = applyTheme((0, _ListItemLayout.listItemLayoutFactory)(_avatar.Avatar, ThemedListItemContent, ThemedListItemActions));
+var ThemedListCheckbox = applyTheme((0, _ListCheckbox.listCheckboxFactory)(_checkbox.Checkbox, ThemedListItemContent));
+var ThemedListItem = applyTheme((0, _ListItem.listItemFactory)(ripple, ThemedListItemLayout, ThemedListItemContent));
+var ThemedList = applyTheme((0, _List.listFactory)(ThemedListItem));
+
+exports.ListItemActions = ThemedListItemActions;
+exports.ListItemContent = ThemedListItemContent;
+exports.ListItemLayout = ThemedListItemLayout;
+exports.ListSubHeader = ThemedListSubHeader;
+exports.ListItemText = ThemedListItemText;
+exports.ListCheckbox = ThemedListCheckbox;
+exports.ListDivider = ThemedListDivider;
+exports.ListItem = ThemedListItem;
+exports.List = ThemedList;
\ No newline at end of file
diff --git a/lib/list/theme.scss b/lib/list/theme.scss
new file mode 100644
index 000000000..e432fcccb
--- /dev/null
+++ b/lib/list/theme.scss
@@ -0,0 +1,159 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.list {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+ padding: $list-vertical-padding 0;
+ text-align: left;
+ white-space: nowrap;
+ list-style: none;
+}
+
+.subheader {
+ padding-left: $list-horizontal-padding;
+ margin: - $list-vertical-padding 0 0;
+ font-size: $list-subheader-font-size;
+ font-weight: $list-subheader-font-weight;
+ line-height: $list-subheader-height;
+ color: $color-text-secondary;
+}
+
+.divider {
+ height: $list-divider-height;
+ margin: - $list-divider-height 0 0;
+ background-color: $color-divider;
+ border: 0;
+ &.inset {
+ margin-right: $list-horizontal-padding;
+ margin-left: $list-content-left-spacing;
+ }
+ .list + & {
+ margin-top: - $list-vertical-padding;
+ }
+ .listItem ~ & {
+ margin-top: $list-vertical-padding;
+ margin-bottom: $list-vertical-padding;
+ }
+}
+
+.listItem {
+ position: relative;
+ > [data-react-toolbox="ripple"] {
+ overflow: hidden;
+ }
+
+ .ripple {
+ color: $color-text-secondary;
+ }
+}
+
+.item {
+ position: relative;
+ display: flex;
+ min-height: $list-item-min-height;
+ align-items: center;
+ padding: 0 $list-horizontal-padding;
+ color: $color-text;
+ &.selectable:not(.disabled):hover {
+ cursor: pointer;
+ background-color: $list-item-hover-color;
+ }
+ &.disabled {
+ pointer-events: none;
+ &:not(.checkboxItem) {
+ opacity: .5;
+ }
+ > .checkbox > [data-react-toolbox="label"] {
+ opacity: .5;
+ }
+ }
+}
+
+.left {
+ [data-react-toolbox="font-icon"] {
+ width: $list-item-icon-size;
+ }
+ & :last-child {
+ > [data-react-toolbox="font-icon"] {
+ margin-right: $list-item-right-icon-margin;
+ }
+ }
+}
+
+.right {
+ > :last-child {
+ margin-right: 0;
+ }
+
+ > :first-child {
+ margin-left: $list-horizontal-padding;
+ }
+}
+
+.left, .right {
+ display: flex;
+ flex: 0 0 auto;
+ align-items: center;
+ vertical-align: middle;
+}
+
+.itemAction {
+ display: flex;
+ margin: $list-item-child-margin $list-horizontal-padding $list-item-child-margin 0;
+
+ > * {
+ padding: 0;
+ }
+
+ > [data-react-toolbox="font-icon"] {
+ font-size: $list-item-icon-font-size;
+ color: $color-text-secondary;
+ }
+}
+
+.itemContentRoot {
+ display: block;
+ flex-grow: 1;
+ &.large {
+ display: flex;
+ height: $list-item-min-height-legend;
+ flex-direction: column;
+ justify-content: center;
+ }
+}
+
+.checkbox {
+ display: flex;
+ width: 100%;
+ height: 100%;
+ min-height: $list-item-min-height;
+ align-items: center;
+ margin: 0;
+ cursor: pointer;
+ > [data-react-toolbox="check"] {
+ margin-right: $list-item-right-checkbox-margin;
+ }
+ > [data-react-toolbox="label"] {
+ padding-left: 0;
+ }
+}
+
+.itemText {
+ display: block;
+
+ &:not(.primary) {
+ padding-top: $list-item-legend-margin-top;
+ font-size: $font-size-small;
+ color: $color-text-secondary;
+ white-space: normal;
+ }
+
+ &.primary {
+ font-size: $font-size-normal;
+ color: $color-text;
+ }
+}
diff --git a/lib/menu/IconMenu.js b/lib/menu/IconMenu.js
new file mode 100644
index 000000000..062b856fa
--- /dev/null
+++ b/lib/menu/IconMenu.js
@@ -0,0 +1,132 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.IconMenu = exports.iconMenuFactory = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _IconButton = require('../button/IconButton.js');
+
+var _IconButton2 = _interopRequireDefault(_IconButton);
+
+var _Menu = require('./Menu.js');
+
+var _Menu2 = _interopRequireDefault(_Menu);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(IconButton, Menu) {
+ var IconMenu = function (_Component) {
+ _inherits(IconMenu, _Component);
+
+ function IconMenu() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, IconMenu);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(IconMenu)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ active: false
+ }, _this.handleButtonClick = function (event) {
+ _this.setState({ active: !_this.state.active });
+ if (_this.props.onClick) _this.props.onClick(event);
+ }, _this.handleMenuHide = function () {
+ _this.setState({ active: false });
+ if (_this.props.onHide) _this.props.onHide();
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(IconMenu, [{
+ key: 'render',
+ value: function render() {
+ return _react2.default.createElement(
+ 'div',
+ { className: (0, _classnames2.default)(this.props.theme.iconMenu, this.props.className) },
+ _react2.default.createElement(IconButton, {
+ className: this.props.theme.icon,
+ icon: this.props.icon,
+ onClick: this.handleButtonClick,
+ ripple: this.props.iconRipple
+ }),
+ _react2.default.createElement(
+ Menu,
+ {
+ ref: 'menu',
+ active: this.state.active,
+ onHide: this.handleMenuHide,
+ onSelect: this.props.onSelect,
+ onShow: this.props.onShow,
+ position: this.props.position,
+ ripple: this.props.menuRipple,
+ selectable: this.props.selectable,
+ selected: this.props.selected
+ },
+ this.props.children
+ )
+ );
+ }
+ }]);
+
+ return IconMenu;
+ }(_react.Component);
+
+ IconMenu.propTypes = {
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ icon: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ iconRipple: _react.PropTypes.bool,
+ menuRipple: _react.PropTypes.bool,
+ onClick: _react.PropTypes.func,
+ onHide: _react.PropTypes.func,
+ onSelect: _react.PropTypes.func,
+ onShow: _react.PropTypes.func,
+ position: _react.PropTypes.string,
+ selectable: _react.PropTypes.bool,
+ selected: _react.PropTypes.any,
+ theme: _react.PropTypes.shape({
+ icon: _react.PropTypes.string,
+ iconMenu: _react.PropTypes.string
+ })
+ };
+ IconMenu.defaultProps = {
+ className: '',
+ icon: 'more_vert',
+ iconRipple: true,
+ menuRipple: true,
+ position: 'auto',
+ selectable: false
+ };
+
+
+ return IconMenu;
+};
+
+var IconMenu = factory(_IconButton2.default, _Menu2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.MENU)(IconMenu);
+exports.iconMenuFactory = factory;
+exports.IconMenu = IconMenu;
\ No newline at end of file
diff --git a/lib/menu/Menu.js b/lib/menu/Menu.js
new file mode 100644
index 000000000..565cc1891
--- /dev/null
+++ b/lib/menu/Menu.js
@@ -0,0 +1,310 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Menu = exports.menuFactory = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactDom = require('react-dom');
+
+var _reactDom2 = _interopRequireDefault(_reactDom);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _utils = require('../utils');
+
+var _MenuItem = require('./MenuItem.js');
+
+var _MenuItem2 = _interopRequireDefault(_MenuItem);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var POSITION = {
+ AUTO: 'auto',
+ STATIC: 'static',
+ TOP_LEFT: 'topLeft',
+ TOP_RIGHT: 'topRight',
+ BOTTOM_LEFT: 'bottomLeft',
+ BOTTOM_RIGHT: 'bottomRight'
+};
+
+var factory = function factory(MenuItem) {
+ var Menu = function (_Component) {
+ _inherits(Menu, _Component);
+
+ function Menu() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Menu);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Menu)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ active: _this.props.active,
+ rippled: false
+ }, _this.handleDocumentClick = function (event) {
+ if (_this.state.active && !_utils.events.targetIsDescendant(event, _reactDom2.default.findDOMNode(_this))) {
+ _this.setState({ active: false, rippled: false });
+ }
+ }, _this.handleSelect = function (item) {
+ var _item$props = item.props;
+ var value = _item$props.value;
+ var onClick = _item$props.onClick;
+
+ _this.setState({ active: false, rippled: _this.props.ripple }, function () {
+ if (onClick) onClick();
+ if (_this.props.onSelect) _this.props.onSelect(value);
+ });
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Menu, [{
+ key: 'componentDidMount',
+ value: function componentDidMount() {
+ var _this2 = this;
+
+ this.positionTimeoutHandle = setTimeout(function () {
+ var _refs$menu$getBoundin = _this2.refs.menu.getBoundingClientRect();
+
+ var width = _refs$menu$getBoundin.width;
+ var height = _refs$menu$getBoundin.height;
+
+ var position = _this2.props.position === POSITION.AUTO ? _this2.calculatePosition() : _this2.props.position;
+ _this2.setState({ position: position, width: width, height: height });
+ });
+ }
+ }, {
+ key: 'componentWillReceiveProps',
+ value: function componentWillReceiveProps(nextProps) {
+ if (this.props.position !== nextProps.position) {
+ var position = nextProps.position === POSITION.AUTO ? this.calculatePosition() : nextProps.position;
+ this.setState({ position: position });
+ }
+
+ if (!this.props.active && nextProps.active && !this.state.active) {
+ this.show();
+ }
+
+ if (this.props.active && !nextProps.active && this.state.active) {
+ this.hide();
+ }
+ }
+ }, {
+ key: 'shouldComponentUpdate',
+ value: function shouldComponentUpdate(nextProps, nextState) {
+ var _this3 = this;
+
+ if (!this.state.active && nextState.active && this.props.position === POSITION.AUTO) {
+ var position = this.calculatePosition();
+ if (this.state.position !== position) {
+ this.setState({ position: position, active: false }, function () {
+ _this3.activateTimeoutHandle = setTimeout(function () {
+ _this3.setState({ active: true });
+ }, 20);
+ });
+ return false;
+ }
+ }
+ return true;
+ }
+ }, {
+ key: 'componentWillUpdate',
+ value: function componentWillUpdate(nextProps, nextState) {
+ if (!this.state.active && nextState.active) {
+ _utils.events.addEventsToDocument({ click: this.handleDocumentClick });
+ }
+ }
+ }, {
+ key: 'componentDidUpdate',
+ value: function componentDidUpdate(prevProps, prevState) {
+ if (prevState.active && !this.state.active) {
+ if (this.props.onHide) this.props.onHide();
+ _utils.events.removeEventsFromDocument({ click: this.handleDocumentClick });
+ } else if (!prevState.active && this.state.active && this.props.onShow) {
+ this.props.onShow();
+ }
+ }
+ }, {
+ key: 'componentWillUnmount',
+ value: function componentWillUnmount() {
+ if (this.state.active) {
+ _utils.events.removeEventsFromDocument({ click: this.handleDocumentClick });
+ }
+ clearTimeout(this.positionTimeoutHandle);
+ clearTimeout(this.activateTimeoutHandle);
+ }
+ }, {
+ key: 'calculatePosition',
+ value: function calculatePosition() {
+ var parentNode = _reactDom2.default.findDOMNode(this).parentNode;
+ if (!parentNode) return;
+
+ var _parentNode$getBoundi = parentNode.getBoundingClientRect();
+
+ var top = _parentNode$getBoundi.top;
+ var left = _parentNode$getBoundi.left;
+ var height = _parentNode$getBoundi.height;
+ var width = _parentNode$getBoundi.width;
+
+ var _utils$getViewport = _utils.utils.getViewport();
+
+ var wh = _utils$getViewport.height;
+ var ww = _utils$getViewport.width;
+
+ var toTop = top < wh / 2 - height / 2;
+ var toLeft = left < ww / 2 - width / 2;
+ return '' + (toTop ? 'top' : 'bottom') + (toLeft ? 'Left' : 'Right');
+ }
+ }, {
+ key: 'getMenuStyle',
+ value: function getMenuStyle() {
+ var _state = this.state;
+ var width = _state.width;
+ var height = _state.height;
+ var position = _state.position;
+
+ if (position !== POSITION.STATIC) {
+ if (this.state.active) {
+ return { clip: 'rect(0 ' + width + 'px ' + height + 'px 0)' };
+ } else if (position === POSITION.TOP_RIGHT) {
+ return { clip: 'rect(0 ' + width + 'px 0 ' + width + 'px)' };
+ } else if (position === POSITION.BOTTOM_RIGHT) {
+ return { clip: 'rect(' + height + 'px ' + width + 'px ' + height + 'px ' + width + 'px)' };
+ } else if (position === POSITION.BOTTOM_LEFT) {
+ return { clip: 'rect(' + height + 'px 0 ' + height + 'px 0)' };
+ } else if (position === POSITION.TOP_LEFT) {
+ return { clip: 'rect(0 0 0 0)' };
+ }
+ }
+ }
+ }, {
+ key: 'getRootStyle',
+ value: function getRootStyle() {
+ if (this.state.position !== POSITION.STATIC) {
+ return { width: this.state.width, height: this.state.height };
+ }
+ }
+ }, {
+ key: 'renderItems',
+ value: function renderItems() {
+ var _this4 = this;
+
+ return _react2.default.Children.map(this.props.children, function (item) {
+ if (!item) return item;
+ if (item.type === MenuItem) {
+ return _react2.default.cloneElement(item, {
+ ripple: item.props.ripple || _this4.props.ripple,
+ selected: typeof item.props.value !== 'undefined' && _this4.props.selectable && item.props.value === _this4.props.selected,
+ onClick: _this4.handleSelect.bind(_this4, item)
+ });
+ } else {
+ return _react2.default.cloneElement(item);
+ }
+ });
+ }
+ }, {
+ key: 'show',
+ value: function show() {
+ var _refs$menu$getBoundin2 = this.refs.menu.getBoundingClientRect();
+
+ var width = _refs$menu$getBoundin2.width;
+ var height = _refs$menu$getBoundin2.height;
+
+ this.setState({ active: true, width: width, height: height });
+ }
+ }, {
+ key: 'hide',
+ value: function hide() {
+ this.setState({ active: false });
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _classnames;
+
+ var theme = this.props.theme;
+
+ var outlineStyle = { width: this.state.width, height: this.state.height };
+ var className = (0, _classnames3.default)([theme.menu, theme[this.state.position]], (_classnames = {}, _defineProperty(_classnames, theme.active, this.state.active), _defineProperty(_classnames, theme.rippled, this.state.rippled), _classnames), this.props.className);
+
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'menu', className: className, style: this.getRootStyle() },
+ this.props.outline ? _react2.default.createElement('div', { className: theme.outline, style: outlineStyle }) : null,
+ _react2.default.createElement(
+ 'ul',
+ { ref: 'menu', className: theme.menuInner, style: this.getMenuStyle() },
+ this.renderItems()
+ )
+ );
+ }
+ }]);
+
+ return Menu;
+ }(_react.Component);
+
+ Menu.propTypes = {
+ active: _react.PropTypes.bool,
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ onHide: _react.PropTypes.func,
+ onSelect: _react.PropTypes.func,
+ onShow: _react.PropTypes.func,
+ outline: _react.PropTypes.bool,
+ position: _react.PropTypes.string,
+ ripple: _react.PropTypes.bool,
+ selectable: _react.PropTypes.bool,
+ selected: _react.PropTypes.any,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ bottomLeft: _react.PropTypes.string,
+ bottomRight: _react.PropTypes.string,
+ menu: _react.PropTypes.string,
+ menuInner: _react.PropTypes.string,
+ outline: _react.PropTypes.string,
+ rippled: _react.PropTypes.string,
+ static: _react.PropTypes.string,
+ topLeft: _react.PropTypes.string,
+ topRight: _react.PropTypes.string
+ })
+ };
+ Menu.defaultProps = {
+ active: false,
+ outline: true,
+ position: POSITION.STATIC,
+ ripple: true,
+ selectable: true
+ };
+
+
+ return Menu;
+};
+
+var Menu = factory(_MenuItem2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.MENU)(Menu);
+exports.menuFactory = factory;
+exports.Menu = Menu;
\ No newline at end of file
diff --git a/lib/menu/MenuDivider.js b/lib/menu/MenuDivider.js
new file mode 100644
index 000000000..cf8455141
--- /dev/null
+++ b/lib/menu/MenuDivider.js
@@ -0,0 +1,30 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.MenuDivider = undefined;
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var MenuDivider = function MenuDivider(_ref) {
+ var theme = _ref.theme;
+ return _react2.default.createElement('hr', { 'data-react-toolbox': 'menu-divider', className: theme.menuDivider });
+};
+
+MenuDivider.propTypes = {
+ theme: _react.PropTypes.shape({
+ menuDivider: _react.PropTypes.string
+ })
+};
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.MENU)(MenuDivider);
+exports.MenuDivider = MenuDivider;
\ No newline at end of file
diff --git a/lib/menu/MenuItem.js b/lib/menu/MenuItem.js
new file mode 100644
index 000000000..7562dfa0b
--- /dev/null
+++ b/lib/menu/MenuItem.js
@@ -0,0 +1,137 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.MenuItem = exports.menuItemFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _FontIcon = require('../font_icon/FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+var _Ripple = require('../ripple/Ripple.js');
+
+var _Ripple2 = _interopRequireDefault(_Ripple);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(ripple) {
+ var MenuItem = function (_Component) {
+ _inherits(MenuItem, _Component);
+
+ function MenuItem() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, MenuItem);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(MenuItem)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleClick = function (event) {
+ if (_this.props.onClick && !_this.props.disabled) {
+ _this.props.onClick(event, _this);
+ }
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(MenuItem, [{
+ key: 'render',
+ value: function render() {
+ var _classnames;
+
+ var _props = this.props;
+ var icon = _props.icon;
+ var caption = _props.caption;
+ var children = _props.children;
+ var shortcut = _props.shortcut;
+ var selected = _props.selected;
+ var disabled = _props.disabled;
+ var theme = _props.theme;
+
+ var others = _objectWithoutProperties(_props, ['icon', 'caption', 'children', 'shortcut', 'selected', 'disabled', 'theme']);
+
+ var className = (0, _classnames3.default)(theme.menuItem, (_classnames = {}, _defineProperty(_classnames, theme.selected, selected), _defineProperty(_classnames, theme.disabled, disabled), _classnames), this.props.className);
+
+ return _react2.default.createElement(
+ 'li',
+ _extends({}, others, { 'data-react-toolbox': 'menu-item', className: className, onClick: this.handleClick }),
+ icon ? _react2.default.createElement(_FontIcon2.default, { value: icon, className: theme.icon }) : null,
+ _react2.default.createElement(
+ 'span',
+ { className: theme.caption },
+ caption
+ ),
+ shortcut ? _react2.default.createElement(
+ 'small',
+ { className: theme.shortcut },
+ shortcut
+ ) : null,
+ children
+ );
+ }
+ }]);
+
+ return MenuItem;
+ }(_react.Component);
+
+ MenuItem.propTypes = {
+ caption: _react.PropTypes.string,
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ icon: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ onClick: _react.PropTypes.func,
+ selected: _react.PropTypes.bool,
+ shortcut: _react.PropTypes.string,
+ theme: _react.PropTypes.shape({
+ caption: _react.PropTypes.string,
+ disabled: _react.PropTypes.string,
+ icon: _react.PropTypes.string,
+ menuItem: _react.PropTypes.string,
+ selected: _react.PropTypes.string,
+ shortcut: _react.PropTypes.string
+ })
+ };
+ MenuItem.defaultProps = {
+ className: '',
+ disabled: false,
+ selected: false
+ };
+
+
+ return ripple(MenuItem);
+};
+
+var MenuItem = factory((0, _Ripple2.default)({}));
+exports.default = (0, _reactCssThemr.themr)(_identifiers.MENU)(MenuItem);
+exports.menuItemFactory = factory;
+exports.MenuItem = MenuItem;
\ No newline at end of file
diff --git a/lib/menu/_config.scss b/lib/menu/_config.scss
new file mode 100644
index 000000000..0b5343661
--- /dev/null
+++ b/lib/menu/_config.scss
@@ -0,0 +1,16 @@
+$menu-expand-duration: .3s !default;
+$menu-fade-duration: .2s !default;
+$menu-ripple-delay: .3s !default;
+$menu-background-color: $color-white !default;
+$menu-padding: .8 * $unit 0 !default;
+$menu-outline-border-radius: .2 * $unit !default;
+$menu-item-hover-background: $palette-grey-200 !default;
+$menu-item-selected-background: transparent !default;
+$menu-item-icon-font-size: 2.4 * $unit !default;
+$menu-item-icon-size: 1.6 * $menu-item-icon-font-size !default;
+$menu-item-height: 4.8 * $unit !default;
+$menu-item-padding: 1.6 * $unit !default;
+$menu-item-font-size: 1.6 * $unit !default;
+$menu-divider-height: (4.8 * $unit) / 4 !default;
+$menu-icon-size: 2.3 * $unit !default;
+$menu-icon-ripple-duration: 650ms !default;
diff --git a/lib/menu/index.js b/lib/menu/index.js
new file mode 100644
index 000000000..56f87c943
--- /dev/null
+++ b/lib/menu/index.js
@@ -0,0 +1,43 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.IconMenu = exports.Menu = exports.MenuItem = exports.MenuDivider = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _button = require('../button');
+
+var _MenuDivider = require('./MenuDivider.js');
+
+var _MenuItem = require('./MenuItem.js');
+
+var _Menu = require('./Menu.js');
+
+var _IconMenu = require('./IconMenu.js');
+
+var _ripple = require('../ripple');
+
+var _ripple2 = _interopRequireDefault(_ripple);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var applyTheme = function applyTheme(Component) {
+ return (0, _reactCssThemr.themr)(_identifiers.MENU, _theme2.default)(Component);
+};
+var ThemedMenuDivider = applyTheme(_MenuDivider.MenuDivider);
+var ThemedMenuItem = applyTheme((0, _MenuItem.menuItemFactory)((0, _ripple2.default)({})));
+var ThemedMenu = applyTheme((0, _Menu.menuFactory)(ThemedMenuItem));
+var ThemedIconMenu = applyTheme((0, _IconMenu.iconMenuFactory)(_button.IconButton, ThemedMenu));
+
+exports.MenuDivider = ThemedMenuDivider;
+exports.MenuItem = ThemedMenuItem;
+exports.Menu = ThemedMenu;
+exports.IconMenu = ThemedIconMenu;
\ No newline at end of file
diff --git a/lib/menu/theme.scss b/lib/menu/theme.scss
new file mode 100644
index 000000000..9f4ec9b3c
--- /dev/null
+++ b/lib/menu/theme.scss
@@ -0,0 +1,154 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.iconMenu {
+ position: relative;
+ display: inline-block;
+ text-align: center;
+ .icon {
+ cursor: pointer;
+ }
+}
+
+.menu {
+ position: relative;
+ display: inline-block;
+ &.topLeft {
+ position: absolute;
+ top: 0;
+ left: 0;
+ > .outline {
+ transform-origin: 0 0;
+ }
+ }
+ &.topRight {
+ position: absolute;
+ top: 0;
+ right: 0;
+ > .outline {
+ transform-origin: 100% 0;
+ }
+ }
+ &.bottomLeft {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ > .outline {
+ transform-origin: 0 100%;
+ }
+ }
+ &.bottomRight {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ > .outline {
+ transform-origin: 100% 100%;
+ }
+ }
+ &:not(.static) {
+ z-index: $z-index-higher;
+ pointer-events: none;
+ > .outline {
+ opacity: 0;
+ transition: transform $menu-expand-duration $animation-curve-default,
+ opacity $menu-fade-duration $animation-curve-default;
+ transform: scale(0);
+ will-change: transform;
+ }
+ > .menuInner {
+ position: absolute;
+ top: 0;
+ left: 0;
+ opacity: 0;
+ }
+ &.rippled:not(.active) {
+ > .outline {
+ transition-delay: $menu-ripple-delay;
+ }
+ > .menuInner {
+ transition-delay: $menu-ripple-delay;
+ }
+ }
+ &.active {
+ pointer-events: all;
+ > .outline {
+ opacity: 1;
+ transform: scale(1);
+ }
+ > .menuInner {
+ opacity: 1;
+ transition: opacity $menu-fade-duration $animation-curve-default,
+ clip $menu-expand-duration $animation-curve-default;
+ }
+ }
+ }
+}
+
+.outline {
+ @include shadow-2dp();
+ position: absolute;
+ top: 0;
+ left: 0;
+ display: block;
+ background-color: $menu-background-color;
+ border-radius: $menu-outline-border-radius;
+}
+
+.menuInner {
+ position: relative;
+ display: block;
+ padding: $menu-padding;
+ text-align: left;
+ white-space: nowrap;
+ list-style: none;
+}
+
+.menuItem {
+ position: relative;
+ display: flex;
+ height: $menu-item-height;
+ align-items: center;
+ padding: 0 $menu-item-padding;
+ overflow: hidden;
+ font-size: $menu-item-font-size;
+ color: $color-text;
+ &:not(.disabled):hover {
+ cursor: pointer;
+ background-color: $menu-item-hover-background;
+ }
+ &.disabled {
+ pointer-events: none;
+ opacity: .5;
+ }
+ &.selected {
+ font-weight: 500;
+ background-color: $menu-item-selected-background;
+ }
+ .ripple {
+ color: $color-text-secondary;
+ }
+
+ .icon {
+ width: $menu-item-icon-size;
+ font-size: $menu-item-icon-font-size !important;
+ }
+}
+
+.caption {
+ flex-grow: 1;
+ font-size: $font-size-normal;
+}
+
+.shortcut {
+ margin-left: $menu-item-padding;
+}
+
+.menuDivider {
+ display: block;
+ width: 100%;
+ height: 1px;
+ margin: $menu-divider-height 0;
+ background-color: $color-divider;
+}
diff --git a/lib/navigation/Navigation.js b/lib/navigation/Navigation.js
new file mode 100644
index 000000000..ec9e44f58
--- /dev/null
+++ b/lib/navigation/Navigation.js
@@ -0,0 +1,86 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Navigation = exports.navigationFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Button = require('../button/Button.js');
+
+var _Button2 = _interopRequireDefault(_Button);
+
+var _Link = require('../link/Link.js');
+
+var _Link2 = _interopRequireDefault(_Link);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var factory = function factory(Button, Link) {
+ var Navigation = function Navigation(_ref) {
+ var actions = _ref.actions;
+ var children = _ref.children;
+ var className = _ref.className;
+ var routes = _ref.routes;
+ var theme = _ref.theme;
+ var type = _ref.type;
+
+ var _className = (0, _classnames2.default)(theme[type], className);
+ var buttons = actions.map(function (action, index) {
+ return _react2.default.createElement(Button, _extends({ className: theme.button, key: index }, action));
+ });
+
+ var links = routes.map(function (route, index) {
+ return _react2.default.createElement(Link, _extends({ className: theme.link, key: index }, route));
+ });
+
+ return _react2.default.createElement(
+ 'nav',
+ { 'data-react-toolbox': 'navigation', className: _className },
+ links,
+ buttons,
+ children
+ );
+ };
+
+ Navigation.propTypes = {
+ actions: _react.PropTypes.array,
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ routes: _react.PropTypes.array,
+ theme: _react.PropTypes.shape({
+ button: _react.PropTypes.string,
+ horizontal: _react.PropTypes.string,
+ link: _react.PropTypes.string,
+ vertical: _react.PropTypes.string
+ }),
+ type: _react.PropTypes.oneOf(['vertical', 'horizontal'])
+ };
+
+ Navigation.defaultProps = {
+ actions: [],
+ className: '',
+ type: 'horizontal',
+ routes: []
+ };
+
+ return Navigation;
+};
+
+var Navigation = factory(_Button2.default, _Link2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.NAVIGATION)(Navigation);
+exports.navigationFactory = factory;
+exports.Navigation = Navigation;
\ No newline at end of file
diff --git a/lib/navigation/_config.scss b/lib/navigation/_config.scss
new file mode 100644
index 000000000..6b654ac73
--- /dev/null
+++ b/lib/navigation/_config.scss
@@ -0,0 +1,2 @@
+$navigation-space: $unit !default;
+$navigation-color: $color-black !default;
diff --git a/lib/navigation/index.js b/lib/navigation/index.js
new file mode 100644
index 000000000..386b3ecee
--- /dev/null
+++ b/lib/navigation/index.js
@@ -0,0 +1,26 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Navigation = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Navigation = require('./Navigation.js');
+
+var _button = require('../button');
+
+var _link = require('../link');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ThemedNavigation = (0, _reactCssThemr.themr)(_identifiers.NAVIGATION, _theme2.default)((0, _Navigation.navigationFactory)(_button.Button, _link.Link));
+exports.default = ThemedNavigation;
+exports.Navigation = ThemedNavigation;
\ No newline at end of file
diff --git a/lib/navigation/theme.scss b/lib/navigation/theme.scss
new file mode 100644
index 000000000..2d8375fd2
--- /dev/null
+++ b/lib/navigation/theme.scss
@@ -0,0 +1,26 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.horizontal {
+ > [data-react-toolbox="button"], > [data-react-toolbox="link"] {
+ display: inline-block;
+ margin: 0 $navigation-space / 2;
+ }
+}
+
+.vertical {
+ > [data-react-toolbox="button"], > [data-react-toolbox="link"] {
+ display: block;
+ margin: $navigation-space / 2;
+ }
+}
+
+.vertical, .horizontal {
+ padding: $navigation-space / 2;
+
+ > [data-react-toolbox="link"] {
+ color: $navigation-color;
+ }
+}
diff --git a/lib/overlay/Overlay.js b/lib/overlay/Overlay.js
new file mode 100644
index 000000000..1bb8e48bc
--- /dev/null
+++ b/lib/overlay/Overlay.js
@@ -0,0 +1,130 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Overlay = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Portal = require('../hoc/Portal.js');
+
+var _Portal2 = _interopRequireDefault(_Portal);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var Overlay = function (_Component) {
+ _inherits(Overlay, _Component);
+
+ function Overlay() {
+ _classCallCheck(this, Overlay);
+
+ return _possibleConstructorReturn(this, Object.getPrototypeOf(Overlay).apply(this, arguments));
+ }
+
+ _createClass(Overlay, [{
+ key: 'componentDidMount',
+ value: function componentDidMount() {
+ if (this.props.active) {
+ this.escKeyListener = document.body.addEventListener('keydown', this.handleEscKey.bind(this));
+ document.body.style.overflow = 'hidden';
+ }
+ }
+ }, {
+ key: 'componentWillUpdate',
+ value: function componentWillUpdate(nextProps) {
+ if (nextProps.active && !this.props.active) document.body.style.overflow = 'hidden';
+ if (!nextProps.active && this.props.active) document.body.style.overflow = null;
+ }
+ }, {
+ key: 'componentDidUpdate',
+ value: function componentDidUpdate() {
+ if (this.props.active && !this.escKeyListener) {
+ this.escKeyListener = document.body.addEventListener('keydown', this.handleEscKey.bind(this));
+ }
+ }
+ }, {
+ key: 'componentWillUnmount',
+ value: function componentWillUnmount() {
+ if (this.escKeyListener) {
+ document.body.removeEventListener('keydown', this.handleEscKey);
+ this.escKeyListener = null;
+ }
+ }
+ }, {
+ key: 'handleEscKey',
+ value: function handleEscKey(e) {
+ if (this.props.active && this.props.onEscKeyDown && e.which === 27) {
+ this.props.onEscKeyDown(e);
+ }
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _classnames;
+
+ var _props = this.props;
+ var active = _props.active;
+ var className = _props.className;
+ var children = _props.children;
+ var invisible = _props.invisible;
+ var onClick = _props.onClick;
+ var theme = _props.theme;
+
+ var _className = (0, _classnames3.default)(theme.overlay, (_classnames = {}, _defineProperty(_classnames, theme.active, active), _defineProperty(_classnames, theme.invisible, invisible), _classnames), className);
+
+ return _react2.default.createElement(
+ _Portal2.default,
+ null,
+ _react2.default.createElement(
+ 'div',
+ { className: _className },
+ _react2.default.createElement('div', { className: theme.backdrop, onClick: onClick }),
+ children
+ )
+ );
+ }
+ }]);
+
+ return Overlay;
+}(_react.Component);
+
+Overlay.propTypes = {
+ active: _react.PropTypes.bool,
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ invisible: _react.PropTypes.bool,
+ onClick: _react.PropTypes.func,
+ onEscKeyDown: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ backdrop: _react.PropTypes.string,
+ invisible: _react.PropTypes.string,
+ overlay: _react.PropTypes.string
+ })
+};
+Overlay.defaultProps = {
+ invisible: false
+};
+exports.default = (0, _reactCssThemr.themr)(_identifiers.OVERLAY)(Overlay);
+exports.Overlay = Overlay;
\ No newline at end of file
diff --git a/lib/overlay/_config.scss b/lib/overlay/_config.scss
new file mode 100644
index 000000000..ccba4d3c7
--- /dev/null
+++ b/lib/overlay/_config.scss
@@ -0,0 +1,2 @@
+$overlay-color: $color-black !default;
+$overlay-opacity: .6 !default;
diff --git a/lib/overlay/index.js b/lib/overlay/index.js
new file mode 100644
index 000000000..96c77eeee
--- /dev/null
+++ b/lib/overlay/index.js
@@ -0,0 +1,22 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Overlay = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Overlay = require('./Overlay.js');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ThemedOverlay = (0, _reactCssThemr.themr)(_identifiers.OVERLAY, _theme2.default)(_Overlay.Overlay);
+exports.default = ThemedOverlay;
+exports.Overlay = ThemedOverlay;
\ No newline at end of file
diff --git a/lib/overlay/theme.scss b/lib/overlay/theme.scss
new file mode 100644
index 000000000..058bb466b
--- /dev/null
+++ b/lib/overlay/theme.scss
@@ -0,0 +1,42 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: $z-index-highest;
+ display: flex;
+ width: 100vw;
+ height: 100vh;
+ flex-direction: column;
+ align-content: center;
+ align-items: center;
+ justify-content: center;
+ pointer-events: none;
+ &.invisible > *:not(.overlay) {
+ pointer-events: all;
+ }
+}
+
+.backdrop {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: $overlay-color;
+ opacity: 0;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transition-property: opacity;
+}
+
+.active {
+ pointer-events: all;
+ > .backdrop {
+ opacity: $overlay-opacity;
+ }
+}
diff --git a/lib/progress_bar/ProgressBar.js b/lib/progress_bar/ProgressBar.js
new file mode 100644
index 000000000..f0aa1fe64
--- /dev/null
+++ b/lib/progress_bar/ProgressBar.js
@@ -0,0 +1,160 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ProgressBar = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _prefixer = require('../utils/prefixer.js');
+
+var _prefixer2 = _interopRequireDefault(_prefixer);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var ProgressBar = function (_Component) {
+ _inherits(ProgressBar, _Component);
+
+ function ProgressBar() {
+ _classCallCheck(this, ProgressBar);
+
+ return _possibleConstructorReturn(this, Object.getPrototypeOf(ProgressBar).apply(this, arguments));
+ }
+
+ _createClass(ProgressBar, [{
+ key: 'calculateRatio',
+ value: function calculateRatio(value) {
+ if (value < this.props.min) return 0;
+ if (value > this.props.max) return 1;
+ return (value - this.props.min) / (this.props.max - this.props.min);
+ }
+ }, {
+ key: 'circularStyle',
+ value: function circularStyle() {
+ if (this.props.mode !== 'indeterminate') {
+ return { strokeDasharray: 2 * Math.PI * 25 * this.calculateRatio(this.props.value) + ', 400' };
+ }
+ }
+ }, {
+ key: 'linearStyle',
+ value: function linearStyle() {
+ if (this.props.mode !== 'indeterminate') {
+ return {
+ buffer: (0, _prefixer2.default)({ transform: 'scaleX(' + this.calculateRatio(this.props.buffer) + ')' }),
+ value: (0, _prefixer2.default)({ transform: 'scaleX(' + this.calculateRatio(this.props.value) + ')' })
+ };
+ } else {
+ return {};
+ }
+ }
+ }, {
+ key: 'renderCircular',
+ value: function renderCircular() {
+ return _react2.default.createElement(
+ 'svg',
+ { className: this.props.theme.circle },
+ _react2.default.createElement('circle', { className: this.props.theme.path, style: this.circularStyle(), cx: '30', cy: '30', r: '25' })
+ );
+ }
+ }, {
+ key: 'renderLinear',
+ value: function renderLinear() {
+ var _linearStyle = this.linearStyle();
+
+ var buffer = _linearStyle.buffer;
+ var value = _linearStyle.value;
+
+ return _react2.default.createElement(
+ 'div',
+ null,
+ _react2.default.createElement('span', { ref: 'buffer', 'data-ref': 'buffer', className: this.props.theme.buffer, style: buffer }),
+ _react2.default.createElement('span', { ref: 'value', 'data-ref': 'value', className: this.props.theme.value, style: value })
+ );
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _classnames;
+
+ var _props = this.props;
+ var className = _props.className;
+ var max = _props.max;
+ var min = _props.min;
+ var mode = _props.mode;
+ var multicolor = _props.multicolor;
+ var type = _props.type;
+ var theme = _props.theme;
+ var value = _props.value;
+
+ var _className = (0, _classnames3.default)(theme[type], (_classnames = {}, _defineProperty(_classnames, theme[mode], mode), _defineProperty(_classnames, theme.multicolor, multicolor), _classnames), className);
+
+ return _react2.default.createElement(
+ 'div',
+ {
+ 'data-react-toolbox': 'progress-bar',
+ 'aria-valuenow': value,
+ 'aria-valuemin': min,
+ 'aria-valuemax': max,
+ className: _className
+ },
+ type === 'circular' ? this.renderCircular() : this.renderLinear()
+ );
+ }
+ }]);
+
+ return ProgressBar;
+}(_react.Component);
+
+ProgressBar.propTypes = {
+ buffer: _react.PropTypes.number,
+ className: _react.PropTypes.string,
+ max: _react.PropTypes.number,
+ min: _react.PropTypes.number,
+ mode: _react.PropTypes.oneOf(['determinate', 'indeterminate']),
+ multicolor: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ buffer: _react.PropTypes.string,
+ circle: _react.PropTypes.string,
+ circular: _react.PropTypes.string,
+ indeterminate: _react.PropTypes.string,
+ linear: _react.PropTypes.string,
+ multicolor: _react.PropTypes.string,
+ path: _react.PropTypes.string,
+ value: _react.PropTypes.string
+ }),
+ type: _react.PropTypes.oneOf(['linear', 'circular']),
+ value: _react.PropTypes.number
+};
+ProgressBar.defaultProps = {
+ buffer: 0,
+ className: '',
+ max: 100,
+ min: 0,
+ mode: 'indeterminate',
+ multicolor: false,
+ type: 'linear',
+ value: 0
+};
+exports.default = (0, _reactCssThemr.themr)(_identifiers.PROGRESS_BAR)(ProgressBar);
+exports.ProgressBar = ProgressBar;
\ No newline at end of file
diff --git a/lib/progress_bar/__test__/index.spec.js b/lib/progress_bar/__test__/index.spec.js
new file mode 100644
index 000000000..ec2d796d7
--- /dev/null
+++ b/lib/progress_bar/__test__/index.spec.js
@@ -0,0 +1,92 @@
+'use strict';
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _expect = require('expect');
+
+var _expect2 = _interopRequireDefault(_expect);
+
+var _reactAddonsTestUtils = require('react-addons-test-utils');
+
+var _reactAddonsTestUtils2 = _interopRequireDefault(_reactAddonsTestUtils);
+
+var _ProgressBar = require('../ProgressBar');
+
+var _ProgressBar2 = _interopRequireDefault(_ProgressBar);
+
+var _theme = require('../theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+var _testing = require('../../utils/testing');
+
+var _testing2 = _interopRequireDefault(_testing);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+describe('ProgressBar', function () {
+ var progressBar = void 0;
+
+ describe('#calculateRatio', function () {
+ before(function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_ProgressBar2.default, { min: 100, max: 300, theme: _theme2.default }));
+ progressBar = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, _ProgressBar.ProgressBar);
+ });
+
+ it('calculates the right ratio', function () {
+ (0, _expect2.default)(progressBar.calculateRatio(150)).toEqual(0.25);
+ });
+
+ it('gets 0 when value is less than min', function () {
+ (0, _expect2.default)(progressBar.calculateRatio(10)).toEqual(0);
+ });
+
+ it('gets 1 when value is more than max', function () {
+ (0, _expect2.default)(progressBar.calculateRatio(400)).toEqual(1);
+ });
+ });
+
+ describe('#render', function () {
+ var buffer = void 0,
+ value = void 0,
+ wrapper = void 0,
+ circle = void 0,
+ strokeLength = void 0;
+
+ it('renders the value and buffer bars when it is linear', function () {
+ wrapper = _testing2.default.shallowRenderComponent(_ProgressBar.ProgressBar, { theme: _theme2.default }).props.children;
+ (0, _expect2.default)(wrapper.props.children.length).toEqual(2);
+ (0, _expect2.default)(wrapper.props.children[0].ref).toEqual('buffer');
+ (0, _expect2.default)(wrapper.props.children[1].ref).toEqual('value');
+ });
+
+ it('renders the value and buffer bars when it is linear', function () {
+ progressBar = _testing2.default.shallowRenderComponent(_ProgressBar.ProgressBar, { mode: 'determinate', value: 30, buffer: 60, theme: _theme2.default });
+ buffer = progressBar.props.children.props.children[0];
+ value = progressBar.props.children.props.children[1];
+ (0, _expect2.default)(buffer.props.style.transform).toEqual('scaleX(' + 0.6 + ')');
+ (0, _expect2.default)(value.props.style.transform).toEqual('scaleX(' + 0.3 + ')');
+ });
+
+ it('renders the svg circle when it is circular', function () {
+ progressBar = _testing2.default.shallowRenderComponent(_ProgressBar.ProgressBar, { type: 'circular', theme: _theme2.default });
+ (0, _expect2.default)(progressBar.props.children.type).toEqual('svg');
+ (0, _expect2.default)(progressBar.props.children.props.children.type).toEqual('circle');
+ });
+
+ it('renders the proper circle length style when it is circular and determinate', function () {
+ progressBar = _testing2.default.shallowRenderComponent(_ProgressBar.ProgressBar, { type: 'circular', mode: 'determinate', value: 30, theme: _theme2.default });
+ circle = progressBar.props.children.props.children;
+ strokeLength = 2 * Math.PI * circle.props.r * 0.3;
+ (0, _expect2.default)(circle.props.style.strokeDasharray).toEqual(strokeLength + ', 400');
+ });
+
+ it('contains mode and className in its className', function () {
+ progressBar = _testing2.default.shallowRenderComponent(_ProgressBar.ProgressBar, { mode: 'determinate', className: 'tight', theme: _theme2.default });
+ (0, _expect2.default)(progressBar.props.className).toContain(_theme2.default.determinate);
+ (0, _expect2.default)(progressBar.props.className).toContain(_theme2.default.tight);
+ });
+ });
+});
\ No newline at end of file
diff --git a/lib/progress_bar/_config.scss b/lib/progress_bar/_config.scss
new file mode 100644
index 000000000..f554b346b
--- /dev/null
+++ b/lib/progress_bar/_config.scss
@@ -0,0 +1,6 @@
+$progress-height: .4 * $unit !default;
+$progress-main-color: $color-primary !default;
+$progress-secondary-color: rgba($color-primary-contrast, 0.7) !default;
+$circle-wrapper-width: 60 !default;
+$circle-radius: 25 !default;
+$scale-ratio: $circle-radius / 20 !default;
diff --git a/lib/progress_bar/index.js b/lib/progress_bar/index.js
new file mode 100644
index 000000000..a61b09a24
--- /dev/null
+++ b/lib/progress_bar/index.js
@@ -0,0 +1,23 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ProgressBar = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _ProgressBar = require('./ProgressBar.js');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ThemedProgressBar = (0, _reactCssThemr.themr)(_identifiers.PROGRESS_BAR, _theme2.default)(_ProgressBar.ProgressBar);
+
+exports.default = ThemedProgressBar;
+exports.ProgressBar = ThemedProgressBar;
\ No newline at end of file
diff --git a/lib/progress_bar/theme.scss b/lib/progress_bar/theme.scss
new file mode 100644
index 000000000..bcbc64d90
--- /dev/null
+++ b/lib/progress_bar/theme.scss
@@ -0,0 +1,137 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.linear {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+ height: $progress-height;
+ overflow: hidden;
+ background: $color-divider;
+ &.indeterminate .value {
+ transform-origin: center center;
+ animation: linear-indeterminate-bar 1s linear infinite;
+ }
+}
+
+.value, .buffer {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transform: scaleX(0);
+ transform-origin: left center;
+}
+
+.value {
+ background-color: $progress-main-color;
+}
+
+.buffer {
+ background-image: linear-gradient(to right, $progress-secondary-color, $progress-secondary-color),
+ linear-gradient(to right, $progress-main-color, $progress-main-color);
+}
+
+.circular {
+ position: relative;
+ display: inline-block;
+ width: $circle-wrapper-width * 1px;
+ height: $circle-wrapper-width * 1px;
+ transform: rotate(-90deg);
+ &.indeterminate {
+ .circle {
+ animation: circular-indeterminate-bar-rotate 2s linear infinite;
+ }
+ .path {
+ animation: circular-indeterminate-bar-dash 1.5s ease-in-out infinite;
+
+ stroke-dasharray: $scale-ratio * 1, $scale-ratio * 200;
+ stroke-dashoffset: 0;
+ }
+ &.multicolor .path {
+ animation: circular-indeterminate-bar-dash 1.5s ease-in-out infinite,
+ colors (1.5s * 4) ease-in-out infinite;
+ }
+ }
+}
+
+.circle {
+ width: 100%;
+ height: 100%;
+}
+
+.path {
+ transition: stroke-dasharray $animation-duration $animation-curve-default;
+ fill: none;
+
+ stroke-dasharray: 0, $scale-ratio * 200;
+ stroke-dashoffset: 0;
+ stroke-linecap: round;
+ stroke-miterlimit: 20;
+ stroke-width: 4;
+ stroke: $progress-main-color;
+}
+
+@keyframes linear-indeterminate-bar {
+ 0% {
+ transform: translate(-50%) scaleX(0);
+ }
+
+ 50% {
+ transform: translate(-0%) scaleX(.3);
+ }
+
+ 100% {
+ transform: translate(50%) scaleX(0);
+ }
+}
+
+@keyframes circular-indeterminate-bar-rotate {
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes circular-indeterminate-bar-dash {
+ 0% {
+ stroke-dasharray: $scale-ratio * 1, $scale-ratio * 200;
+ stroke-dashoffset: $scale-ratio * 0;
+ }
+
+ 50% {
+ stroke-dasharray: $scale-ratio * 89, $scale-ratio * 200;
+ stroke-dashoffset: $scale-ratio * -35;
+ }
+
+ 100% {
+ stroke-dasharray: $scale-ratio * 89, $scale-ratio * 200;
+ stroke-dashoffset: $scale-ratio * -124;
+ }
+}
+
+@keyframes colors {
+ 0% {
+ stroke: #4285f4;
+ }
+
+ 25% {
+ stroke: #de3e35;
+ }
+
+ 50% {
+ stroke: #f7c223;
+ }
+
+ 75% {
+ stroke: #1b9a59;
+ }
+
+ 100% {
+ stroke: #4285f4;
+ }
+}
diff --git a/lib/radio/Radio.js b/lib/radio/Radio.js
new file mode 100644
index 000000000..161c0b980
--- /dev/null
+++ b/lib/radio/Radio.js
@@ -0,0 +1,46 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var factory = function factory(ripple) {
+ var Radio = function Radio(_ref) {
+ var checked = _ref.checked;
+ var onMouseDown = _ref.onMouseDown;
+ var theme = _ref.theme;
+
+ var other = _objectWithoutProperties(_ref, ['checked', 'onMouseDown', 'theme']);
+
+ return _react2.default.createElement('div', _extends({
+ 'data-react-toolbox': 'radio',
+ className: theme[checked ? 'radioChecked' : 'radio'],
+ onMouseDown: onMouseDown
+ }, other));
+ };
+
+ Radio.propTypes = {
+ checked: _react.PropTypes.bool,
+ children: _react.PropTypes.any,
+ onMouseDown: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ radio: _react.PropTypes.string,
+ radioChecked: _react.PropTypes.string,
+ ripple: _react.PropTypes.string
+ })
+ };
+
+ return ripple(Radio);
+};
+
+exports.default = factory;
\ No newline at end of file
diff --git a/lib/radio/RadioButton.js b/lib/radio/RadioButton.js
new file mode 100644
index 000000000..adbff67a3
--- /dev/null
+++ b/lib/radio/RadioButton.js
@@ -0,0 +1,147 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.RadioButton = exports.radioButtonFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Ripple = require('../ripple/Ripple.js');
+
+var _Ripple2 = _interopRequireDefault(_Ripple);
+
+var _Radio = require('./Radio.js');
+
+var _Radio2 = _interopRequireDefault(_Radio);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(Radio) {
+ var RadioButton = function (_Component) {
+ _inherits(RadioButton, _Component);
+
+ function RadioButton() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, RadioButton);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(RadioButton)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleClick = function (event) {
+ var _this$props = _this.props;
+ var checked = _this$props.checked;
+ var disabled = _this$props.disabled;
+ var onChange = _this$props.onChange;
+
+ if (event.pageX !== 0 && event.pageY !== 0) _this.blur();
+ if (!disabled && !checked && onChange) onChange(event, _this);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(RadioButton, [{
+ key: 'blur',
+ value: function blur() {
+ this.refs.input.blur();
+ }
+ }, {
+ key: 'focus',
+ value: function focus() {
+ this.refs.input.focus();
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var className = _props.className;
+ var checked = _props.checked;
+ var disabled = _props.disabled;
+ var label = _props.label;
+ var theme = _props.theme;
+ var onChange = _props.onChange;
+
+ var others = _objectWithoutProperties(_props, ['className', 'checked', 'disabled', 'label', 'theme', 'onChange']); // eslint-disable-line
+
+
+ var _className = (0, _classnames2.default)(theme[this.props.disabled ? 'disabled' : 'field'], className);
+ return _react2.default.createElement(
+ 'label',
+ { 'data-react-toolbox': 'radio-button', className: _className },
+ _react2.default.createElement('input', _extends({}, others, {
+ className: theme.input,
+ onClick: this.handleClick,
+ readOnly: true,
+ ref: 'input',
+ type: 'radio'
+ })),
+ _react2.default.createElement(Radio, { checked: checked, disabled: disabled, theme: theme }),
+ label ? _react2.default.createElement(
+ 'span',
+ { className: theme.text },
+ label
+ ) : null
+ );
+ }
+ }]);
+
+ return RadioButton;
+ }(_react.Component);
+
+ RadioButton.propTypes = {
+ checked: _react.PropTypes.bool,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ label: _react.PropTypes.string,
+ name: _react.PropTypes.string,
+ onBlur: _react.PropTypes.func,
+ onChange: _react.PropTypes.func,
+ onFocus: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ disabled: _react.PropTypes.string,
+ field: _react.PropTypes.string,
+ input: _react.PropTypes.string,
+ text: _react.PropTypes.string
+ }),
+ value: _react.PropTypes.any
+ };
+ RadioButton.defaultProps = {
+ checked: false,
+ className: '',
+ disabled: false
+ };
+
+
+ return RadioButton;
+};
+
+var Radio = (0, _Radio2.default)((0, _Ripple2.default)({ centered: true, spread: 2.6 }));
+var RadioButton = factory(Radio);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.RADIO)(RadioButton);
+exports.radioButtonFactory = factory;
+exports.RadioButton = RadioButton;
\ No newline at end of file
diff --git a/lib/radio/RadioGroup.js b/lib/radio/RadioGroup.js
new file mode 100644
index 000000000..e85ce1dd9
--- /dev/null
+++ b/lib/radio/RadioGroup.js
@@ -0,0 +1,102 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.RadioGroup = exports.radioGroupFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _RadioButton = require('./RadioButton.js');
+
+var _RadioButton2 = _interopRequireDefault(_RadioButton);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(RadioButton) {
+ var RadioGroup = function (_Component) {
+ _inherits(RadioGroup, _Component);
+
+ function RadioGroup() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, RadioGroup);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(RadioGroup)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleChange = function (value) {
+ if (_this.props.onChange) _this.props.onChange(value);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(RadioGroup, [{
+ key: 'renderRadioButtons',
+ value: function renderRadioButtons() {
+ var _this2 = this;
+
+ return _react2.default.Children.map(this.props.children, function (radio, idx) {
+ return _react2.default.createElement(RadioButton, _extends({}, radio.props, {
+ checked: radio.props.value === _this2.props.value,
+ disabled: _this2.props.disabled || radio.props.disabled,
+ key: idx,
+ label: radio.props.label,
+ onChange: _this2.handleChange.bind(_this2, radio.props.value),
+ value: radio.props.value
+ }));
+ });
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'radio-group', className: this.props.className },
+ this.renderRadioButtons()
+ );
+ }
+ }]);
+
+ return RadioGroup;
+ }(_react.Component);
+
+ RadioGroup.propTypes = {
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ name: _react.PropTypes.string,
+ onChange: _react.PropTypes.func,
+ value: _react.PropTypes.any
+ };
+ RadioGroup.defaultProps = {
+ className: '',
+ disabled: false
+ };
+
+
+ return RadioGroup;
+};
+
+var RadioGroup = factory(_RadioButton2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.RADIO)(RadioGroup);
+exports.radioGroupFactory = factory;
+exports.RadioGroup = RadioGroup;
\ No newline at end of file
diff --git a/lib/radio/_config.scss b/lib/radio/_config.scss
new file mode 100644
index 000000000..42bc3e9dd
--- /dev/null
+++ b/lib/radio/_config.scss
@@ -0,0 +1,9 @@
+$radio-field-margin-bottom: 1.5 * $unit !default;
+$radio-button-size: 1.6 * $unit !default;
+$radio-inner-margin: $radio-button-size / 4 !default;
+$radio-inner-color: $color-primary !default;
+$radio-focus-color: rgba($color-black, 0.1) !default;
+$radio-checked-focus-color: rgba($color-primary, 0.26) !default;
+$radio-text-color: $color-black !default;
+$radio-disabled-color: rgba($color-black, 0.26) !default;
+$radio-text-font-size: 1.4 * $unit !default;
diff --git a/lib/radio/index.js b/lib/radio/index.js
new file mode 100644
index 000000000..b8cfd8400
--- /dev/null
+++ b/lib/radio/index.js
@@ -0,0 +1,36 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.RadioGroup = exports.RadioButton = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _ripple = require('../ripple');
+
+var _ripple2 = _interopRequireDefault(_ripple);
+
+var _Radio = require('./Radio.js');
+
+var _Radio2 = _interopRequireDefault(_Radio);
+
+var _RadioButton = require('./RadioButton.js');
+
+var _RadioGroup = require('./RadioGroup.js');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ThemedRadio = (0, _Radio2.default)((0, _ripple2.default)({ centered: true, spread: 2.6 }));
+var ThemedRadioButton = (0, _reactCssThemr.themr)(_identifiers.RADIO, _theme2.default)((0, _RadioButton.radioButtonFactory)(ThemedRadio));
+var ThemedRadioGroup = (0, _reactCssThemr.themr)(_identifiers.RADIO, _theme2.default)((0, _RadioGroup.radioGroupFactory)(ThemedRadioButton));
+
+exports.default = ThemedRadioButton;
+exports.RadioButton = ThemedRadioButton;
+exports.RadioGroup = ThemedRadioGroup;
\ No newline at end of file
diff --git a/lib/radio/theme.scss b/lib/radio/theme.scss
new file mode 100644
index 000000000..5d0645089
--- /dev/null
+++ b/lib/radio/theme.scss
@@ -0,0 +1,96 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.radio {
+ position: relative;
+ display: inline-block;
+ width: $radio-button-size;
+ height: $radio-button-size;
+ vertical-align: top;
+ cursor: pointer;
+ border: .2 * $unit solid $radio-text-color;
+ border-radius: 50%;
+ &:before {
+ @include material-animation-default();
+ position: absolute;
+ top: $radio-inner-margin - .2 * $unit;
+ left: $radio-inner-margin - .2 * $unit;
+ width: $radio-button-size - $radio-inner-margin * 2;
+ height: $radio-button-size - $radio-inner-margin * 2;
+ content: "";
+ background-color: $radio-inner-color;
+ border-radius: 50%;
+ transition: transform;
+ transform: scale(0);
+ }
+
+ .ripple {
+ background-color: $radio-inner-color;
+ opacity: .3;
+ transition-duration: 650ms;
+ }
+}
+
+.radioChecked {
+ @extend .radio;
+ border: .2 * $unit solid $radio-inner-color;
+ &:before {
+ transform: scale(1);
+ }
+}
+
+.field {
+ position: relative;
+ display: block;
+ height: $radio-button-size;
+ margin-bottom: $radio-field-margin-bottom;
+ white-space: nowrap;
+ vertical-align: middle;
+}
+
+.text {
+ display: inline-block;
+ padding-left: $unit;
+ font-size: $radio-text-font-size;
+ line-height: $radio-button-size;
+ color: $radio-text-color;
+ white-space: nowrap;
+ vertical-align: top;
+}
+
+.input {
+ position: absolute;
+ width: 0;
+ height: 0;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ opacity: 0;
+ appearance: none;
+ &:focus ~ .radio {
+ box-shadow: 0 0 0 $unit $radio-focus-color;
+ }
+ &:focus ~ .radioChecked {
+ box-shadow: 0 0 0 $unit $radio-checked-focus-color;
+ }
+}
+
+.disabled {
+ @extend .field;
+ .text {
+ color: $radio-disabled-color;
+ }
+ .radio {
+ cursor: auto;
+ border-color: $radio-disabled-color;
+ }
+ .radioChecked {
+ cursor: auto;
+ border-color: $radio-disabled-color;
+ &:before {
+ background-color: $radio-disabled-color;
+ }
+ }
+}
diff --git a/lib/ripple/Ripple.js b/lib/ripple/Ripple.js
new file mode 100644
index 000000000..4ad0c99d0
--- /dev/null
+++ b/lib/ripple/Ripple.js
@@ -0,0 +1,232 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactDom = require('react-dom');
+
+var _reactDom2 = _interopRequireDefault(_reactDom);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _events = require('../utils/events');
+
+var _events2 = _interopRequireDefault(_events);
+
+var _prefixer = require('../utils/prefixer');
+
+var _prefixer2 = _interopRequireDefault(_prefixer);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var defaults = {
+ centered: false,
+ className: '',
+ spread: 2,
+ theme: {}
+};
+
+var rippleFactory = function rippleFactory() {
+ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
+
+ var _defaults$options = _extends({}, defaults, options);
+
+ var defaultCentered = _defaults$options.centered;
+ var defaultClassName = _defaults$options.className;
+ var defaultSpread = _defaults$options.spread;
+ var defaultTheme = _defaults$options.theme;
+
+ var props = _objectWithoutProperties(_defaults$options, ['centered', 'className', 'spread', 'theme']);
+
+ return function (ComposedComponent) {
+ var RippledComponent = function (_Component) {
+ _inherits(RippledComponent, _Component);
+
+ function RippledComponent() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, RippledComponent);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(RippledComponent)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ active: false,
+ left: null,
+ restarting: false,
+ top: null,
+ width: null
+ }, _this.handleEnd = function () {
+ document.removeEventListener(_this.touch ? 'touchend' : 'mouseup', _this.handleEnd);
+ _this.setState({ active: false });
+ }, _this.start = function (_ref) {
+ var pageX = _ref.pageX;
+ var pageY = _ref.pageY;
+ var touch = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
+
+ if (!_this._isTouchRippleReceivingMouseEvent(touch)) {
+ _this.touch = touch;
+ document.addEventListener(_this.touch ? 'touchend' : 'mouseup', _this.handleEnd);
+
+ var _this$_getDescriptor = _this._getDescriptor(pageX, pageY);
+
+ var top = _this$_getDescriptor.top;
+ var left = _this$_getDescriptor.left;
+ var width = _this$_getDescriptor.width;
+
+ _this.setState({ active: false, restarting: true, top: top, left: left, width: width }, function () {
+ _this.refs.ripple.offsetWidth; //eslint-disable-line no-unused-expressions
+ _this.setState({ active: true, restarting: false });
+ });
+ }
+ }, _this.handleMouseDown = function (event) {
+ if (!_this.props.disabled) _this.start(event);
+ if (_this.props.onMouseDown) _this.props.onMouseDown(event);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(RippledComponent, [{
+ key: 'componentDidMount',
+ value: function componentDidMount() {
+ var _this2 = this;
+
+ if (this.props.onRippleEnded) {
+ _events2.default.addEventListenerOnTransitionEnded(this.refs.ripple, function (evt) {
+ if (evt.propertyName === 'transform') _this2.props.onRippleEnded(evt);
+ });
+ }
+ }
+ }, {
+ key: 'componentWillUnmount',
+ value: function componentWillUnmount() {
+ if (this.props.onRippleEnded) {
+ _events2.default.removeEventListenerOnTransitionEnded(this.refs.ripple);
+ }
+ }
+ }, {
+ key: '_isTouchRippleReceivingMouseEvent',
+ value: function _isTouchRippleReceivingMouseEvent(touch) {
+ return this.touch && !touch;
+ }
+ }, {
+ key: '_getDescriptor',
+ value: function _getDescriptor(pageX, pageY) {
+ var _ReactDOM$findDOMNode = _reactDom2.default.findDOMNode(this).getBoundingClientRect();
+
+ var left = _ReactDOM$findDOMNode.left;
+ var top = _ReactDOM$findDOMNode.top;
+ var height = _ReactDOM$findDOMNode.height;
+ var width = _ReactDOM$findDOMNode.width;
+ var _props = this.props;
+ var centered = _props.rippleCentered;
+ var spread = _props.rippleSpread;
+
+ return {
+ left: centered ? 0 : pageX - left - width / 2 - window.scrollX,
+ top: centered ? 0 : pageY - top - height / 2 - window.scrollY,
+ width: width * spread
+ };
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ if (!this.props.ripple) {
+ return _react2.default.createElement(ComposedComponent, this.props);
+ } else {
+ var _classnames;
+
+ var _props2 = this.props;
+ var children = _props2.children;
+ var ripple = _props2.ripple;
+ var className = _props2.rippleClassName;
+ var centered = _props2.rippleCentered;
+ var spread = _props2.rippleSpread;
+
+ var other = _objectWithoutProperties(_props2, ['children', 'ripple', 'rippleClassName', 'rippleCentered', 'rippleSpread']);
+
+ var rippleClassName = (0, _classnames3.default)(this.props.theme.ripple, (_classnames = {}, _defineProperty(_classnames, this.props.theme.rippleActive, this.state.active), _defineProperty(_classnames, this.props.theme.rippleRestarting, this.state.restarting), _classnames), className);
+
+ var _state = this.state;
+ var left = _state.left;
+ var top = _state.top;
+ var width = _state.width;
+
+ var scale = this.state.restarting ? 0 : 1;
+ var rippleStyle = (0, _prefixer2.default)({
+ transform: 'translate3d(' + (-width / 2 + left) + 'px, ' + (-width / 2 + top) + 'px, 0) scale(' + scale + ')'
+ }, { width: width, height: width });
+
+ return _react2.default.createElement(
+ ComposedComponent,
+ _extends({}, other, { onMouseDown: this.handleMouseDown }),
+ children ? children : null,
+ _react2.default.createElement(
+ 'span',
+ _extends({ 'data-react-toolbox': 'ripple', className: this.props.theme.rippleWrapper }, props),
+ _react2.default.createElement('span', { ref: 'ripple', role: 'ripple', className: rippleClassName, style: rippleStyle })
+ )
+ );
+ }
+ }
+ }]);
+
+ return RippledComponent;
+ }(_react.Component);
+
+ RippledComponent.propTypes = {
+ children: _react.PropTypes.any,
+ disabled: _react.PropTypes.bool,
+ onRippleEnded: _react.PropTypes.func,
+ ripple: _react.PropTypes.bool,
+ rippleCentered: _react.PropTypes.bool,
+ rippleClassName: _react.PropTypes.string,
+ rippleSpread: _react.PropTypes.number,
+ theme: _react.PropTypes.shape({
+ ripple: _react.PropTypes.string,
+ rippleActive: _react.PropTypes.string,
+ rippleRestarting: _react.PropTypes.string,
+ rippleWrapper: _react.PropTypes.string
+ })
+ };
+ RippledComponent.defaultProps = {
+ disabled: false,
+ ripple: true,
+ rippleCentered: defaultCentered,
+ rippleClassName: defaultClassName,
+ rippleSpread: defaultSpread
+ };
+
+
+ return (0, _reactCssThemr.themr)(_identifiers.RIPPLE, defaultTheme)(RippledComponent);
+ };
+};
+
+exports.default = rippleFactory;
\ No newline at end of file
diff --git a/lib/ripple/_config.scss b/lib/ripple/_config.scss
new file mode 100644
index 000000000..8c26b6f10
--- /dev/null
+++ b/lib/ripple/_config.scss
@@ -0,0 +1,3 @@
+$ripple-duration: 800ms !default;
+$ripple-final-opacity: .3 !default;
+$ripple-size: 15 * $unit !default;
diff --git a/lib/ripple/index.js b/lib/ripple/index.js
new file mode 100644
index 000000000..a63d64d47
--- /dev/null
+++ b/lib/ripple/index.js
@@ -0,0 +1,21 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _Ripple = require('./Ripple.js');
+
+var _Ripple2 = _interopRequireDefault(_Ripple);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+exports.default = function (options) {
+ return (0, _Ripple2.default)(_extends({}, options, { theme: _theme2.default }));
+};
\ No newline at end of file
diff --git a/lib/ripple/theme.scss b/lib/ripple/theme.scss
new file mode 100644
index 000000000..6212078db
--- /dev/null
+++ b/lib/ripple/theme.scss
@@ -0,0 +1,42 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+%ripple {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ z-index: $z-index-high;
+ pointer-events: none;
+ background-color: currentColor;
+ border-radius: 50%;
+ transform-origin: 50% 50%;
+}
+
+.rippleWrapper {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: $z-index-normal;
+ pointer-events: none;
+}
+
+.ripple {
+ @extend %ripple;
+ transition-duration: $ripple-duration;
+ &.rippleRestarting {
+ opacity: $ripple-final-opacity;
+ transition-property: none;
+ }
+ &.rippleActive {
+ opacity: $ripple-final-opacity;
+ transition-property: transform;
+ }
+ &:not(.rippleActive):not(.rippleRestarting) {
+ opacity: 0;
+ transition-property: opacity, transform;
+ }
+}
diff --git a/lib/slider/Slider.js b/lib/slider/Slider.js
new file mode 100644
index 000000000..917c7f1b3
--- /dev/null
+++ b/lib/slider/Slider.js
@@ -0,0 +1,378 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Slider = exports.sliderFactory = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactDom = require('react-dom');
+
+var _reactDom2 = _interopRequireDefault(_reactDom);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _events = require('../utils/events.js');
+
+var _events2 = _interopRequireDefault(_events);
+
+var _prefixer = require('../utils/prefixer.js');
+
+var _prefixer2 = _interopRequireDefault(_prefixer);
+
+var _utils = require('../utils/utils.js');
+
+var _utils2 = _interopRequireDefault(_utils);
+
+var _ProgressBar = require('../progress_bar/ProgressBar.js');
+
+var _ProgressBar2 = _interopRequireDefault(_ProgressBar);
+
+var _Input = require('../input/Input.js');
+
+var _Input2 = _interopRequireDefault(_Input);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(ProgressBar, Input) {
+ var Slider = function (_Component) {
+ _inherits(Slider, _Component);
+
+ function Slider() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Slider);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Slider)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ inputFocused: false,
+ inputValue: null,
+ sliderLength: 0,
+ sliderStart: 0
+ }, _this.handleInputFocus = function () {
+ _this.setState({
+ inputFocused: true,
+ inputValue: _this.valueForInput(_this.props.value)
+ });
+ }, _this.handleInputChange = function (value) {
+ _this.setState({ inputValue: value });
+ }, _this.handleInputBlur = function (event) {
+ var value = _this.state.inputValue || 0;
+ _this.setState({ inputFocused: false, inputValue: null }, function () {
+ _this.props.onChange(_this.trimValue(value), event);
+ });
+ }, _this.handleKeyDown = function (event) {
+ if ([13, 27].indexOf(event.keyCode) !== -1) {
+ _this.refs.input.blur();
+ _reactDom2.default.findDOMNode(_this).blur();
+ }
+ if (event.keyCode === 38) _this.addToValue(_this.props.step);
+ if (event.keyCode === 40) _this.addToValue(-_this.props.step);
+ }, _this.handleMouseDown = function (event) {
+ if (_this.state.inputFocused) _this.refs.input.blur();
+ _events2.default.addEventsToDocument(_this.getMouseEventMap());
+ _this.start(_events2.default.getMousePosition(event));
+ _events2.default.pauseEvent(event);
+ }, _this.handleMouseMove = function (event) {
+ _events2.default.pauseEvent(event);
+ _this.move(_events2.default.getMousePosition(event));
+ }, _this.handleMouseUp = function () {
+ _this.end(_this.getMouseEventMap());
+ }, _this.handleResize = function (event, callback) {
+ var _ReactDOM$findDOMNode = _reactDom2.default.findDOMNode(_this.refs.progressbar).getBoundingClientRect();
+
+ var left = _ReactDOM$findDOMNode.left;
+ var right = _ReactDOM$findDOMNode.right;
+
+ var cb = callback || function () {};
+ _this.setState({ sliderStart: left, sliderLength: right - left }, cb);
+ }, _this.handleSliderBlur = function () {
+ _events2.default.removeEventsFromDocument(_this.getKeyboardEvents());
+ }, _this.handleSliderFocus = function () {
+ _events2.default.addEventsToDocument(_this.getKeyboardEvents());
+ }, _this.handleTouchEnd = function () {
+ _this.end(_this.getTouchEventMap());
+ }, _this.handleTouchMove = function (event) {
+ _this.move(_events2.default.getTouchPosition(event));
+ }, _this.handleTouchStart = function (event) {
+ if (_this.state.inputFocused) _this.refs.input.blur();
+ _this.start(_events2.default.getTouchPosition(event));
+ _events2.default.addEventsToDocument(_this.getTouchEventMap());
+ _events2.default.pauseEvent(event);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Slider, [{
+ key: 'componentDidMount',
+ value: function componentDidMount() {
+ window.addEventListener('resize', this.handleResize);
+ this.handleResize();
+ }
+ }, {
+ key: 'shouldComponentUpdate',
+ value: function shouldComponentUpdate(nextProps, nextState) {
+ if (!this.state.inputFocused && nextState.inputFocused) return false;
+ if (this.state.inputFocused && this.props.value !== nextProps.value) {
+ this.setState({ inputValue: this.valueForInput(nextProps.value) });
+ return false;
+ }
+ return true;
+ }
+ }, {
+ key: 'componentWillUnmount',
+ value: function componentWillUnmount() {
+ window.removeEventListener('resize', this.handleResize);
+ _events2.default.removeEventsFromDocument(this.getMouseEventMap());
+ _events2.default.removeEventsFromDocument(this.getTouchEventMap());
+ _events2.default.removeEventsFromDocument(this.getKeyboardEvents());
+ }
+ }, {
+ key: 'addToValue',
+ value: function addToValue(increment) {
+ var value = this.state.inputFocused ? parseFloat(this.state.inputValue) : this.props.value;
+ value = this.trimValue(value + increment);
+ if (value !== this.props.value) this.props.onChange(value);
+ }
+ }, {
+ key: 'getKeyboardEvents',
+ value: function getKeyboardEvents() {
+ return {
+ keydown: this.handleKeyDown
+ };
+ }
+ }, {
+ key: 'getMouseEventMap',
+ value: function getMouseEventMap() {
+ return {
+ mousemove: this.handleMouseMove,
+ mouseup: this.handleMouseUp
+ };
+ }
+ }, {
+ key: 'getTouchEventMap',
+ value: function getTouchEventMap() {
+ return {
+ touchmove: this.handleTouchMove,
+ touchend: this.handleTouchEnd
+ };
+ }
+ }, {
+ key: 'end',
+ value: function end(revents) {
+ _events2.default.removeEventsFromDocument(revents);
+ this.setState({ pressed: false });
+ }
+ }, {
+ key: 'knobOffset',
+ value: function knobOffset() {
+ var _props = this.props;
+ var max = _props.max;
+ var min = _props.min;
+
+ return this.state.sliderLength * (this.props.value - min) / (max - min);
+ }
+ }, {
+ key: 'move',
+ value: function move(position) {
+ var newValue = this.positionToValue(position);
+ if (newValue !== this.props.value) this.props.onChange(newValue);
+ }
+ }, {
+ key: 'positionToValue',
+ value: function positionToValue(position) {
+ var _state = this.state;
+ var start = _state.sliderStart;
+ var length = _state.sliderLength;
+ var _props2 = this.props;
+ var max = _props2.max;
+ var min = _props2.min;
+
+ return this.trimValue((position.x - start) / length * (max - min) + min);
+ }
+ }, {
+ key: 'start',
+ value: function start(position) {
+ var _this2 = this;
+
+ this.handleResize(null, function () {
+ _this2.setState({ pressed: true });
+ _this2.props.onChange(_this2.positionToValue(position));
+ });
+ }
+ }, {
+ key: 'stepDecimals',
+ value: function stepDecimals() {
+ return (this.props.step.toString().split('.')[1] || []).length;
+ }
+ }, {
+ key: 'trimValue',
+ value: function trimValue(value) {
+ if (value < this.props.min) return this.props.min;
+ if (value > this.props.max) return this.props.max;
+ return _utils2.default.round(value, this.stepDecimals());
+ }
+ }, {
+ key: 'valueForInput',
+ value: function valueForInput(value) {
+ var decimals = this.stepDecimals();
+ return decimals > 0 ? value.toFixed(decimals) : value.toString();
+ }
+ }, {
+ key: 'renderSnaps',
+ value: function renderSnaps() {
+ var _this3 = this;
+
+ if (this.props.snaps) {
+ return _react2.default.createElement(
+ 'div',
+ { ref: 'snaps', className: this.props.theme.snaps },
+ _utils2.default.range(0, (this.props.max - this.props.min) / this.props.step).map(function (i) {
+ return _react2.default.createElement('div', { key: 'span-' + i, className: _this3.props.theme.snap });
+ })
+ );
+ }
+ }
+ }, {
+ key: 'renderInput',
+ value: function renderInput() {
+ if (this.props.editable) {
+ var value = this.state.inputFocused ? this.state.inputValue : this.valueForInput(this.props.value);
+ return _react2.default.createElement(Input, {
+ ref: 'input',
+ className: this.props.theme.input,
+ onFocus: this.handleInputFocus,
+ onChange: this.handleInputChange,
+ onBlur: this.handleInputBlur,
+ value: value
+ });
+ }
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _classnames;
+
+ var theme = this.props.theme;
+
+ var knobStyles = (0, _prefixer2.default)({ transform: 'translateX(' + this.knobOffset() + 'px)' });
+ var className = (0, _classnames3.default)(theme.slider, (_classnames = {}, _defineProperty(_classnames, theme.editable, this.props.editable), _defineProperty(_classnames, theme.pinned, this.props.pinned), _defineProperty(_classnames, theme.pressed, this.state.pressed), _defineProperty(_classnames, theme.ring, this.props.value === this.props.min), _classnames), this.props.className);
+
+ return _react2.default.createElement(
+ 'div',
+ {
+ className: className,
+ 'data-react-toolbox': 'slider',
+ onBlur: this.handleSliderBlur,
+ onFocus: this.handleSliderFocus,
+ tabIndex: '0'
+ },
+ _react2.default.createElement(
+ 'div',
+ {
+ ref: 'slider',
+ className: theme.container,
+ onMouseDown: this.handleMouseDown,
+ onTouchStart: this.handleTouchStart
+ },
+ _react2.default.createElement(
+ 'div',
+ {
+ ref: 'knob',
+ className: theme.knob,
+ onMouseDown: this.handleMouseDown,
+ onTouchStart: this.handleTouchStart,
+ style: knobStyles
+ },
+ _react2.default.createElement('div', { className: theme.innerknob, 'data-value': parseInt(this.props.value) })
+ ),
+ _react2.default.createElement(
+ 'div',
+ { className: theme.progress },
+ _react2.default.createElement(ProgressBar, {
+ ref: 'progressbar',
+ className: theme.innerprogress,
+ max: this.props.max,
+ min: this.props.min,
+ mode: 'determinate',
+ value: this.props.value
+ }),
+ this.renderSnaps()
+ )
+ ),
+ this.renderInput()
+ );
+ }
+ }]);
+
+ return Slider;
+ }(_react.Component);
+
+ Slider.propTypes = {
+ className: _react.PropTypes.string,
+ editable: _react.PropTypes.bool,
+ max: _react.PropTypes.number,
+ min: _react.PropTypes.number,
+ onChange: _react.PropTypes.func,
+ pinned: _react.PropTypes.bool,
+ snaps: _react.PropTypes.bool,
+ step: _react.PropTypes.number,
+ theme: _react.PropTypes.shape({
+ container: _react.PropTypes.string,
+ editable: _react.PropTypes.string,
+ innerknob: _react.PropTypes.string,
+ innerprogress: _react.PropTypes.string,
+ input: _react.PropTypes.string,
+ knob: _react.PropTypes.string,
+ pinned: _react.PropTypes.string,
+ pressed: _react.PropTypes.string,
+ progress: _react.PropTypes.string,
+ ring: _react.PropTypes.string,
+ slider: _react.PropTypes.string,
+ snap: _react.PropTypes.string,
+ snaps: _react.PropTypes.string
+ }),
+ value: _react.PropTypes.number
+ };
+ Slider.defaultProps = {
+ className: '',
+ editable: false,
+ max: 100,
+ min: 0,
+ pinned: false,
+ snaps: false,
+ step: 0.01,
+ value: 0
+ };
+
+
+ return Slider;
+};
+
+var Slider = factory(_ProgressBar2.default, _Input2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.SLIDER)(Slider);
+exports.sliderFactory = factory;
+exports.Slider = Slider;
\ No newline at end of file
diff --git a/lib/slider/__tests__/index.spec.js b/lib/slider/__tests__/index.spec.js
new file mode 100644
index 000000000..92507525b
--- /dev/null
+++ b/lib/slider/__tests__/index.spec.js
@@ -0,0 +1,202 @@
+'use strict';
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactAddonsTestUtils = require('react-addons-test-utils');
+
+var _reactAddonsTestUtils2 = _interopRequireDefault(_reactAddonsTestUtils);
+
+var _sinon = require('sinon');
+
+var _sinon2 = _interopRequireDefault(_sinon);
+
+var _expect = require('expect');
+
+var _expect2 = _interopRequireDefault(_expect);
+
+var _ProgressBar = require('../../progress_bar/ProgressBar.js');
+
+var _Input = require('../../input/Input.js');
+
+var _Input2 = _interopRequireDefault(_Input);
+
+var _Slider = require('../Slider.js');
+
+var _Slider2 = _interopRequireDefault(_Slider);
+
+var _testing = require('../../utils/testing');
+
+var _testing2 = _interopRequireDefault(_testing);
+
+var _theme = require('../theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+describe('Slider', function () {
+ var slider = void 0,
+ progress = void 0,
+ input = void 0,
+ onChange = void 0;
+
+ describe('#positionToValue', function () {
+ before(function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Slider2.default, { min: -500, max: 500 }));
+ slider = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, _Slider.Slider);
+ slider.setState({ sliderStart: 500, sliderLength: 100 });
+ });
+
+ it('returns min when position is less than origin', function () {
+ (0, _expect2.default)(slider.positionToValue({ x: 400 })).toEqual(-500);
+ });
+
+ it('returns max when position is more and origin plus length', function () {
+ (0, _expect2.default)(slider.positionToValue({ x: 900 })).toEqual(500);
+ });
+
+ it('returns the proper position when the position is inside slider', function () {
+ (0, _expect2.default)(slider.positionToValue({ x: 520 })).toEqual(-300);
+ });
+ });
+
+ describe('#trimValue', function () {
+ before(function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Slider2.default, { min: 0, max: 100, step: 0.1 }));
+ slider = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, _Slider.Slider);
+ });
+
+ it('rounds to the proper number', function () {
+ (0, _expect2.default)(slider.trimValue(57.16)).toEqual(57.2);
+ (0, _expect2.default)(slider.trimValue(57.12)).toEqual(57.10);
+ });
+
+ it('returns min if number is less than min', function () {
+ (0, _expect2.default)(slider.trimValue(-57.16)).toEqual(0);
+ });
+
+ it('returns max if number is more than max', function () {
+ (0, _expect2.default)(slider.trimValue(257.16)).toEqual(100);
+ });
+ });
+
+ describe('#valueForInput', function () {
+ before(function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Slider2.default, { min: 0, max: 100, step: 0.01 }));
+ slider = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, _Slider.Slider);
+ });
+
+ it('returns a fixed number when an integer is given', function () {
+ (0, _expect2.default)(slider.valueForInput(4)).toEqual('4.00');
+ });
+
+ it('returns a fixed number when a float is given', function () {
+ (0, _expect2.default)(slider.valueForInput(4.06)).toEqual('4.06');
+ });
+ });
+
+ describe('#knobOffset', function () {
+ it('returns the corresponding offset for a given value and slider length/start', function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Slider2.default, { min: -500, max: 500, value: -250 }));
+ slider = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, _Slider.Slider);
+ slider.setState({ sliderStart: 500, sliderLength: 100 });
+ (0, _expect2.default)(slider.knobOffset()).toEqual(25);
+ });
+ });
+
+ describe('#render', function () {
+ it('contains a linear progress bar with proper properties', function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Slider2.default, { min: 100, max: 1000, value: 140 }));
+ slider = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, _Slider.Slider);
+ progress = _reactAddonsTestUtils2.default.findRenderedComponentWithType(slider, _ProgressBar.ProgressBar);
+ (0, _expect2.default)(progress.props.mode).toEqual('determinate');
+ (0, _expect2.default)(progress.props.type).toEqual('linear');
+ (0, _expect2.default)(progress.props.value).toEqual(140);
+ (0, _expect2.default)(progress.props.min).toEqual(100);
+ (0, _expect2.default)(progress.props.max).toEqual(1000);
+ });
+
+ it('contains an input component if its editable', function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Slider2.default, { editable: true, value: 130 }));
+ slider = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, _Slider.Slider);
+ input = _reactAddonsTestUtils2.default.findRenderedComponentWithType(slider, _Input2.default);
+ (0, _expect2.default)(parseInt(input.props.value)).toEqual(slider.props.value);
+ });
+
+ it('contains the proper number of snaps when snapped', function () {
+ slider = _testing2.default.shallowRenderComponent(_Slider.Slider, { editable: true, pinned: true, theme: _theme2.default });
+ (0, _expect2.default)(slider.props.className).toContain(_theme2.default.ring);
+ (0, _expect2.default)(slider.props.className).toContain(_theme2.default.pinned);
+ slider = _testing2.default.shallowRenderComponent(_Slider.Slider, { editable: true, value: 50, theme: _theme2.default });
+ (0, _expect2.default)(slider.props.className).toNotContain(_theme2.default.ring);
+ });
+ });
+
+ describe('#events', function () {
+ beforeEach(function () {
+ onChange = _sinon2.default.spy();
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Slider2.default, { min: -500, max: 500, onChange: onChange }));
+ slider = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, _Slider.Slider);
+ slider.setState({ sliderStart: 0, sliderLength: 1000 });
+ slider.handleResize = function (event, callback) {
+ callback();
+ };
+ });
+
+ it('sets pressed state when knob is clicked', function () {
+ _reactAddonsTestUtils2.default.Simulate.mouseDown(slider.refs.knob);
+ (0, _expect2.default)(slider.state.pressed).toEqual(true);
+ });
+
+ it('sets pressed state when knob is touched', function () {
+ _reactAddonsTestUtils2.default.Simulate.touchStart(slider.refs.knob, { touches: [{ pageX: 200 }] });
+ (0, _expect2.default)(slider.state.pressed).toEqual(true);
+ });
+
+ it('sets a proper value when the slider is clicked', function () {
+ _reactAddonsTestUtils2.default.Simulate.mouseDown(slider.refs.slider, { pageX: 200 });
+ (0, _expect2.default)(onChange.called).toEqual(true);
+ (0, _expect2.default)(onChange.getCall(0).args[0]).toEqual(-300);
+ });
+
+ it('sets a proper value when the slider is touched', function () {
+ _reactAddonsTestUtils2.default.Simulate.touchStart(slider.refs.slider, { touches: [{ pageX: 200, pageY: 0 }] });
+ (0, _expect2.default)(onChange.called).toEqual(true);
+ (0, _expect2.default)(onChange.getCall(0).args[0]).toEqual(-300);
+ });
+
+ it('changes input value when slider changes', function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Slider2.default, { editable: true, onChange: onChange }));
+ slider = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, _Slider.Slider);
+ slider.setState({ sliderStart: 0, sliderLength: 1000 });
+ slider.handleResize = function (event, callback) {
+ callback();
+ };
+ input = _reactAddonsTestUtils2.default.findRenderedComponentWithType(slider, _Input2.default);
+ _reactAddonsTestUtils2.default.Simulate.mouseDown(slider.refs.slider, { pageX: 900 });
+ (0, _expect2.default)(onChange.called).toEqual(true);
+ (0, _expect2.default)(onChange.getCall(0).args[0]).toEqual(90);
+ });
+
+ it('changes its value when input is blurred', function () {
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Slider2.default, { editable: true, value: 50, onChange: onChange }));
+ slider = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, _Slider.Slider);
+ input = _reactAddonsTestUtils2.default.findRenderedComponentWithType(slider, _Input.Input);
+ _reactAddonsTestUtils2.default.Simulate.change(input.refs.input, { target: { value: '80' } });
+ _reactAddonsTestUtils2.default.Simulate.blur(input.refs.input);
+ (0, _expect2.default)(onChange.called).toEqual(true);
+ (0, _expect2.default)(onChange.getCall(0).args[0]).toEqual(80);
+ });
+
+ it('calls onChange callback when the value is changed', function () {
+ var onChangeSpy = _sinon2.default.spy();
+ var tree = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(_Slider2.default, { onChange: onChangeSpy }));
+ slider = _reactAddonsTestUtils2.default.findRenderedComponentWithType(tree, _Slider.Slider);
+ slider.setState({ sliderStart: 0, sliderLength: 1000 });
+ _reactAddonsTestUtils2.default.Simulate.mouseDown(slider.refs.slider, { pageX: 900 });
+ (0, _expect2.default)(onChangeSpy.called).toEqual(true);
+ });
+ });
+});
\ No newline at end of file
diff --git a/lib/slider/_config.scss b/lib/slider/_config.scss
new file mode 100644
index 000000000..a5b0799b2
--- /dev/null
+++ b/lib/slider/_config.scss
@@ -0,0 +1,12 @@
+$slider-main-color: $color-primary !default;
+$slider-main-color-contrast: $color-primary-contrast !default;
+$slider-snap-color: $color-black !default;
+$slider-knob-size: 3.2 * $unit !default;
+$slider-inner-knob-size: 1.2 * $unit !default;
+$slider-snap-size: .2 * $unit !default;
+$slider-input-width: 5 * $unit !default;
+$slider-bar-height: .2 * $unit !default;
+$slider-pin-size: 2.6 * $unit !default;
+$slider-pin-elevation: 1.7 * $unit !default;
+$slider-side-separation: 1 * $unit !default;
+$slider-empty-knob-border: .2 * $unit !default;
diff --git a/lib/slider/index.js b/lib/slider/index.js
new file mode 100644
index 000000000..ddcc2d7f2
--- /dev/null
+++ b/lib/slider/index.js
@@ -0,0 +1,26 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Slider = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _progress_bar = require('../progress_bar');
+
+var _input = require('../input');
+
+var _Slider = require('./Slider.js');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ThemedSlider = (0, _reactCssThemr.themr)(_identifiers.SLIDER, _theme2.default)((0, _Slider.sliderFactory)(_progress_bar.ProgressBar, _input.Input));
+exports.default = ThemedSlider;
+exports.Slider = ThemedSlider;
\ No newline at end of file
diff --git a/lib/slider/theme.scss b/lib/slider/theme.scss
new file mode 100644
index 000000000..bf3468152
--- /dev/null
+++ b/lib/slider/theme.scss
@@ -0,0 +1,193 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.container {
+ position: relative;
+ width: calc(100% - #{$slider-knob-size});
+ height: $slider-knob-size;
+ margin-right: $slider-knob-size;
+ user-select: none;
+ &:not(:last-child) {
+ margin-right: $slider-side-separation + $slider-knob-size;
+ }
+ &:not(:first-child) {
+ margin-left: $slider-side-separation;
+ }
+}
+
+.knob {
+ position: relative;
+ top: 0;
+ left: 0;
+ z-index: $z-index-higher;
+ display: flex;
+ width: $slider-knob-size;
+ height: $slider-knob-size;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ background-color: transparent;
+}
+
+.innerknob {
+ z-index: $z-index-high;
+ width: $slider-inner-knob-size;
+ height: $slider-inner-knob-size;
+ background-color: $slider-main-color;
+ border-radius: 50%;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: .1s;
+ transition-property: height, width, background-color, border;
+}
+
+.snaps {
+ position: absolute;
+ top: $slider-knob-size / 2 - $slider-snap-size / 2;
+ left: 0;
+ display: flex;
+ width: calc(100% + #{$slider-snap-size});
+ height: $slider-snap-size;
+ flex-direction: row;
+ pointer-events: none;
+ &:after {
+ display: block;
+ width: $slider-snap-size;
+ height: $slider-snap-size;
+ content: "";
+ background-color: $slider-snap-color;
+ border-radius: 50%;
+ }
+}
+
+.snap {
+ flex: 1;
+ &:after {
+ display: block;
+ width: $slider-snap-size;
+ height: $slider-snap-size;
+ content: "";
+ background-color: $slider-snap-color;
+ border-radius: 50%;
+ }
+}
+
+.input {
+ width: $slider-input-width;
+ padding: 0;
+ margin-bottom: 0;
+ > input {
+ text-align: center;
+ }
+}
+
+.progress {
+ position: absolute;
+ top: 0;
+ left: $slider-knob-size / 2;
+ width: 100%;
+ height: 100%;
+ .innerprogress {
+ position: absolute;
+ top: $slider-knob-size / 2 - $slider-bar-height / 2;
+ height: $slider-bar-height;
+ [data-ref="value"] {
+ transition-duration: 0s;
+ }
+ }
+}
+
+.slider {
+ &:focus .knob:before {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: $z-index-normal;
+ content: "";
+ background-color: $slider-main-color;
+ border-radius: 50%;
+ opacity: .26;
+ }
+ &.editable {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ }
+ &.pinned .innerknob {
+ &:before {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: $slider-pin-size;
+ height: $slider-pin-size;
+ margin-left: ($slider-knob-size - $slider-pin-size) / 2;
+ content: "";
+ background-color: $slider-main-color;
+ border-radius: 50% 50% 50% 0;
+ transition: transform .2s ease, background-color .18s ease;
+ transform: rotate(-45deg) scale(0) translate(0);
+ }
+ &:after {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: $slider-knob-size;
+ height: $slider-pin-size;
+ font-size: 10px;
+ color: $color-background;
+ text-align: center;
+ content: attr(data-value);
+ transition: transform .2s ease, background-color .18s ease;
+ transform: scale(0) translate(0);
+ }
+ }
+ &.pressed {
+ &.pinned .innerknob {
+ &:before {
+ transition-delay: 100ms;
+ transform: rotate(-45deg) scale(1) translate($slider-pin-elevation, - $slider-pin-elevation);
+ }
+ &:after {
+ transition-delay: 100ms;
+ transform: scale(1) translate(0, - $slider-pin-elevation);
+ }
+ }
+ &:not(.pinned) {
+ &.ring .progress {
+ left: $slider-knob-size / 2 + ($slider-knob-size - $slider-empty-knob-border * 2) / 2;
+ width: calc(100% - #{($slider-knob-size - $slider-empty-knob-border * 2) / 2});
+ }
+ .innerknob {
+ width: 100%;
+ height: 100%;
+ transform: translateZ(0);
+ }
+ }
+ }
+ &.ring {
+ .innerknob {
+ background-color: transparent;
+ border: $slider-empty-knob-border solid $color-divider;
+ &:before {
+ background-color: $slider-main-color;
+ }
+ }
+ .progress {
+ left: $slider-knob-size / 2 + $slider-empty-knob-border * 2;
+ width: calc(100% - #{$slider-empty-knob-border * 2});
+ transition: left .18s ease, width .18s ease;
+ }
+ &.pinned {
+ .innerknob {
+ background-color: $color-background;
+ }
+ .progress {
+ left: $slider-knob-size / 2;
+ width: calc(100%);
+ }
+ }
+ }
+}
diff --git a/lib/snackbar/Snackbar.js b/lib/snackbar/Snackbar.js
new file mode 100644
index 000000000..07e157927
--- /dev/null
+++ b/lib/snackbar/Snackbar.js
@@ -0,0 +1,135 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Snackbar = exports.snackbarFactory = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _ActivableRenderer = require('../hoc/ActivableRenderer.js');
+
+var _ActivableRenderer2 = _interopRequireDefault(_ActivableRenderer);
+
+var _FontIcon = require('../font_icon/FontIcon.js');
+
+var _FontIcon2 = _interopRequireDefault(_FontIcon);
+
+var _Overlay = require('../overlay/Overlay.js');
+
+var _Overlay2 = _interopRequireDefault(_Overlay);
+
+var _Button = require('../button/Button.js');
+
+var _Button2 = _interopRequireDefault(_Button);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(Overlay, Button) {
+ var Snackbar = function (_Component) {
+ _inherits(Snackbar, _Component);
+
+ function Snackbar() {
+ _classCallCheck(this, Snackbar);
+
+ return _possibleConstructorReturn(this, Object.getPrototypeOf(Snackbar).apply(this, arguments));
+ }
+
+ _createClass(Snackbar, [{
+ key: 'componentWillReceiveProps',
+ value: function componentWillReceiveProps(nextProps) {
+ var _this2 = this;
+
+ if (nextProps.active && nextProps.timeout) {
+ if (this.curTimeout) clearTimeout(this.curTimeout);
+ this.curTimeout = setTimeout(function () {
+ nextProps.onTimeout();
+ _this2.curTimeout = null;
+ }, nextProps.timeout);
+ }
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var action = _props.action;
+ var active = _props.active;
+ var icon = _props.icon;
+ var label = _props.label;
+ var onClick = _props.onClick;
+ var theme = _props.theme;
+ var type = _props.type;
+
+ var className = (0, _classnames3.default)([theme.snackbar, theme[type]], _defineProperty({}, theme.active, active), this.props.className);
+
+ return _react2.default.createElement(
+ Overlay,
+ { invisible: true },
+ _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'snackbar', className: className },
+ icon ? _react2.default.createElement(_FontIcon2.default, { value: icon, className: theme.icon }) : null,
+ _react2.default.createElement(
+ 'span',
+ { className: theme.label },
+ label
+ ),
+ action ? _react2.default.createElement(Button, { className: theme.button, label: action, onClick: onClick }) : null
+ )
+ );
+ }
+ }]);
+
+ return Snackbar;
+ }(_react.Component);
+
+ Snackbar.propTypes = {
+ action: _react.PropTypes.string,
+ active: _react.PropTypes.bool,
+ className: _react.PropTypes.string,
+ icon: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.element]),
+ label: _react.PropTypes.string,
+ onClick: _react.PropTypes.func,
+ onTimeout: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ accept: _react.PropTypes.string,
+ active: _react.PropTypes.string,
+ button: _react.PropTypes.string,
+ cancel: _react.PropTypes.string,
+ icon: _react.PropTypes.string,
+ label: _react.PropTypes.string,
+ snackbar: _react.PropTypes.string,
+ warning: _react.PropTypes.string
+ }),
+ timeout: _react.PropTypes.number,
+ type: _react.PropTypes.oneOf(['accept', 'cancel', 'warning'])
+ };
+
+
+ return (0, _ActivableRenderer2.default)()(Snackbar);
+};
+
+var Snackbar = factory(_Overlay2.default, _Button2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.SNACKBAR)(Snackbar);
+exports.snackbarFactory = factory;
+exports.Snackbar = Snackbar;
\ No newline at end of file
diff --git a/lib/snackbar/_config.scss b/lib/snackbar/_config.scss
new file mode 100644
index 000000000..69f7cdf50
--- /dev/null
+++ b/lib/snackbar/_config.scss
@@ -0,0 +1,9 @@
+$snackbar-color-cancel: $palette-red-500 !default;
+$snackbar-color-accept: $palette-green-500 !default;
+$snackbar-color-warning: $palette-lime-a200 !default;
+$snackbar-background-color: $color-text !default;
+$snackbar-border-radius: .2 * $unit !default;
+$snackbar-button-offset: 4.8 * $unit !default;
+$snackbar-color: $color-white !default;
+$snackbar-horizontal-offset: 2.4 * $unit !default;
+$snackbar-vertical-offset: 1.4 * $unit !default;
diff --git a/lib/snackbar/index.js b/lib/snackbar/index.js
new file mode 100644
index 000000000..4af5c61fd
--- /dev/null
+++ b/lib/snackbar/index.js
@@ -0,0 +1,27 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Snackbar = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Snackbar = require('./Snackbar.js');
+
+var _overlay = require('../overlay');
+
+var _button = require('../button');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var ThemedSnackbar = (0, _reactCssThemr.themr)(_identifiers.SNACKBAR, _theme2.default)((0, _Snackbar.snackbarFactory)(_overlay.Overlay, _button.Button));
+
+exports.default = ThemedSnackbar;
+exports.Snackbar = ThemedSnackbar;
\ No newline at end of file
diff --git a/lib/snackbar/theme.scss b/lib/snackbar/theme.scss
new file mode 100644
index 000000000..e9e7071ac
--- /dev/null
+++ b/lib/snackbar/theme.scss
@@ -0,0 +1,53 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.snackbar {
+ position: fixed;
+ right: $snackbar-horizontal-offset;
+ bottom: 0;
+ left: $snackbar-horizontal-offset;
+ z-index: $z-index-higher;
+ display: flex;
+ align-items: center;
+ padding: $snackbar-vertical-offset $snackbar-horizontal-offset;
+ margin: 0 auto;
+ margin-top: $snackbar-vertical-offset;
+ color: $snackbar-color;
+ background-color: $snackbar-background-color;
+ border-radius: $snackbar-border-radius;
+ transition: all $animation-duration $animation-curve-default $animation-duration;
+ &.accept .button {
+ color: $snackbar-color-accept;
+ }
+ &.warning .button {
+ color: $snackbar-color-warning;
+ }
+ &.cancel .button {
+ color: $snackbar-color-cancel;
+ }
+ &:not(.active) {
+ transform: translateY(100%);
+ }
+ &.active {
+ transform: translateY(0%);
+ }
+}
+
+.icon {
+ margin-right: $snackbar-vertical-offset;
+}
+
+.label {
+ flex-grow: 1;
+ font-size: $font-size-small;
+}
+
+.button {
+ min-width: inherit;
+ margin-top: - $snackbar-vertical-offset / 2;
+ margin-right: - $snackbar-horizontal-offset / 2;
+ margin-bottom: - $snackbar-vertical-offset / 2;
+ margin-left: $snackbar-button-offset;
+}
diff --git a/lib/switch/Switch.js b/lib/switch/Switch.js
new file mode 100644
index 000000000..96ad9977a
--- /dev/null
+++ b/lib/switch/Switch.js
@@ -0,0 +1,152 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Switch = exports.switchFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Ripple = require('../ripple/Ripple.js');
+
+var _Ripple2 = _interopRequireDefault(_Ripple);
+
+var _Thumb = require('./Thumb.js');
+
+var _Thumb2 = _interopRequireDefault(_Thumb);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(Thumb) {
+ var Switch = function (_Component) {
+ _inherits(Switch, _Component);
+
+ function Switch() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Switch);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Switch)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleToggle = function (event) {
+ if (event.pageX !== 0 && event.pageY !== 0) _this.blur();
+ if (!_this.props.disabled && _this.props.onChange) {
+ _this.props.onChange(!_this.props.checked, event);
+ }
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Switch, [{
+ key: 'blur',
+ value: function blur() {
+ this.refs.input.blur();
+ }
+ }, {
+ key: 'focus',
+ value: function focus() {
+ this.refs.input.focus();
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var className = _props.className;
+ var checked = _props.checked;
+ var disabled = _props.disabled;
+ var onChange = _props.onChange;
+ var theme = _props.theme;
+
+ var others = _objectWithoutProperties(_props, ['className', 'checked', 'disabled', 'onChange', 'theme']); //eslint-disable-line no-unused-vars
+
+
+ var _className = (0, _classnames2.default)(theme[disabled ? 'disabled' : 'field'], className);
+ return _react2.default.createElement(
+ 'label',
+ { 'data-react-toolbox': 'switch', className: _className },
+ _react2.default.createElement('input', _extends({}, others, {
+ checked: this.props.checked,
+ className: theme.input,
+ onClick: this.handleToggle,
+ readOnly: true,
+ ref: 'input',
+ type: 'checkbox'
+ })),
+ _react2.default.createElement(
+ 'span',
+ { className: theme[checked ? 'on' : 'off'] },
+ _react2.default.createElement(Thumb, { disabled: this.props.disabled, theme: theme })
+ ),
+ this.props.label ? _react2.default.createElement(
+ 'span',
+ { className: theme.text },
+ this.props.label
+ ) : null
+ );
+ }
+ }]);
+
+ return Switch;
+ }(_react.Component);
+
+ Switch.propTypes = {
+ checked: _react.PropTypes.bool,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ label: _react.PropTypes.string,
+ name: _react.PropTypes.string,
+ onBlur: _react.PropTypes.func,
+ onChange: _react.PropTypes.func,
+ onFocus: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ disabled: _react.PropTypes.string,
+ field: _react.PropTypes.string,
+ input: _react.PropTypes.string,
+ off: _react.PropTypes.string,
+ on: _react.PropTypes.string,
+ ripple: _react.PropTypes.string,
+ text: _react.PropTypes.string,
+ thumb: _react.PropTypes.string
+ })
+ };
+ Switch.defaultProps = {
+ checked: false,
+ className: '',
+ disabled: false
+ };
+
+
+ return Switch;
+};
+
+var Thumb = (0, _Thumb2.default)((0, _Ripple2.default)({ centered: true, spread: 2.6 }));
+var Switch = factory(Thumb);
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.SWITCH)(Switch);
+exports.switchFactory = factory;
+exports.Switch = Switch;
\ No newline at end of file
diff --git a/lib/switch/Thumb.js b/lib/switch/Thumb.js
new file mode 100644
index 000000000..07af70068
--- /dev/null
+++ b/lib/switch/Thumb.js
@@ -0,0 +1,38 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+var factory = function factory(ripple) {
+ var Thumb = function Thumb(_ref) {
+ var onMouseDown = _ref.onMouseDown;
+ var theme = _ref.theme;
+
+ var other = _objectWithoutProperties(_ref, ['onMouseDown', 'theme']);
+
+ return _react2.default.createElement('span', _extends({ role: 'thumb', className: theme.thumb, onMouseDown: onMouseDown }, other));
+ };
+
+ Thumb.propTypes = {
+ children: _react.PropTypes.any,
+ theme: _react.PropTypes.shape({
+ ripple: _react.PropTypes.string,
+ thumb: _react.PropTypes.string
+ })
+ };
+
+ return ripple(Thumb);
+};
+
+exports.default = factory;
\ No newline at end of file
diff --git a/lib/switch/_config.scss b/lib/switch/_config.scss
new file mode 100644
index 000000000..784084717
--- /dev/null
+++ b/lib/switch/_config.scss
@@ -0,0 +1,22 @@
+$switch-color: $color-primary !default;
+$switch-text-color: $color-black !default;
+$switch-thumb-off-color: $palette-grey-50 !default;
+$switch-track-on-color: rgba($color-primary, 0.5) !default;
+$switch-track-off-color: rgba($color-black, 0.26) !default;
+$switch-off-ripple-color: rgba($color-black, 0.4) !default;
+$switch-on-focus-color: rgba($color-primary, 0.26) !default;
+$switch-off-focus-color: rgba($color-black, 0.1) !default;
+$switch-disabled-thumb-color: $palette-grey-400 !default;
+$switch-disabled-track-color: rgba($color-black, 0.12) !default;
+$switch-disabled-text-color: rgba($color-black, 0.26) !default;
+$switch-total-height: 2.4 * $unit !default;
+$switch-track-length: 3.6 * $unit !default;
+$switch-track-height: 1.4 * $unit !default;
+$switch-thumb-size: 2 * $unit !default;
+$switch-thumb-on-color: $switch-color !default;
+$switch-focus-init-size: .8 * $unit !default;
+$switch-focus-size: $switch-total-height * 2 !default;
+$switch-focus-diff: ($switch-focus-size - $switch-focus-init-size) / 2 !default;
+$switch-ripple-duration: 650ms !default;
+$switch-font-size: $font-size-small !default;
+$switch-field-margin-bottom: 1.5 * $unit !default;
diff --git a/lib/switch/index.js b/lib/switch/index.js
new file mode 100644
index 000000000..6e717b50d
--- /dev/null
+++ b/lib/switch/index.js
@@ -0,0 +1,36 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Switch = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _Switch = require('./Switch.js');
+
+var _identifiers = require('../identifiers.js');
+
+var _Thumb = require('./Thumb.js');
+
+var _Thumb2 = _interopRequireDefault(_Thumb);
+
+var _ripple = require('../ripple');
+
+var _ripple2 = _interopRequireDefault(_ripple);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var applyTheme = function applyTheme(Component) {
+ return (0, _reactCssThemr.themr)(_identifiers.SWITCH, _theme2.default)(Component);
+};
+var ripple = (0, _ripple2.default)({ centered: true, spread: 2.6 });
+var ThemedThumb = applyTheme((0, _Thumb2.default)(ripple));
+var ThemedSwitch = applyTheme((0, _Switch.switchFactory)(ThemedThumb));
+
+exports.default = ThemedSwitch;
+exports.Switch = ThemedSwitch;
\ No newline at end of file
diff --git a/lib/switch/theme.scss b/lib/switch/theme.scss
new file mode 100644
index 000000000..c01664379
--- /dev/null
+++ b/lib/switch/theme.scss
@@ -0,0 +1,123 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.field {
+ position: relative;
+ display: block;
+ height: $switch-total-height;
+ margin-bottom: $switch-field-margin-bottom;
+ white-space: nowrap;
+ vertical-align: middle;
+}
+
+.text {
+ display: inline-block;
+ padding-left: $unit;
+ font-size: $switch-font-size;
+ line-height: $switch-total-height;
+ color: $switch-text-color;
+ white-space: nowrap;
+ vertical-align: top;
+}
+
+%switch {
+ position: relative;
+ display: inline-block;
+ width: $switch-track-length;
+ height: $switch-track-height;
+ margin-top: ($switch-total-height - $switch-track-height) / 2;
+ vertical-align: top;
+ cursor: pointer;
+ border-radius: $switch-track-height;
+}
+
+.thumb {
+ @include material-animation-default(.28s);
+ position: absolute;
+ top: ($switch-track-height - $switch-thumb-size) / 2;
+ width: $switch-thumb-size;
+ height: $switch-thumb-size;
+ cursor: pointer;
+ border-radius: 50%;
+ transition-property: left;
+
+ .ripple {
+ background-color: $switch-color;
+ opacity: .3;
+ transition-duration: $switch-ripple-duration;
+ }
+}
+
+.on {
+ @extend %switch;
+ background: $switch-track-on-color;
+ .thumb {
+ @include shadow-3dp();
+ left: $switch-track-length - $switch-thumb-size;
+ background: $switch-thumb-on-color;
+ }
+}
+
+.off {
+ @extend %switch;
+ background: $switch-track-off-color;
+ .thumb {
+ @include shadow-2dp();
+ left: 0;
+ background: $switch-thumb-off-color;
+ }
+ .ripple {
+ background: $switch-off-ripple-color;
+ }
+}
+
+%thumb-focus {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ box-sizing: border-box;
+ display: inline-block;
+ width: $switch-focus-init-size;
+ height: $switch-focus-init-size;
+ content: "";
+ background-color: transparent;
+ border-radius: 50%;
+ transform: translate(- $switch-focus-init-size / 2, - $switch-focus-init-size / 2);
+}
+
+.input {
+ width: 0;
+ height: 0;
+ overflow: hidden;
+ opacity: 0;
+ &:focus:not(:active) {
+ + .switch-on > .thumb:before {
+ @extend %thumb-focus;
+ background-color: $switch-on-focus-color;
+ box-shadow: 0 0 0 $switch-focus-diff $switch-on-focus-color;
+ }
+ + .switch-off > .thumb:before {
+ @extend %thumb-focus;
+ background-color: $switch-off-focus-color;
+ box-shadow: 0 0 0 $switch-focus-diff $switch-off-focus-color;
+ }
+ }
+}
+
+.disabled {
+ @extend .field;
+ .text {
+ color: $switch-disabled-text-color;
+ }
+ .on, .off {
+ cursor: auto;
+ background: $switch-disabled-track-color;
+ }
+ .thumb {
+ cursor: auto;
+ background-color: $switch-disabled-thumb-color;
+ border-color: transparent;
+ }
+}
diff --git a/lib/table/Table.js b/lib/table/Table.js
new file mode 100644
index 000000000..f46bb78f2
--- /dev/null
+++ b/lib/table/Table.js
@@ -0,0 +1,196 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Table = exports.tableFactory = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Checkbox = require('../checkbox/Checkbox.js');
+
+var _Checkbox2 = _interopRequireDefault(_Checkbox);
+
+var _TableHead = require('./TableHead.js');
+
+var _TableHead2 = _interopRequireDefault(_TableHead);
+
+var _TableRow = require('./TableRow.js');
+
+var _TableRow2 = _interopRequireDefault(_TableRow);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(TableHead, TableRow) {
+ var Table = function (_Component) {
+ _inherits(Table, _Component);
+
+ function Table() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Table);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Table)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleFullSelect = function () {
+ if (_this.props.onSelect) {
+ var _this$props = _this.props;
+ var source = _this$props.source;
+ var selected = _this$props.selected;
+
+ var newSelected = source.length === selected.length ? [] : source.map(function (i, idx) {
+ return idx;
+ });
+ _this.props.onSelect(newSelected);
+ }
+ }, _this.handleRowSelect = function (index) {
+ if (_this.props.onSelect) {
+ var position = _this.props.selected.indexOf(index);
+ var newSelected = [].concat(_toConsumableArray(_this.props.selected));
+ if (position !== -1) {
+ newSelected.splice(position, 1);
+ }
+ if (position !== -1 && _this.props.multiSelectable) {
+ newSelected.push(index);
+ } else {
+ newSelected = [index];
+ }
+ _this.props.onSelect(newSelected);
+ }
+ }, _this.handleRowChange = function (index, key, value) {
+ if (_this.props.onChange) {
+ _this.props.onChange(index, key, value);
+ }
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Table, [{
+ key: 'renderHead',
+ value: function renderHead() {
+ if (this.props.heading) {
+ var _props = this.props;
+ var model = _props.model;
+ var selected = _props.selected;
+ var source = _props.source;
+ var selectable = _props.selectable;
+ var multiSelectable = _props.multiSelectable;
+
+ var isSelected = selected.length === source.length;
+ return _react2.default.createElement(TableHead, {
+ model: model,
+ onSelect: this.handleFullSelect,
+ selectable: selectable,
+ multiSelectable: multiSelectable,
+ selected: isSelected,
+ theme: this.props.theme
+ });
+ }
+ }
+ }, {
+ key: 'renderBody',
+ value: function renderBody() {
+ var _this2 = this;
+
+ var _props2 = this.props;
+ var source = _props2.source;
+ var model = _props2.model;
+ var onChange = _props2.onChange;
+ var selectable = _props2.selectable;
+ var selected = _props2.selected;
+ var theme = _props2.theme;
+
+ return _react2.default.createElement(
+ 'tbody',
+ null,
+ source.map(function (data, index) {
+ return _react2.default.createElement(TableRow, {
+ data: data,
+ index: index,
+ key: index,
+ model: model,
+ onChange: onChange ? _this2.handleRowChange.bind(_this2) : undefined,
+ onSelect: _this2.handleRowSelect.bind(_this2, index),
+ selectable: selectable,
+ selected: selected.indexOf(index) !== -1,
+ theme: theme
+ });
+ })
+ );
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props3 = this.props;
+ var className = _props3.className;
+ var theme = _props3.theme;
+
+ return _react2.default.createElement(
+ 'table',
+ { 'data-react-toolbox': 'table', className: (0, _classnames2.default)(theme.table, className) },
+ this.renderHead(),
+ this.renderBody()
+ );
+ }
+ }]);
+
+ return Table;
+ }(_react.Component);
+
+ Table.propTypes = {
+ className: _react.PropTypes.string,
+ heading: _react.PropTypes.bool,
+ model: _react.PropTypes.object,
+ multiSelectable: _react.PropTypes.bool,
+ onChange: _react.PropTypes.func,
+ onSelect: _react.PropTypes.func,
+ selectable: _react.PropTypes.bool,
+ selected: _react.PropTypes.array,
+ source: _react.PropTypes.array,
+ theme: _react.PropTypes.shape({
+ table: _react.PropTypes.string
+ })
+ };
+ Table.defaultProps = {
+ className: '',
+ heading: true,
+ selectable: true,
+ multiSelectable: true,
+ selected: [],
+ source: []
+ };
+
+
+ return Table;
+};
+
+var TableHead = (0, _TableHead2.default)(_Checkbox2.default);
+var TableRow = (0, _TableRow2.default)(_Checkbox2.default);
+var Table = factory(TableHead, TableRow);
+
+exports.default = (0, _reactCssThemr.themr)(_identifiers.TABLE)(Table);
+exports.tableFactory = factory;
+exports.Table = Table;
\ No newline at end of file
diff --git a/lib/table/TableHead.js b/lib/table/TableHead.js
new file mode 100644
index 000000000..dcc71513b
--- /dev/null
+++ b/lib/table/TableHead.js
@@ -0,0 +1,75 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+var factory = function factory(Checkbox) {
+ var TableHead = function TableHead(_ref) {
+ var model = _ref.model;
+ var onSelect = _ref.onSelect;
+ var selectable = _ref.selectable;
+ var multiSelectable = _ref.multiSelectable;
+ var selected = _ref.selected;
+ var theme = _ref.theme;
+
+ var selectCell = void 0;
+ var contentCells = Object.keys(model).map(function (key) {
+ var name = model[key].title || key;
+ return _react2.default.createElement(
+ 'th',
+ { key: key },
+ name
+ );
+ });
+
+ if (selectable && multiSelectable) {
+ selectCell = _react2.default.createElement(
+ 'th',
+ { key: 'select', className: theme.selectable },
+ _react2.default.createElement(Checkbox, { onChange: onSelect, checked: selected })
+ );
+ } else if (selectable) {
+ selectCell = _react2.default.createElement('th', { key: 'select', className: theme.selectable });
+ }
+ return _react2.default.createElement(
+ 'thead',
+ null,
+ _react2.default.createElement(
+ 'tr',
+ null,
+ [selectCell].concat(_toConsumableArray(contentCells))
+ )
+ );
+ };
+
+ TableHead.propTypes = {
+ className: _react.PropTypes.string,
+ model: _react.PropTypes.object,
+ multiSelectable: _react.PropTypes.bool,
+ onSelect: _react.PropTypes.func,
+ selectable: _react.PropTypes.bool,
+ selected: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ selectable: _react.PropTypes.string
+ })
+ };
+
+ TableHead.defaultProps = {
+ className: '',
+ model: {},
+ selected: false
+ };
+
+ return TableHead;
+};
+
+exports.default = factory;
\ No newline at end of file
diff --git a/lib/table/TableRow.js b/lib/table/TableRow.js
new file mode 100644
index 000000000..ae0a25ccc
--- /dev/null
+++ b/lib/table/TableRow.js
@@ -0,0 +1,148 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _utils = require('../utils/utils');
+
+var _utils2 = _interopRequireDefault(_utils);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(Checkbox) {
+ var TableRow = function (_Component) {
+ _inherits(TableRow, _Component);
+
+ function TableRow() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, TableRow);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(TableRow)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleInputChange = function (index, key, type, event) {
+ var value = type === 'checkbox' ? event.target.checked : event.target.value;
+ var onChange = _this.props.model[key].onChange || _this.props.onChange;
+ onChange(index, key, value);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(TableRow, [{
+ key: 'renderSelectCell',
+ value: function renderSelectCell() {
+ if (this.props.selectable) {
+ return _react2.default.createElement(
+ 'td',
+ { className: this.props.theme.selectable },
+ _react2.default.createElement(Checkbox, { checked: this.props.selected, onChange: this.props.onSelect })
+ );
+ }
+ }
+ }, {
+ key: 'renderCells',
+ value: function renderCells() {
+ var _this2 = this;
+
+ return Object.keys(this.props.model).map(function (key) {
+ return _react2.default.createElement(
+ 'td',
+ { key: key },
+ _this2.renderCell(key)
+ );
+ });
+ }
+ }, {
+ key: 'renderCell',
+ value: function renderCell(key) {
+ var value = this.props.data[key];
+
+ // if the value is a valid React element return it directly, since it
+ // cannot be edited and should not be converted to a string...
+ if (_react2.default.isValidElement(value)) {
+ return value;
+ }
+
+ var onChange = this.props.model[key].onChange || this.props.onChange;
+ if (onChange) {
+ return this.renderInput(key, value);
+ } else if (value) {
+ return value.toString();
+ }
+ }
+ }, {
+ key: 'renderInput',
+ value: function renderInput(key, value) {
+ var index = this.props.index;
+ var inputType = _utils2.default.inputTypeForPrototype(this.props.model[key].type);
+ var inputValue = _utils2.default.prepareValueForInput(value, inputType);
+ var checked = inputType === 'checkbox' && value ? true : null;
+ return _react2.default.createElement('input', {
+ checked: checked,
+ onChange: this.handleInputChange.bind(null, index, key, inputType),
+ type: inputType,
+ value: inputValue
+ });
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _classnames;
+
+ var className = (0, _classnames3.default)(this.props.theme.row, (_classnames = {}, _defineProperty(_classnames, this.props.theme.editable, this.props.onChange), _defineProperty(_classnames, this.props.theme.selected, this.props.selected), _classnames));
+
+ return _react2.default.createElement(
+ 'tr',
+ { 'data-react-toolbox-table': 'row', className: className },
+ this.renderSelectCell(),
+ this.renderCells()
+ );
+ }
+ }]);
+
+ return TableRow;
+ }(_react.Component);
+
+ TableRow.propTypes = {
+ data: _react.PropTypes.object,
+ index: _react.PropTypes.number,
+ model: _react.PropTypes.object,
+ onChange: _react.PropTypes.func,
+ onSelect: _react.PropTypes.func,
+ selectable: _react.PropTypes.bool,
+ selected: _react.PropTypes.bool,
+ theme: _react.PropTypes.shape({
+ editable: _react.PropTypes.string,
+ row: _react.PropTypes.string,
+ selectable: _react.PropTypes.string,
+ selected: _react.PropTypes.string
+ })
+ };
+
+
+ return TableRow;
+};
+
+exports.default = factory;
\ No newline at end of file
diff --git a/lib/table/_config.scss b/lib/table/_config.scss
new file mode 100644
index 000000000..7ec3b5a2b
--- /dev/null
+++ b/lib/table/_config.scss
@@ -0,0 +1,5 @@
+$table-row-height: 48px !default;
+$table-row-divider: solid 1px rgba(0,0,0,.12) !default;
+$table-row-offset: 1.8 * $unit !default;
+$table-row-highlight: #eee !default;
+$table-text-color: #757575 !default;
diff --git a/lib/table/index.js b/lib/table/index.js
new file mode 100644
index 000000000..994d96cb2
--- /dev/null
+++ b/lib/table/index.js
@@ -0,0 +1,38 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Table = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _checkbox = require('../checkbox');
+
+var _Table = require('./Table.js');
+
+var _TableHead = require('./TableHead.js');
+
+var _TableHead2 = _interopRequireDefault(_TableHead);
+
+var _TableRow = require('./TableRow.js');
+
+var _TableRow2 = _interopRequireDefault(_TableRow);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var applyTheme = function applyTheme(Component) {
+ return (0, _reactCssThemr.themr)(_identifiers.TABLE, _theme2.default)(Component);
+};
+var ThemedTableHead = applyTheme((0, _TableHead2.default)(_checkbox.Checkbox));
+var ThemedTableRow = applyTheme((0, _TableRow2.default)(_checkbox.Checkbox));
+var ThemedTable = applyTheme((0, _Table.tableFactory)(ThemedTableHead, ThemedTableRow));
+
+exports.default = ThemedTable;
+exports.Table = ThemedTable;
\ No newline at end of file
diff --git a/lib/table/theme.scss b/lib/table/theme.scss
new file mode 100644
index 000000000..50db57d34
--- /dev/null
+++ b/lib/table/theme.scss
@@ -0,0 +1,56 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.table {
+ width: 100%;
+ font-size: $font-size-tiny;
+ color: $table-text-color;
+ text-align: left;
+ tr {
+ height: $table-row-height;
+ line-height: $table-row-height;
+ border-bottom: $table-row-divider;
+ }
+ th {
+ font-weight: $font-weight-bold;
+ &:first-letter {
+ text-transform: capitalize;
+ }
+ }
+ th, td {
+ position: relative;
+ padding: 0 $table-row-offset;
+ &.selectable {
+ width: 1.8 * $unit;
+ padding-right: 0;
+ > * {
+ margin: 0;
+ }
+ }
+ }
+}
+
+.row {
+ transition: background-color $animation-duration $animation-curve-default;
+ &:last-child {
+ border-color: transparent;
+ }
+ > td {
+ > input {
+ display: block;
+ width: 100%;
+ background-color: transparent;
+ border: 0;
+ }
+ }
+}
+
+.selected, .row:hover {
+ background-color: $table-row-highlight;
+}
+
+.editable > * {
+ cursor: pointer;
+}
diff --git a/lib/tabs/Tab.js b/lib/tabs/Tab.js
new file mode 100644
index 000000000..2a744ddf5
--- /dev/null
+++ b/lib/tabs/Tab.js
@@ -0,0 +1,109 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Tab = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var Tab = function (_Component) {
+ _inherits(Tab, _Component);
+
+ function Tab() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Tab);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Tab)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleClick = function (event) {
+ if (!_this.props.disabled && _this.props.onClick) {
+ _this.props.onClick(event);
+ }
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Tab, [{
+ key: 'componentDidUpdate',
+ value: function componentDidUpdate(prevProps) {
+ if (!prevProps.active && this.props.active && this.props.onActive) {
+ this.props.onActive();
+ }
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _classnames;
+
+ var _props = this.props;
+ var active = _props.active;
+ var activeClassName = _props.activeClassName;
+ var hidden = _props.hidden;
+ var disabled = _props.disabled;
+ var className = _props.className;
+ var theme = _props.theme;
+
+ var _className = (0, _classnames3.default)(theme.label, (_classnames = {}, _defineProperty(_classnames, theme.active, active), _defineProperty(_classnames, theme.hidden, hidden), _defineProperty(_classnames, theme.disabled, disabled), _defineProperty(_classnames, activeClassName, active), _classnames), className);
+
+ return _react2.default.createElement(
+ 'label',
+ { 'data-react-toolbox': 'tab', className: _className, onClick: this.handleClick },
+ this.props.label
+ );
+ }
+ }]);
+
+ return Tab;
+}(_react.Component);
+
+Tab.propTypes = {
+ active: _react.PropTypes.bool,
+ activeClassName: _react.PropTypes.string,
+ className: _react.PropTypes.string,
+ disabled: _react.PropTypes.bool,
+ hidden: _react.PropTypes.bool,
+ label: _react.PropTypes.any.isRequired,
+ onActive: _react.PropTypes.func,
+ onClick: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ disabled: _react.PropTypes.string,
+ hidden: _react.PropTypes.string,
+ label: _react.PropTypes.string
+ })
+};
+Tab.defaultProps = {
+ active: false,
+ className: '',
+ disabled: false,
+ hidden: false
+};
+exports.default = (0, _reactCssThemr.themr)(_identifiers.TABS)(Tab);
+exports.Tab = Tab;
\ No newline at end of file
diff --git a/lib/tabs/TabContent.js b/lib/tabs/TabContent.js
new file mode 100644
index 000000000..96f3538be
--- /dev/null
+++ b/lib/tabs/TabContent.js
@@ -0,0 +1,72 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.TabContent = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var TabContent = function (_Component) {
+ _inherits(TabContent, _Component);
+
+ function TabContent() {
+ _classCallCheck(this, TabContent);
+
+ return _possibleConstructorReturn(this, Object.getPrototypeOf(TabContent).apply(this, arguments));
+ }
+
+ _createClass(TabContent, [{
+ key: 'render',
+ value: function render() {
+ var className = (0, _classnames3.default)(this.props.theme.tab, _defineProperty({}, this.props.theme.active, this.props.active), this.props.className);
+
+ return _react2.default.createElement(
+ 'section',
+ { className: className, tabIndex: this.props.tabIndex },
+ this.props.children
+ );
+ }
+ }]);
+
+ return TabContent;
+}(_react.Component);
+
+TabContent.propTypes = {
+ active: _react.PropTypes.bool,
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ tabIndex: _react.PropTypes.number,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ tab: _react.PropTypes.string
+ })
+};
+TabContent.defaultProps = {
+ active: false,
+ className: ''
+};
+exports.default = (0, _reactCssThemr.themr)(_identifiers.TABS)(TabContent);
+exports.TabContent = TabContent;
\ No newline at end of file
diff --git a/lib/tabs/Tabs.js b/lib/tabs/Tabs.js
new file mode 100644
index 000000000..c86982d92
--- /dev/null
+++ b/lib/tabs/Tabs.js
@@ -0,0 +1,194 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Tabs = exports.tabsFactory = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Tab = require('./Tab.js');
+
+var _Tab2 = _interopRequireDefault(_Tab);
+
+var _TabContent = require('./TabContent.js');
+
+var _TabContent2 = _interopRequireDefault(_TabContent);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(Tab, TabContent) {
+ var Tabs = function (_Component) {
+ _inherits(Tabs, _Component);
+
+ function Tabs() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Tabs);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Tabs)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ pointer: {}
+ }, _this.handleHeaderClick = function (idx) {
+ if (_this.props.onChange) _this.props.onChange(idx);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Tabs, [{
+ key: 'componentDidMount',
+ value: function componentDidMount() {
+ !this.props.disableAnimatedBottomBorder && this.updatePointer(this.props.index);
+ }
+ }, {
+ key: 'componentWillReceiveProps',
+ value: function componentWillReceiveProps(nextProps) {
+ !this.props.disableAnimatedBottomBorder && this.updatePointer(nextProps.index);
+ }
+ }, {
+ key: 'componentWillUnmount',
+ value: function componentWillUnmount() {
+ clearTimeout(this.pointerTimeout);
+ }
+ }, {
+ key: 'parseChildren',
+ value: function parseChildren() {
+ var headers = [];
+ var contents = [];
+
+ _react2.default.Children.forEach(this.props.children, function (item) {
+ if (item.type === Tab) {
+ headers.push(item);
+ if (item.props.children) {
+ contents.push(_react2.default.createElement(TabContent, { children: item.props.children }));
+ }
+ } else if (item.type === TabContent) {
+ contents.push(item);
+ }
+ });
+
+ return { headers: headers, contents: contents };
+ }
+ }, {
+ key: 'updatePointer',
+ value: function updatePointer(idx) {
+ var _this2 = this;
+
+ clearTimeout(this.pointerTimeout);
+ this.pointerTimeout = setTimeout(function () {
+ var startPoint = _this2.refs.tabs.getBoundingClientRect().left;
+ var label = _this2.refs.navigation.children[idx].getBoundingClientRect();
+ _this2.setState({
+ pointer: {
+ top: _this2.refs.navigation.getBoundingClientRect().height + 'px',
+ left: label.left - startPoint + 'px',
+ width: label.width + 'px'
+ }
+ });
+ }, 20);
+ }
+ }, {
+ key: 'renderHeaders',
+ value: function renderHeaders(headers) {
+ var _this3 = this;
+
+ return headers.map(function (item, idx) {
+ return _react2.default.cloneElement(item, {
+ key: idx,
+ active: _this3.props.index === idx,
+ onClick: _this3.handleHeaderClick.bind(_this3, idx, item)
+ });
+ });
+ }
+ }, {
+ key: 'renderContents',
+ value: function renderContents(contents) {
+ var _this4 = this;
+
+ var activeIdx = contents.findIndex(function (item, idx) {
+ return _this4.props.index === idx;
+ });
+
+ if (contents && contents[activeIdx]) {
+ return _react2.default.cloneElement(contents[activeIdx], {
+ key: activeIdx,
+ active: true,
+ tabIndex: activeIdx
+ });
+ }
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var className = _props.className;
+ var theme = _props.theme;
+
+ var _parseChildren = this.parseChildren();
+
+ var headers = _parseChildren.headers;
+ var contents = _parseChildren.contents;
+
+ return _react2.default.createElement(
+ 'div',
+ { ref: 'tabs', 'data-react-toolbox': 'tabs', className: (0, _classnames2.default)(theme.tabs, className) },
+ _react2.default.createElement(
+ 'nav',
+ { className: theme.navigation, ref: 'navigation' },
+ this.renderHeaders(headers)
+ ),
+ _react2.default.createElement('span', { className: theme.pointer, style: this.state.pointer }),
+ this.renderContents(contents)
+ );
+ }
+ }]);
+
+ return Tabs;
+ }(_react.Component);
+
+ Tabs.propTypes = {
+ children: _react.PropTypes.node,
+ className: _react.PropTypes.string,
+ disableAnimatedBottomBorder: _react.PropTypes.bool,
+ index: _react.PropTypes.number,
+ onChange: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ navigation: _react.PropTypes.string,
+ pointer: _react.PropTypes.string,
+ tabs: _react.PropTypes.string
+ })
+ };
+ Tabs.defaultProps = {
+ index: 0
+ };
+
+
+ return Tabs;
+};
+
+var Tabs = factory(_Tab2.default, _TabContent2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.TABS)(Tabs);
+exports.tabsFactory = factory;
+exports.Tabs = Tabs;
\ No newline at end of file
diff --git a/lib/tabs/__tests__/index.spec.js b/lib/tabs/__tests__/index.spec.js
new file mode 100644
index 000000000..9f6ffa9cf
--- /dev/null
+++ b/lib/tabs/__tests__/index.spec.js
@@ -0,0 +1,100 @@
+'use strict';
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _expect = require('expect');
+
+var _expect2 = _interopRequireDefault(_expect);
+
+var _testing = require('../../utils/testing');
+
+var _testing2 = _interopRequireDefault(_testing);
+
+var _reactAddonsTestUtils = require('react-addons-test-utils');
+
+var _reactAddonsTestUtils2 = _interopRequireDefault(_reactAddonsTestUtils);
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _Tabs = require('../Tabs');
+
+var _Tabs2 = _interopRequireDefault(_Tabs);
+
+var _Tab = require('../Tab');
+
+var _Tab2 = _interopRequireDefault(_Tab);
+
+var _TabContent = require('../TabContent');
+
+var _TabContent2 = _interopRequireDefault(_TabContent);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+describe('Tabs', function () {
+ var tabContents = void 0,
+ composition = void 0;
+
+ it('only renders the current tab', function () {
+ var Composition = function (_Component) {
+ _inherits(Composition, _Component);
+
+ function Composition() {
+ _classCallCheck(this, Composition);
+
+ var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Composition).call(this));
+
+ _this.state = { index: 0 };
+ return _this;
+ }
+
+ _createClass(Composition, [{
+ key: 'render',
+ value: function render() {
+ return _react2.default.createElement(
+ _Tabs2.default,
+ { index: this.state.index },
+ _react2.default.createElement(
+ _Tab2.default,
+ { label: 'tab1' },
+ 'tab1'
+ ),
+ _react2.default.createElement(
+ _Tab2.default,
+ { label: 'tab2' },
+ 'tab2'
+ )
+ );
+ }
+ }]);
+
+ return Composition;
+ }(_react.Component);
+
+ // initial render
+
+
+ composition = _testing2.default.renderComponent(Composition);
+
+ tabContents = _reactAddonsTestUtils2.default.scryRenderedComponentsWithType(composition, _TabContent2.default);
+
+ (0, _expect2.default)(tabContents.length).toEqual(1);
+ (0, _expect2.default)(tabContents[0].props.tabIndex).toEqual(0);
+
+ // after tab change
+ composition.setState({ index: 1 });
+ composition.forceUpdate();
+
+ tabContents = _reactAddonsTestUtils2.default.scryRenderedComponentsWithType(composition, _TabContent2.default);
+
+ (0, _expect2.default)(tabContents.length).toEqual(1);
+ (0, _expect2.default)(tabContents[0].props.tabIndex).toEqual(1);
+ });
+});
\ No newline at end of file
diff --git a/lib/tabs/_config.scss b/lib/tabs/_config.scss
new file mode 100644
index 000000000..f67e55875
--- /dev/null
+++ b/lib/tabs/_config.scss
@@ -0,0 +1,11 @@
+$tab-label-disabled-opacity: .2 !default;
+$tab-label-h-padding: 1.2 * $unit !default;
+$tab-label-height: 4.8 * $unit !default;
+$tab-text-height: 1.4 * $unit !default;
+$tab-label-v-padding: ($tab-label-height - $tab-text-height) / 2 !default;
+$tab-navigation-border-color: $color-divider !default;
+$tab-pointer-color: $color-primary !default;
+$tab-pointer-height: .2 * $unit !default;
+$tab-text: $color-black !default;
+$tab-text-color: $tab-text !default;
+$tab-text-inactive-color: rgba($tab-text, .7) !default;
diff --git a/lib/tabs/index.js b/lib/tabs/index.js
new file mode 100644
index 000000000..a96995f5c
--- /dev/null
+++ b/lib/tabs/index.js
@@ -0,0 +1,32 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.Tabs = exports.Tab = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _Tabs = require('./Tabs.js');
+
+var _TabContent = require('./TabContent.js');
+
+var _Tab = require('./Tab.js');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var applyTheme = function applyTheme(Component) {
+ return (0, _reactCssThemr.themr)(_identifiers.TABS, _theme2.default)(Component);
+};
+var ThemedTabContent = applyTheme(_TabContent.TabContent);
+var ThemedTab = applyTheme(_Tab.Tab);
+var ThemedTabs = applyTheme((0, _Tabs.tabsFactory)(ThemedTab, ThemedTabContent));
+
+exports.Tab = ThemedTab;
+exports.Tabs = ThemedTabs;
\ No newline at end of file
diff --git a/lib/tabs/theme.scss b/lib/tabs/theme.scss
new file mode 100644
index 000000000..f2e77760e
--- /dev/null
+++ b/lib/tabs/theme.scss
@@ -0,0 +1,63 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.tabs {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+}
+
+.navigation {
+ display: flex;
+ flex-direction: row;
+ box-shadow: inset 0 -1px $tab-navigation-border-color;
+}
+
+.label {
+ padding: $tab-label-v-padding $tab-label-h-padding;
+ font-size: $tab-text-height;
+ font-weight: $font-weight-semi-bold;
+ line-height: 1;
+ color: $tab-text-inactive-color;
+ text-transform: uppercase;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transition-property: box-shadow, color;
+ &.active {
+ color: $tab-text-color;
+ }
+ &.disabled {
+ opacity: $tab-label-disabled-opacity;
+ }
+ &:not(.disabled) {
+ cursor: pointer;
+ }
+ &.hidden {
+ display: none;
+ }
+}
+
+.pointer {
+ position: absolute;
+ width: 0;
+ height: $tab-pointer-height;
+ margin-top: - $tab-pointer-height;
+ background-color: $tab-pointer-color;
+ transition-timing-function: $animation-curve-default;
+ transition-duration: $animation-duration;
+ transition-property: left, width;
+}
+
+.tab {
+ display: flex;
+ flex-direction: column;
+ padding: $tab-label-v-padding $tab-label-h-padding;
+ &:not(.active) {
+ display: none;
+ }
+ &.active {
+ display: block;
+ }
+}
diff --git a/lib/time_picker/Clock.js b/lib/time_picker/Clock.js
new file mode 100644
index 000000000..5891b3cce
--- /dev/null
+++ b/lib/time_picker/Clock.js
@@ -0,0 +1,182 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactAddonsCssTransitionGroup = require('react-addons-css-transition-group');
+
+var _reactAddonsCssTransitionGroup2 = _interopRequireDefault(_reactAddonsCssTransitionGroup);
+
+var _animations = require('../animations');
+
+var _time = require('../utils/time.js');
+
+var _time2 = _interopRequireDefault(_time);
+
+var _ClockHours = require('./ClockHours.js');
+
+var _ClockHours2 = _interopRequireDefault(_ClockHours);
+
+var _ClockMinutes = require('./ClockMinutes.js');
+
+var _ClockMinutes2 = _interopRequireDefault(_ClockMinutes);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var Clock = function (_Component) {
+ _inherits(Clock, _Component);
+
+ function Clock() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Clock);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Clock)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ center: { x: null, y: null },
+ radius: 0
+ }, _this.handleHourChange = function (hours) {
+ if (_this.props.time.getHours() !== hours) {
+ _this.props.onChange(_time2.default.setHours(_this.props.time, _this.adaptHourToFormat(hours)));
+ }
+ }, _this.handleMinuteChange = function (minutes) {
+ if (_this.props.time.getMinutes() !== minutes) {
+ _this.props.onChange(_time2.default.setMinutes(_this.props.time, minutes));
+ }
+ }, _this.handleCalculateShape = function () {
+ var _this$refs$placeholde = _this.refs.placeholder.getBoundingClientRect();
+
+ var top = _this$refs$placeholde.top;
+ var left = _this$refs$placeholde.left;
+ var width = _this$refs$placeholde.width;
+
+ _this.setState({
+ center: { x: left + width / 2 - window.scrollX, y: top + width / 2 - window.scrollX },
+ radius: width / 2
+ });
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Clock, [{
+ key: 'componentDidMount',
+ value: function componentDidMount() {
+ var _this2 = this;
+
+ window.addEventListener('resize', this.handleCalculateShape);
+ setTimeout(function () {
+ _this2.handleCalculateShape();
+ });
+ }
+ }, {
+ key: 'componentWillUnmount',
+ value: function componentWillUnmount() {
+ window.removeEventListener('resize', this.handleCalculateShape);
+ }
+ }, {
+ key: 'adaptHourToFormat',
+ value: function adaptHourToFormat(hour) {
+ if (this.props.format === 'ampm') {
+ if (_time2.default.getTimeMode(this.props.time) === 'pm') {
+ return hour < 12 ? hour + 12 : hour;
+ } else {
+ return hour === 12 ? 0 : hour;
+ }
+ } else {
+ return hour;
+ }
+ }
+ }, {
+ key: 'renderHours',
+ value: function renderHours() {
+ return _react2.default.createElement(_ClockHours2.default, {
+ center: this.state.center,
+ format: this.props.format,
+ onChange: this.handleHourChange,
+ radius: this.state.radius,
+ selected: this.props.time.getHours(),
+ spacing: this.state.radius * 0.18,
+ onHandMoved: this.props.onHandMoved,
+ theme: this.props.theme
+ });
+ }
+ }, {
+ key: 'renderMinutes',
+ value: function renderMinutes() {
+ return _react2.default.createElement(_ClockMinutes2.default, {
+ center: this.state.center,
+ onChange: this.handleMinuteChange,
+ radius: this.state.radius,
+ selected: this.props.time.getMinutes(),
+ spacing: this.state.radius * 0.18,
+ onHandMoved: this.props.onHandMoved,
+ theme: this.props.theme
+ });
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var theme = this.props.theme;
+
+ var animation = this.props.display === 'hours' ? _animations.ZoomOut : _animations.ZoomIn;
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'clock', className: theme.clock },
+ _react2.default.createElement(
+ 'div',
+ { ref: 'placeholder', className: theme.placeholder, style: { height: this.state.radius * 2 } },
+ _react2.default.createElement(
+ _reactAddonsCssTransitionGroup2.default,
+ { transitionName: animation, transitionEnterTimeout: 500, transitionLeaveTimeout: 500 },
+ _react2.default.createElement(
+ 'div',
+ { key: this.props.display, className: theme.clockWrapper, style: { height: this.state.radius * 2 } },
+ this.props.display === 'hours' ? this.renderHours() : null,
+ this.props.display === 'minutes' ? this.renderMinutes() : null
+ )
+ )
+ )
+ );
+ }
+ }]);
+
+ return Clock;
+}(_react.Component);
+
+Clock.propTypes = {
+ className: _react.PropTypes.string,
+ display: _react.PropTypes.oneOf(['hours', 'minutes']),
+ format: _react.PropTypes.oneOf(['24hr', 'ampm']),
+ onChange: _react.PropTypes.func,
+ onHandMoved: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ clock: _react.PropTypes.string,
+ clockWrapper: _react.PropTypes.string,
+ placeholder: _react.PropTypes.string
+ }),
+ time: _react.PropTypes.object
+};
+Clock.defaultProps = {
+ className: '',
+ display: 'hours',
+ format: '24hr',
+ time: new Date()
+};
+exports.default = Clock;
\ No newline at end of file
diff --git a/lib/time_picker/ClockFace.js b/lib/time_picker/ClockFace.js
new file mode 100644
index 000000000..dda37de9e
--- /dev/null
+++ b/lib/time_picker/ClockFace.js
@@ -0,0 +1,117 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var Face = function (_Component) {
+ _inherits(Face, _Component);
+
+ function Face() {
+ _classCallCheck(this, Face);
+
+ return _possibleConstructorReturn(this, Object.getPrototypeOf(Face).apply(this, arguments));
+ }
+
+ _createClass(Face, [{
+ key: 'numberStyle',
+ value: function numberStyle(rad, num) {
+ return {
+ position: 'absolute',
+ left: rad + rad * Math.sin(360 * (Math.PI / 180) / 12 * (num - 1)) + this.props.spacing,
+ top: rad - rad * Math.cos(360 * (Math.PI / 180) / 12 * (num - 1)) + this.props.spacing
+ };
+ }
+ }, {
+ key: 'faceStyle',
+ value: function faceStyle() {
+ return {
+ height: this.props.radius * 2,
+ width: this.props.radius * 2
+ };
+ }
+ }, {
+ key: 'renderNumber',
+ value: function renderNumber(number, idx) {
+ var _props = this.props;
+ var active = _props.active;
+ var radius = _props.radius;
+ var spacing = _props.spacing;
+ var theme = _props.theme;
+ var twoDigits = _props.twoDigits;
+
+ return _react2.default.createElement(
+ 'span',
+ {
+ className: (0, _classnames3.default)(theme.number, _defineProperty({}, theme.active, number === active)),
+ style: this.numberStyle(radius - spacing, idx + 1),
+ key: number
+ },
+ twoDigits ? ('0' + number).slice(-2) : number
+ );
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props2 = this.props;
+ var numbers = _props2.numbers;
+ var onTouchStart = _props2.onTouchStart;
+ var onMouseDown = _props2.onMouseDown;
+ var theme = _props2.theme;
+
+ return _react2.default.createElement(
+ 'div',
+ {
+ ref: 'root',
+ className: theme.face,
+ onTouchStart: onTouchStart,
+ onMouseDown: onMouseDown,
+ style: this.faceStyle()
+ },
+ numbers.map(this.renderNumber.bind(this))
+ );
+ }
+ }]);
+
+ return Face;
+}(_react.Component);
+
+Face.propTypes = {
+ active: _react.PropTypes.number,
+ numbers: _react.PropTypes.array,
+ radius: _react.PropTypes.number,
+ spacing: _react.PropTypes.number,
+ theme: _react.PropTypes.shape({
+ active: _react.PropTypes.string,
+ face: _react.PropTypes.string,
+ number: _react.PropTypes.string
+ }),
+ twoDigits: _react.PropTypes.bool
+};
+Face.defaultProps = {
+ active: null,
+ numbers: [],
+ radius: 0,
+ twoDigits: false
+};
+exports.default = Face;
\ No newline at end of file
diff --git a/lib/time_picker/ClockHand.js b/lib/time_picker/ClockHand.js
new file mode 100644
index 000000000..053b19e07
--- /dev/null
+++ b/lib/time_picker/ClockHand.js
@@ -0,0 +1,175 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _events = require('../utils/events.js');
+
+var _events2 = _interopRequireDefault(_events);
+
+var _prefixer = require('../utils/prefixer.js');
+
+var _prefixer2 = _interopRequireDefault(_prefixer);
+
+var _utils = require('../utils/utils.js');
+
+var _utils2 = _interopRequireDefault(_utils);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var Hand = function (_Component) {
+ _inherits(Hand, _Component);
+
+ function Hand() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Hand);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Hand)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ knobWidth: 0
+ }, _this.handleMouseMove = function (event) {
+ _this.move(_events2.default.getMousePosition(event));
+ }, _this.handleTouchMove = function (event) {
+ _this.move(_events2.default.getTouchPosition(event));
+ }, _this.handleMouseUp = function () {
+ _this.end(_this.getMouseEventMap());
+ }, _this.handleTouchEnd = function () {
+ _this.end(_this.getTouchEventMap());
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Hand, [{
+ key: 'componentDidMount',
+ value: function componentDidMount() {
+ var _this2 = this;
+
+ setTimeout(function () {
+ _this2.setState({ knobWidth: _this2.refs.knob.offsetWidth });
+ });
+ }
+ }, {
+ key: 'componentWillUnmount',
+ value: function componentWillUnmount() {
+ _events2.default.removeEventsFromDocument(this.getMouseEventMap());
+ _events2.default.removeEventsFromDocument(this.getTouchEventMap());
+ }
+ }, {
+ key: 'getMouseEventMap',
+ value: function getMouseEventMap() {
+ return {
+ mousemove: this.handleMouseMove,
+ mouseup: this.handleMouseUp
+ };
+ }
+ }, {
+ key: 'getTouchEventMap',
+ value: function getTouchEventMap() {
+ return {
+ touchmove: this.handleTouchMove,
+ touchend: this.handleTouchEnd
+ };
+ }
+ }, {
+ key: 'mouseStart',
+ value: function mouseStart(event) {
+ _events2.default.addEventsToDocument(this.getMouseEventMap());
+ this.move(_events2.default.getMousePosition(event));
+ }
+ }, {
+ key: 'touchStart',
+ value: function touchStart(event) {
+ _events2.default.addEventsToDocument(this.getTouchEventMap());
+ this.move(_events2.default.getTouchPosition(event));
+ _events2.default.pauseEvent(event);
+ }
+ }, {
+ key: 'getPositionRadius',
+ value: function getPositionRadius(position) {
+ var x = this.props.origin.x - position.x;
+ var y = this.props.origin.y - position.y;
+ return Math.sqrt(x * x + y * y);
+ }
+ }, {
+ key: 'trimAngleToValue',
+ value: function trimAngleToValue(angle) {
+ return this.props.step * Math.round(angle / this.props.step);
+ }
+ }, {
+ key: 'positionToAngle',
+ value: function positionToAngle(position) {
+ return _utils2.default.angle360FromPositions(this.props.origin.x, this.props.origin.y, position.x, position.y);
+ }
+ }, {
+ key: 'end',
+ value: function end(evts) {
+ if (this.props.onMoved) this.props.onMoved();
+ _events2.default.removeEventsFromDocument(evts);
+ }
+ }, {
+ key: 'move',
+ value: function move(position) {
+ var degrees = this.trimAngleToValue(this.positionToAngle(position));
+ var radius = this.getPositionRadius(position);
+ if (this.props.onMove) this.props.onMove(degrees === 360 ? 0 : degrees, radius);
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var theme = this.props.theme;
+
+ var className = theme.hand + ' ' + this.props.className;
+ var handStyle = (0, _prefixer2.default)({
+ height: this.props.length - this.state.knobWidth / 2,
+ transform: 'rotate(' + this.props.angle + 'deg)'
+ });
+
+ return _react2.default.createElement(
+ 'div',
+ { className: className, style: handStyle },
+ _react2.default.createElement('div', { ref: 'knob', className: theme.knob })
+ );
+ }
+ }]);
+
+ return Hand;
+}(_react.Component);
+
+Hand.propTypes = {
+ angle: _react.PropTypes.number,
+ className: _react.PropTypes.string,
+ length: _react.PropTypes.number,
+ onMove: _react.PropTypes.func,
+ onMoved: _react.PropTypes.func,
+ origin: _react.PropTypes.object,
+ step: _react.PropTypes.number,
+ theme: _react.PropTypes.shape({
+ hand: _react.PropTypes.string,
+ knob: _react.PropTypes.string
+ })
+};
+Hand.defaultProps = {
+ className: '',
+ angle: 0,
+ length: 0,
+ origin: {}
+};
+exports.default = Hand;
\ No newline at end of file
diff --git a/lib/time_picker/ClockHours.js b/lib/time_picker/ClockHours.js
new file mode 100644
index 000000000..85a711ab1
--- /dev/null
+++ b/lib/time_picker/ClockHours.js
@@ -0,0 +1,149 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _utils = require('../utils/utils.js');
+
+var _utils2 = _interopRequireDefault(_utils);
+
+var _ClockHand = require('./ClockHand.js');
+
+var _ClockHand2 = _interopRequireDefault(_ClockHand);
+
+var _ClockFace = require('./ClockFace.js');
+
+var _ClockFace2 = _interopRequireDefault(_ClockFace);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+var outerNumbers = [0].concat(_toConsumableArray(_utils2.default.range(13, 24)));
+var innerNumbers = [12].concat(_toConsumableArray(_utils2.default.range(1, 12)));
+var innerSpacing = 1.7;
+var step = 360 / 12;
+
+var Hours = function (_Component) {
+ _inherits(Hours, _Component);
+
+ function Hours() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Hours);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Hours)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ inner: _this.props.format === '24hr' && _this.props.selected > 0 && _this.props.selected <= 12
+ }, _this.handleHandMove = function (degrees, radius) {
+ var currentInner = radius < _this.props.radius - _this.props.spacing * innerSpacing;
+ if (_this.props.format === '24hr' && _this.state.inner !== currentInner) {
+ _this.setState({ inner: currentInner }, function () {
+ _this.props.onChange(_this.valueFromDegrees(degrees));
+ });
+ } else {
+ _this.props.onChange(_this.valueFromDegrees(degrees));
+ }
+ }, _this.handleMouseDown = function (event) {
+ _this.refs.hand.mouseStart(event);
+ }, _this.handleTouchStart = function (event) {
+ _this.refs.hand.touchStart(event);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Hours, [{
+ key: 'valueFromDegrees',
+ value: function valueFromDegrees(degrees) {
+ if (this.props.format === 'ampm' || this.props.format === '24hr' && this.state.inner) {
+ return innerNumbers[degrees / step];
+ } else {
+ return outerNumbers[degrees / step];
+ }
+ }
+ }, {
+ key: 'renderInnerFace',
+ value: function renderInnerFace(innerRadius) {
+ if (this.props.format === '24hr') {
+ return _react2.default.createElement(_ClockFace2.default, {
+ onTouchStart: this.handleTouchStart,
+ onMouseDown: this.handleMouseDown,
+ numbers: innerNumbers,
+ spacing: this.props.spacing,
+ radius: innerRadius,
+ theme: this.props.theme,
+ active: this.props.selected
+ });
+ }
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var format = _props.format;
+ var selected = _props.selected;
+ var radius = _props.radius;
+ var spacing = _props.spacing;
+ var center = _props.center;
+ var onHandMoved = _props.onHandMoved;
+
+ var is24hr = format === '24hr';
+
+ return _react2.default.createElement(
+ 'div',
+ null,
+ _react2.default.createElement(_ClockFace2.default, {
+ onTouchStart: this.handleTouchStart,
+ onMouseDown: this.handleMouseDown,
+ numbers: is24hr ? outerNumbers : innerNumbers,
+ spacing: spacing,
+ radius: radius,
+ twoDigits: is24hr,
+ active: is24hr ? selected : selected % 12 || 12,
+ theme: this.props.theme
+ }),
+ this.renderInnerFace(radius - spacing * innerSpacing),
+ _react2.default.createElement(_ClockHand2.default, { ref: 'hand',
+ angle: selected * step,
+ length: (this.state.inner ? radius - spacing * innerSpacing : radius) - spacing,
+ onMove: this.handleHandMove,
+ theme: this.props.theme,
+ onMoved: onHandMoved,
+ origin: center,
+ step: step
+ })
+ );
+ }
+ }]);
+
+ return Hours;
+}(_react.Component);
+
+Hours.propTypes = {
+ center: _react.PropTypes.object,
+ format: _react.PropTypes.oneOf(['24hr', 'ampm']),
+ onChange: _react.PropTypes.func,
+ onHandMoved: _react.PropTypes.func,
+ radius: _react.PropTypes.number,
+ selected: _react.PropTypes.number,
+ spacing: _react.PropTypes.number,
+ theme: _react.PropTypes.object
+};
+exports.default = Hours;
\ No newline at end of file
diff --git a/lib/time_picker/ClockMinutes.js b/lib/time_picker/ClockMinutes.js
new file mode 100644
index 000000000..780a90ba4
--- /dev/null
+++ b/lib/time_picker/ClockMinutes.js
@@ -0,0 +1,105 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _utils = require('../utils/utils.js');
+
+var _utils2 = _interopRequireDefault(_utils);
+
+var _ClockHand = require('./ClockHand.js');
+
+var _ClockHand2 = _interopRequireDefault(_ClockHand);
+
+var _ClockFace = require('./ClockFace.js');
+
+var _ClockFace2 = _interopRequireDefault(_ClockFace);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var minutes = _utils2.default.range(0, 60, 5);
+var step = 360 / 60;
+
+var Minutes = function (_Component) {
+ _inherits(Minutes, _Component);
+
+ function Minutes() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Minutes);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Minutes)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleHandMove = function (degrees) {
+ _this.props.onChange(degrees / step);
+ }, _this.handleMouseDown = function (event) {
+ _this.refs.hand.mouseStart(event);
+ }, _this.handleTouchStart = function (event) {
+ _this.refs.hand.touchStart(event);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(Minutes, [{
+ key: 'render',
+ value: function render() {
+ return _react2.default.createElement(
+ 'div',
+ null,
+ _react2.default.createElement(_ClockFace2.default, {
+ onTouchStart: this.handleTouchStart,
+ onMouseDown: this.handleMouseDown,
+ numbers: minutes,
+ spacing: this.props.spacing,
+ radius: this.props.radius,
+ active: this.props.selected,
+ theme: this.props.theme,
+ twoDigits: true
+ }),
+ _react2.default.createElement(_ClockHand2.default, { ref: 'hand',
+ className: minutes.indexOf(this.props.selected) === -1 ? this.props.theme.small : '',
+ angle: this.props.selected * step,
+ length: this.props.radius - this.props.spacing,
+ onMove: this.handleHandMove,
+ origin: this.props.center,
+ theme: this.props.theme,
+ step: step
+ })
+ );
+ }
+ }]);
+
+ return Minutes;
+}(_react.Component);
+
+Minutes.propTypes = {
+ center: _react.PropTypes.object,
+ onChange: _react.PropTypes.func,
+ radius: _react.PropTypes.number,
+ selected: _react.PropTypes.number,
+ spacing: _react.PropTypes.number,
+ theme: _react.PropTypes.shape({
+ small: _react.PropTypes.string
+ })
+};
+Minutes.defaultProps = {
+ selected: 0,
+ onChange: null
+};
+exports.default = Minutes;
\ No newline at end of file
diff --git a/lib/time_picker/TimePicker.js b/lib/time_picker/TimePicker.js
new file mode 100644
index 000000000..d93e13a9d
--- /dev/null
+++ b/lib/time_picker/TimePicker.js
@@ -0,0 +1,143 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.TimePicker = exports.timePickerFactory = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _events = require('../utils/events.js');
+
+var _events2 = _interopRequireDefault(_events);
+
+var _time = require('../utils/time.js');
+
+var _time2 = _interopRequireDefault(_time);
+
+var _Dialog = require('../dialog/Dialog.js');
+
+var _Dialog2 = _interopRequireDefault(_Dialog);
+
+var _Input = require('../input/Input.js');
+
+var _Input2 = _interopRequireDefault(_Input);
+
+var _TimePickerDialog = require('./TimePickerDialog.js');
+
+var _TimePickerDialog2 = _interopRequireDefault(_TimePickerDialog);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(TimePickerDialog, Input) {
+ var TimePicker = function (_Component) {
+ _inherits(TimePicker, _Component);
+
+ function TimePicker() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, TimePicker);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(TimePicker)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ active: false
+ }, _this.handleDismiss = function () {
+ _this.setState({ active: false });
+ }, _this.handleInputMouseDown = function (event) {
+ _events2.default.pauseEvent(event);
+ _this.setState({ active: true });
+ }, _this.handleSelect = function (value, event) {
+ if (_this.props.onChange) _this.props.onChange(value, event);
+ _this.setState({ active: false });
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(TimePicker, [{
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var value = _props.value;
+ var format = _props.format;
+ var inputClassName = _props.inputClassName;
+ var theme = _props.theme;
+
+ var formattedTime = value ? _time2.default.formatTime(value, format) : '';
+ return _react2.default.createElement(
+ 'div',
+ { 'data-react-toolbox': 'time-picker' },
+ _react2.default.createElement(Input, {
+ className: (0, _classnames3.default)(theme.input, _defineProperty({}, inputClassName, inputClassName)),
+ error: this.props.error,
+ label: this.props.label,
+ onMouseDown: this.handleInputMouseDown,
+ readOnly: true,
+ type: 'text',
+ value: formattedTime
+ }),
+ _react2.default.createElement(TimePickerDialog, {
+ active: this.state.active,
+ className: this.props.className,
+ format: format,
+ onDismiss: this.handleDismiss,
+ onSelect: this.handleSelect,
+ theme: this.props.theme,
+ value: this.props.value
+ })
+ );
+ }
+ }]);
+
+ return TimePicker;
+ }(_react.Component);
+
+ TimePicker.propTypes = {
+ className: _react.PropTypes.string,
+ error: _react.PropTypes.string,
+ format: _react.PropTypes.oneOf(['24hr', 'ampm']),
+ inputClassName: _react.PropTypes.string,
+ label: _react.PropTypes.string,
+ onChange: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ input: _react.PropTypes.string
+ }),
+ value: _react.PropTypes.object
+ };
+ TimePicker.defaultProps = {
+ className: '',
+ format: '24hr'
+ };
+
+
+ return TimePicker;
+};
+
+var TimePickerDialog = (0, _TimePickerDialog2.default)(_Dialog2.default);
+var TimePicker = factory(TimePickerDialog, _Input2.default);
+exports.default = (0, _reactCssThemr.themr)(_identifiers.TIME_PICKER)(TimePicker);
+exports.timePickerFactory = factory;
+exports.TimePicker = TimePicker;
\ No newline at end of file
diff --git a/lib/time_picker/TimePickerDialog.js b/lib/time_picker/TimePickerDialog.js
new file mode 100644
index 000000000..84bcf4792
--- /dev/null
+++ b/lib/time_picker/TimePickerDialog.js
@@ -0,0 +1,182 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames = require('classnames');
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
+var _time = require('../utils/time.js');
+
+var _time2 = _interopRequireDefault(_time);
+
+var _Clock = require('./Clock.js');
+
+var _Clock2 = _interopRequireDefault(_Clock);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory(Dialog) {
+ var TimePickerDialog = function (_Component) {
+ _inherits(TimePickerDialog, _Component);
+
+ function TimePickerDialog() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, TimePickerDialog);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(TimePickerDialog)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ display: 'hours',
+ displayTime: _this.props.value
+ }, _this.handleClockChange = function (value) {
+ _this.setState({ displayTime: value });
+ }, _this.handleSelect = function (event) {
+ _this.props.onSelect(_this.state.displayTime, event);
+ }, _this.toggleTimeMode = function () {
+ _this.setState({ displayTime: _time2.default.toggleTimeMode(_this.state.displayTime) });
+ }, _this.handleHandMoved = function () {
+ if (_this.state.display === 'hours') _this.setState({ display: 'minutes' });
+ }, _this.switchDisplay = function (display) {
+ _this.setState({ display: display });
+ }, _this.actions = [{ label: 'Cancel', className: _this.props.theme.button, onClick: _this.props.onDismiss }, { label: 'Ok', className: _this.props.theme.button, onClick: _this.handleSelect }], _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(TimePickerDialog, [{
+ key: 'componentDidUpdate',
+ value: function componentDidUpdate(prevProps) {
+ if (!prevProps.active && this.props.active) {
+ setTimeout(this.refs.clock.handleCalculateShape, 1000);
+ }
+ }
+ }, {
+ key: 'formatHours',
+ value: function formatHours() {
+ if (this.props.format === 'ampm') {
+ return this.state.displayTime.getHours() % 12 || 12;
+ } else {
+ return this.state.displayTime.getHours();
+ }
+ }
+ }, {
+ key: 'renderAMPMLabels',
+ value: function renderAMPMLabels() {
+ var theme = this.props.theme;
+
+ if (this.props.format === 'ampm') {
+ return _react2.default.createElement(
+ 'div',
+ { className: theme.ampm },
+ _react2.default.createElement(
+ 'span',
+ { className: theme.am, onClick: this.toggleTimeMode },
+ 'AM'
+ ),
+ _react2.default.createElement(
+ 'span',
+ { className: theme.pm, onClick: this.toggleTimeMode },
+ 'PM'
+ )
+ );
+ }
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var theme = this.props.theme;
+
+ var display = this.state.display + 'Display';
+ var format = _time2.default.getTimeMode(this.state.displayTime) + 'Format';
+ var className = (0, _classnames2.default)([theme.dialog, theme[display], theme[format]], this.props.className);
+ return _react2.default.createElement(
+ Dialog,
+ { active: this.props.active, className: className, actions: this.actions },
+ _react2.default.createElement(
+ 'header',
+ { className: theme.header },
+ _react2.default.createElement(
+ 'span',
+ { className: theme.hours, onClick: this.switchDisplay.bind(this, 'hours') },
+ ('0' + this.formatHours()).slice(-2)
+ ),
+ _react2.default.createElement(
+ 'span',
+ { className: theme.separator },
+ ':'
+ ),
+ _react2.default.createElement(
+ 'span',
+ { className: theme.minutes, onClick: this.switchDisplay.bind(this, 'minutes') },
+ ('0' + this.state.displayTime.getMinutes()).slice(-2)
+ ),
+ this.renderAMPMLabels()
+ ),
+ _react2.default.createElement(_Clock2.default, {
+ ref: 'clock',
+ display: this.state.display,
+ format: this.props.format,
+ onChange: this.handleClockChange,
+ onHandMoved: this.handleHandMoved,
+ theme: this.props.theme,
+ time: this.state.displayTime
+ })
+ );
+ }
+ }]);
+
+ return TimePickerDialog;
+ }(_react.Component);
+
+ TimePickerDialog.propTypes = {
+ active: _react.PropTypes.bool,
+ className: _react.PropTypes.string,
+ format: _react.PropTypes.oneOf(['24hr', 'ampm']),
+ onDismiss: _react.PropTypes.func,
+ onSelect: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ am: _react.PropTypes.string,
+ amFormat: _react.PropTypes.string,
+ ampm: _react.PropTypes.string,
+ button: _react.PropTypes.string,
+ dialog: _react.PropTypes.string,
+ header: _react.PropTypes.string,
+ hours: _react.PropTypes.string,
+ hoursDisplay: _react.PropTypes.string,
+ minutes: _react.PropTypes.string,
+ minutesDisplay: _react.PropTypes.string,
+ pm: _react.PropTypes.string,
+ pmFormat: _react.PropTypes.string,
+ separator: _react.PropTypes.string
+ }),
+ value: _react.PropTypes.object
+ };
+ TimePickerDialog.defaultProps = {
+ active: false,
+ format: '24hr',
+ value: new Date()
+ };
+
+
+ return TimePickerDialog;
+};
+
+exports.default = factory;
\ No newline at end of file
diff --git a/lib/time_picker/_config.scss b/lib/time_picker/_config.scss
new file mode 100644
index 000000000..51fbd509b
--- /dev/null
+++ b/lib/time_picker/_config.scss
@@ -0,0 +1,27 @@
+$timepicker-header-font-size: 5.2 * $unit !default;
+$timepicker-header-padding: $unit !default;
+$timepicker-ampm-font-size: 1.6 * $unit !default;
+$timepicker-primary: $color-primary !default;
+$timepicker-primary-contrast: $color-primary-contrast !default;
+$timepicker-primary-dark: $color-primary-dark !default;
+$timepicker-primary-color: $timepicker-primary !default;
+$timepicker-primary-hover-color: rgba($timepicker-primary, .2) !default;
+$timepicker-primary-contrast-color: $timepicker-primary-contrast !default;
+$timepicker-primary-dark-color: $timepicker-primary-dark !default;
+$timepicker-ampm-height: 2.2 * $unit !default;
+$timepicker-ampm-width: 4 * $unit !default;
+$timepicker-dialog-width: 30 * $unit !default;
+
+$clock-padding: 1.5 * $unit 2 * $unit !default;
+$clock-primary: $color-primary !default;
+$clock-primary-contrast: $color-primary-contrast !default;
+$clock-primary-dark: $color-primary-dark !default;
+$clock-primary-color: $clock-primary !default;
+$clock-primary-hover-color: rgba($clock-primary, .2) !default;
+$clock-primary-contrast-color: $clock-primary-contrast !default;
+$clock-primary-dark-color: $clock-primary-dark !default;
+$clock-number-size: 2 * $unit !default;
+$clock-hand-width: .4 * $unit !default;
+$clock-hand-dot-size: 1 * $unit !default;
+$clock-knob-size: 3.4 * $unit !default;
+$clock-knob-small-size: 1.2 * $unit !default;
diff --git a/lib/time_picker/index.js b/lib/time_picker/index.js
new file mode 100644
index 000000000..8753ebd3e
--- /dev/null
+++ b/lib/time_picker/index.js
@@ -0,0 +1,35 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.TimePicker = undefined;
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+var _TimePicker = require('./TimePicker.js');
+
+var _TimePickerDialog = require('./TimePickerDialog.js');
+
+var _TimePickerDialog2 = _interopRequireDefault(_TimePickerDialog);
+
+var _dialog = require('../dialog');
+
+var _dialog2 = _interopRequireDefault(_dialog);
+
+var _input = require('../input');
+
+var _input2 = _interopRequireDefault(_input);
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var TimePickerDialog = (0, _TimePickerDialog2.default)(_dialog2.default);
+var ThemedTimePicker = (0, _reactCssThemr.themr)(_identifiers.TIME_PICKER, _theme2.default)((0, _TimePicker.timePickerFactory)(TimePickerDialog, _input2.default));
+exports.default = ThemedTimePicker;
+exports.TimePicker = ThemedTimePicker;
\ No newline at end of file
diff --git a/lib/time_picker/theme.scss b/lib/time_picker/theme.scss
new file mode 100644
index 000000000..297982895
--- /dev/null
+++ b/lib/time_picker/theme.scss
@@ -0,0 +1,168 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.input > [role="input"] {
+ cursor: pointer;
+}
+
+.header {
+ position: relative;
+ width: 100%;
+ padding: $timepicker-header-padding;
+ font-size: $timepicker-header-font-size;
+ color: $timepicker-primary-contrast-color;
+ text-align: center;
+ background: $timepicker-primary-color;
+}
+
+.hours, .minutes {
+ display: inline-block;
+ cursor: pointer;
+ opacity: .6;
+}
+
+.separator {
+ margin: 0 $timepicker-header-padding / 2;
+ opacity: .6;
+}
+
+.ampm {
+ position: absolute;
+ top: 50%;
+ right: 2 * $unit;
+ width: $timepicker-ampm-width;
+ height: $timepicker-ampm-height * 2;
+ margin-top: - $timepicker-ampm-height;
+ font-size: $timepicker-ampm-font-size;
+ line-height: $timepicker-ampm-height;
+ text-align: center;
+}
+
+.am, .pm {
+ display: block;
+ cursor: pointer;
+ opacity: .6;
+}
+
+.dialog {
+ width: $timepicker-dialog-width;
+ > [role="body"] {
+ padding: 0;
+ overflow-y: visible;
+ }
+ > [role="navigation"] > .button {
+ color: $timepicker-primary-color;
+ &:hover {
+ background: $timepicker-primary-hover-color;
+ }
+ &:focus:not(:active) {
+ background: $timepicker-primary-hover-color;
+ }
+ }
+ &.hoursDisplay .hours, &.minutesDisplay .minutes, &.amFormat .am,
+ &.pmFormat .pm {
+ opacity: 1;
+ }
+}
+
+.clock {
+ padding: $clock-padding;
+}
+
+.placeholder {
+ position: relative;
+ z-index: $z-index-high;
+}
+
+.clockWrapper {
+ position: absolute;
+ width: 100%;
+ background-color: $color-divider;
+ border-radius: 50%;
+}
+
+.face {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ z-index: $z-index-high;
+ cursor: pointer;
+ border-radius: 50%;
+ transform: translateX(-50%) translateY(-50%);
+}
+
+.number {
+ position: relative;
+ width: $clock-number-size;
+ height: $clock-number-size;
+ margin-top: - $clock-number-size / 2;
+ margin-left: - $clock-number-size / 2;
+ text-align: center;
+ pointer-events: none;
+ user-select: none;
+ &.active {
+ color: $clock-primary-contrast-color;
+ }
+}
+
+.hand {
+ position: absolute;
+ bottom: 50%;
+ left: 50%;
+ display: block;
+ width: $clock-hand-width;
+ margin-left: - $clock-hand-width / 2;
+ background-color: $clock-primary-color;
+ transform-origin: 50% 100%;
+ &:before {
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ width: $clock-hand-dot-size;
+ height: $clock-hand-dot-size;
+ margin-bottom: - $clock-hand-dot-size / 2;
+ margin-left: - $clock-hand-dot-size / 2;
+ content: "";
+ background-color: $clock-primary-color;
+ border-radius: 50%;
+ }
+ &.small > .knob {
+ background-color: $clock-primary-hover-color;
+ &:after {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: $clock-knob-small-size;
+ height: $clock-knob-small-size;
+ margin-top: - $clock-knob-small-size / 2;
+ margin-left: - $clock-knob-small-size / 2;
+ content: "";
+ background: $clock-primary-color;
+ border-radius: 50%;
+ }
+ &:before {
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ width: $clock-hand-width;
+ height: $clock-knob-size - $clock-knob-small-size;
+ margin-left: - $clock-hand-width / 2;
+ content: "";
+ background: $clock-primary-color;
+ }
+ }
+}
+
+.knob {
+ position: absolute;
+ top: - $clock-knob-size;
+ left: 50%;
+ width: $clock-knob-size;
+ height: $clock-knob-size;
+ margin-left: - $clock-knob-size / 2;
+ cursor: pointer;
+ background-color: $clock-primary-color;
+ border-radius: 50%;
+}
diff --git a/lib/tooltip/Tooltip.js b/lib/tooltip/Tooltip.js
new file mode 100644
index 000000000..0f0c4f146
--- /dev/null
+++ b/lib/tooltip/Tooltip.js
@@ -0,0 +1,140 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.tooltipFactory = undefined;
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _classnames2 = require('classnames');
+
+var _classnames3 = _interopRequireDefault(_classnames2);
+
+var _reactCssThemr = require('react-css-themr');
+
+var _identifiers = require('../identifiers.js');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var factory = function factory() {
+ var defaultTheme = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
+
+ var Tooltip = function Tooltip(ComposedComponent) {
+ var TooltippedComponent = function (_Component) {
+ _inherits(TooltippedComponent, _Component);
+
+ function TooltippedComponent() {
+ var _Object$getPrototypeO;
+
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, TooltippedComponent);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(TooltippedComponent)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.state = {
+ active: false
+ }, _this.handleMouseEnter = function (event) {
+ if (_this.timeout) clearTimeout(_this.timeout);
+ _this.timeout = setTimeout(function () {
+ return _this.setState({ active: true });
+ }, _this.props.tooltipDelay);
+ if (_this.props.onMouseEnter) _this.props.onMouseEnter(event);
+ }, _this.handleMouseLeave = function (event) {
+ if (_this.timeout) clearTimeout(_this.timeout);
+ if (_this.state.active) _this.setState({ active: false });
+ if (_this.props.onMouseLeave) _this.props.onMouseLeave(event);
+ }, _this.handleClick = function (event) {
+ if (_this.timeout) clearTimeout(_this.timeout);
+ if (_this.props.tooltipHideOnClick) _this.setState({ active: false });
+ if (_this.props.onClick) _this.props.onClick(event);
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ _createClass(TooltippedComponent, [{
+ key: 'render',
+ value: function render() {
+ var _props = this.props;
+ var children = _props.children;
+ var className = _props.className;
+ var tooltip = _props.tooltip;
+ var tooltipDelay = _props.tooltipDelay;
+ var tooltipHideOnClick = _props.tooltipHideOnClick;
+ var theme = _props.theme;
+
+ var other = _objectWithoutProperties(_props, ['children', 'className', 'tooltip', 'tooltipDelay', 'tooltipHideOnClick', 'theme']); //eslint-disable-line no-unused-vars
+
+
+ var composedClassName = (0, _classnames3.default)(this.props.theme.tooltipWrapper, className);
+ var tooltipClassName = (0, _classnames3.default)(this.props.theme.tooltip, _defineProperty({}, this.props.theme.tooltipActive, this.state.active));
+
+ return _react2.default.createElement(
+ ComposedComponent,
+ _extends({}, other, {
+ className: composedClassName,
+ onClick: this.handleClick,
+ onMouseEnter: this.handleMouseEnter,
+ onMouseLeave: this.handleMouseLeave
+ }),
+ children ? children : null,
+ _react2.default.createElement(
+ 'span',
+ { 'data-react-toolbox': 'tooltip', className: tooltipClassName },
+ tooltip
+ )
+ );
+ }
+ }]);
+
+ return TooltippedComponent;
+ }(_react.Component);
+
+ TooltippedComponent.propTypes = {
+ children: _react.PropTypes.any,
+ className: _react.PropTypes.string,
+ onClick: _react.PropTypes.func,
+ onMouseEnter: _react.PropTypes.func,
+ onMouseLeave: _react.PropTypes.func,
+ theme: _react.PropTypes.shape({
+ tooltip: _react.PropTypes.string,
+ tooltipActive: _react.PropTypes.string,
+ tooltipWrapper: _react.PropTypes.string
+ }),
+ tooltip: _react.PropTypes.string,
+ tooltipDelay: _react.PropTypes.number,
+ tooltipHideOnClick: _react.PropTypes.bool
+ };
+ TooltippedComponent.defaultProps = {
+ className: '',
+ tooltipDelay: 0,
+ tooltipHideOnClick: true
+ };
+
+
+ return (0, _reactCssThemr.themr)(_identifiers.TOOLTIP, defaultTheme)(TooltippedComponent);
+ };
+
+ return Tooltip;
+};
+
+exports.default = factory();
+exports.tooltipFactory = factory;
\ No newline at end of file
diff --git a/lib/tooltip/_config.scss b/lib/tooltip/_config.scss
new file mode 100644
index 000000000..042410519
--- /dev/null
+++ b/lib/tooltip/_config.scss
@@ -0,0 +1,8 @@
+$tooltip-background: rgba(97,97,97,.9) !default;
+$tooltip-margin: 0.5 * $unit !default;
+$tooltip-border-radius: .2 * $unit !default;
+$tooltip-color: #fff !default;
+$tooltip-font-size: $unit !default;
+$tooltip-max-width: 17 * $unit !default;
+$tooltip-padding: .8 * $unit !default;
+$tooltip-animation-duration: 200ms !default;
diff --git a/lib/tooltip/index.js b/lib/tooltip/index.js
new file mode 100644
index 000000000..447d04c5b
--- /dev/null
+++ b/lib/tooltip/index.js
@@ -0,0 +1,15 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _Tooltip = require('./Tooltip.js');
+
+var _theme = require('./theme.scss');
+
+var _theme2 = _interopRequireDefault(_theme);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+exports.default = (0, _Tooltip.tooltipFactory)(_theme2.default);
\ No newline at end of file
diff --git a/lib/tooltip/theme.scss b/lib/tooltip/theme.scss
new file mode 100644
index 000000000..e87d013f2
--- /dev/null
+++ b/lib/tooltip/theme.scss
@@ -0,0 +1,34 @@
+@import "../colors";
+@import "../globals";
+@import "../mixins";
+@import "./config";
+
+.tooltipWrapper {
+ position: relative;
+}
+
+.tooltip {
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ z-index: $z-index-higher;
+ display: block;
+ max-width: $tooltip-max-width;
+ padding: $tooltip-padding;
+ margin: $tooltip-margin 0;
+ font-family: Roboto, sans-serif;
+ font-size: $tooltip-font-size;
+ font-weight: $font-weight-bold;
+ line-height: $font-size-small;
+ color: $tooltip-color;
+ text-align: center;
+ text-transform: none;
+ background: $tooltip-background;
+ border-radius: $tooltip-border-radius;
+ transition: $animation-curve-default $tooltip-animation-duration transform;
+ transform: scale(0) translateX(-50%);
+ transform-origin: top left;
+ &.tooltipActive {
+ transform: scale(1) translateX(-50%);
+ }
+}
diff --git a/lib/utils/events.js b/lib/utils/events.js
new file mode 100644
index 000000000..87e42c565
--- /dev/null
+++ b/lib/utils/events.js
@@ -0,0 +1,69 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.default = {
+ getMousePosition: function getMousePosition(event) {
+ return {
+ x: event.pageX - (window.scrollX || window.pageXOffset),
+ y: event.pageY - (window.scrollY || window.pageYOffset)
+ };
+ },
+ getTouchPosition: function getTouchPosition(event) {
+ return {
+ x: event.touches[0].pageX - (window.scrollX || window.pageXOffset),
+ y: event.touches[0].pageY - (window.scrollY || window.pageYOffset)
+ };
+ },
+ pauseEvent: function pauseEvent(event) {
+ event.stopPropagation();
+ event.preventDefault();
+ },
+ addEventsToDocument: function addEventsToDocument(eventMap) {
+ for (var key in eventMap) {
+ document.addEventListener(key, eventMap[key], false);
+ }
+ },
+ removeEventsFromDocument: function removeEventsFromDocument(eventMap) {
+ for (var key in eventMap) {
+ document.removeEventListener(key, eventMap[key], false);
+ }
+ },
+ targetIsDescendant: function targetIsDescendant(event, parent) {
+ var node = event.target;
+ while (node !== null) {
+ if (node === parent) return true;
+ node = node.parentNode;
+ }
+ return false;
+ },
+ addEventListenerOnTransitionEnded: function addEventListenerOnTransitionEnded(element, fn) {
+ var eventName = transitionEventNamesFor(element);
+ if (!eventName) return false;
+ element.addEventListener(eventName, fn);
+ return true;
+ },
+ removeEventListenerOnTransitionEnded: function removeEventListenerOnTransitionEnded(element) {
+ var eventName = transitionEventNamesFor(element);
+ if (!eventName) return false;
+ element.removeEventListener(eventName);
+ return true;
+ }
+};
+
+
+var TRANSITIONS = {
+ 'transition': 'transitionend',
+ 'OTransition': 'oTransitionEnd',
+ 'MozTransition': 'transitionend',
+ 'WebkitTransition': 'webkitTransitionEnd'
+};
+
+function transitionEventNamesFor(element) {
+ for (var transition in TRANSITIONS) {
+ if (element.style[transition] !== undefined) {
+ return TRANSITIONS[transition];
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/utils/index.js b/lib/utils/index.js
new file mode 100644
index 000000000..d1bd0ea43
--- /dev/null
+++ b/lib/utils/index.js
@@ -0,0 +1,30 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.utils = exports.time = exports.prefixer = exports.events = undefined;
+
+var _events = require('./events.js');
+
+var _events2 = _interopRequireDefault(_events);
+
+var _prefixer = require('./prefixer.js');
+
+var _prefixer2 = _interopRequireDefault(_prefixer);
+
+var _time = require('./time.js');
+
+var _time2 = _interopRequireDefault(_time);
+
+var _utils = require('./utils.js');
+
+var _utils2 = _interopRequireDefault(_utils);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+exports.default = { events: _events2.default, prefixer: _prefixer2.default, time: _time2.default, utils: _utils2.default };
+exports.events = _events2.default;
+exports.prefixer = _prefixer2.default;
+exports.time = _time2.default;
+exports.utils = _utils2.default;
\ No newline at end of file
diff --git a/lib/utils/polyfills.js b/lib/utils/polyfills.js
new file mode 100644
index 000000000..5cc5e7f3a
--- /dev/null
+++ b/lib/utils/polyfills.js
@@ -0,0 +1,11 @@
+'use strict';
+
+require('core-js/fn/array/from');
+
+require('core-js/fn/array/iterator');
+
+require('core-js/fn/map');
+
+require('core-js/fn/string/starts-with');
+
+require('core-js/fn/symbol');
\ No newline at end of file
diff --git a/lib/utils/prefixer.js b/lib/utils/prefixer.js
new file mode 100644
index 000000000..895313f8f
--- /dev/null
+++ b/lib/utils/prefixer.js
@@ -0,0 +1,47 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+var WEBKIT = 'Webkit';
+var MICROSOFT = 'Ms';
+
+var properties = {
+ transform: [WEBKIT, MICROSOFT]
+};
+
+function capitalize(string) {
+ return string.charAt(0).toUpperCase() + string.substr(1);
+}
+
+function getPrefixes(property, value) {
+ return properties[property].reduce(function (acc, item) {
+ acc['' + item + capitalize(property)] = value;
+ return acc;
+ }, {});
+}
+
+function addPrefixesTo(style, property, value) {
+ var vendor = getPrefixes(property, value);
+ for (var prefix in vendor) {
+ style[prefix] = vendor[prefix];
+ }
+
+ return style;
+}
+
+function prefixer(style) {
+ var defaultValue = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+ var _style = defaultValue;
+ for (var property in style) {
+ _style[property] = style[property];
+ if (properties[property]) {
+ addPrefixesTo(_style, property, style[property]);
+ }
+ }
+
+ return _style;
+}
+
+exports.default = prefixer;
\ No newline at end of file
diff --git a/lib/utils/testing.js b/lib/utils/testing.js
new file mode 100644
index 000000000..d8f2a1dd7
--- /dev/null
+++ b/lib/utils/testing.js
@@ -0,0 +1,38 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _reactAddonsTestUtils = require('react-addons-test-utils');
+
+var _reactAddonsTestUtils2 = _interopRequireDefault(_reactAddonsTestUtils);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+exports.default = {
+ renderComponent: function renderComponent(Component) {
+ var props = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+ var state = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
+
+ var component = _reactAddonsTestUtils2.default.renderIntoDocument(_react2.default.createElement(Component, props));
+ if (state !== {}) {
+ component.setState(state);
+ }
+ return component;
+ },
+ shallowRenderComponent: function shallowRenderComponent(component, props) {
+ var shallowRenderer = _reactAddonsTestUtils2.default.createRenderer();
+
+ for (var _len = arguments.length, children = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
+ children[_key - 2] = arguments[_key];
+ }
+
+ shallowRenderer.render(_react2.default.createElement(component, props, children.length > 1 ? children : children[0]));
+ return shallowRenderer.getRenderOutput();
+ }
+};
\ No newline at end of file
diff --git a/lib/utils/time.js b/lib/utils/time.js
new file mode 100644
index 000000000..1bc166776
--- /dev/null
+++ b/lib/utils/time.js
@@ -0,0 +1,210 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+var time = {
+ getDaysInMonth: function getDaysInMonth(d) {
+ var resultDate = this.getFirstDayOfMonth(d);
+ resultDate.setMonth(resultDate.getMonth() + 1);
+ resultDate.setDate(resultDate.getDate() - 1);
+ return resultDate.getDate();
+ },
+ getFirstDayOfMonth: function getFirstDayOfMonth(d) {
+ return new Date(d.getFullYear(), d.getMonth(), 1);
+ },
+ getFirstWeekDay: function getFirstWeekDay(d) {
+ return this.getFirstDayOfMonth(d).getDay();
+ },
+ getTimeMode: function getTimeMode(d) {
+ return d.getHours() >= 12 ? 'pm' : 'am';
+ },
+ getFullMonth: function getFullMonth(d) {
+ var month = d.getMonth();
+ switch (month) {
+ default:
+ return 'Unknown';
+ case 0:
+ return 'January';
+ case 1:
+ return 'February';
+ case 2:
+ return 'March';
+ case 3:
+ return 'April';
+ case 4:
+ return 'May';
+ case 5:
+ return 'June';
+ case 6:
+ return 'July';
+ case 7:
+ return 'August';
+ case 8:
+ return 'September';
+ case 9:
+ return 'October';
+ case 10:
+ return 'November';
+ case 11:
+ return 'December';
+ }
+ },
+ getShortMonth: function getShortMonth(d) {
+ var month = d.getMonth();
+ switch (month) {
+ default:
+ return 'Unknown';
+ case 0:
+ return 'Jan';
+ case 1:
+ return 'Feb';
+ case 2:
+ return 'Mar';
+ case 3:
+ return 'Apr';
+ case 4:
+ return 'May';
+ case 5:
+ return 'Jun';
+ case 6:
+ return 'Jul';
+ case 7:
+ return 'Aug';
+ case 8:
+ return 'Sep';
+ case 9:
+ return 'Oct';
+ case 10:
+ return 'Nov';
+ case 11:
+ return 'Dec';
+ }
+ },
+ getFullDayOfWeek: function getFullDayOfWeek(day) {
+ switch (day) {
+ default:
+ return 'Unknown';
+ case 0:
+ return 'Sunday';
+ case 1:
+ return 'Monday';
+ case 2:
+ return 'Tuesday';
+ case 3:
+ return 'Wednesday';
+ case 4:
+ return 'Thursday';
+ case 5:
+ return 'Friday';
+ case 6:
+ return 'Saturday';
+ }
+ },
+ getShortDayOfWeek: function getShortDayOfWeek(day) {
+ switch (day) {
+ default:
+ return 'Unknown';
+ case 0:
+ return 'Sun';
+ case 1:
+ return 'Mon';
+ case 2:
+ return 'Tue';
+ case 3:
+ return 'Wed';
+ case 4:
+ return 'Thu';
+ case 5:
+ return 'Fri';
+ case 6:
+ return 'Sat';
+ }
+ },
+ clone: function clone(d) {
+ return new Date(d.getTime());
+ },
+ cloneAsDate: function cloneAsDate(d) {
+ var clonedDate = this.clone(d);
+ clonedDate.setHours(0, 0, 0, 0);
+ return clonedDate;
+ },
+ isDateObject: function isDateObject(d) {
+ return d instanceof Date;
+ },
+ addDays: function addDays(d, days) {
+ var newDate = this.clone(d);
+ newDate.setDate(d.getDate() + days);
+ return newDate;
+ },
+ addMonths: function addMonths(d, months) {
+ var newDate = this.clone(d);
+ newDate.setMonth(d.getMonth() + months);
+ return newDate;
+ },
+ addYears: function addYears(d, years) {
+ var newDate = this.clone(d);
+ newDate.setFullYear(d.getFullYear() + years);
+ return newDate;
+ },
+ setDay: function setDay(d, day) {
+ var newDate = this.clone(d);
+ newDate.setDate(day);
+ return newDate;
+ },
+ setMonth: function setMonth(d, month) {
+ var newDate = this.clone(d);
+ newDate.setMonth(month);
+ return newDate;
+ },
+ setYear: function setYear(d, year) {
+ var newDate = this.clone(d);
+ newDate.setFullYear(year);
+ return newDate;
+ },
+ setHours: function setHours(d, hours) {
+ var newDate = this.clone(d);
+ newDate.setHours(hours);
+ return newDate;
+ },
+ setMinutes: function setMinutes(d, minutes) {
+ var newDate = this.clone(d);
+ newDate.setMinutes(minutes);
+ return newDate;
+ },
+ toggleTimeMode: function toggleTimeMode(d) {
+ var newDate = this.clone(d);
+ var hours = newDate.getHours();
+
+ newDate.setHours(hours - (hours > 12 ? -12 : 12));
+ return newDate;
+ },
+ formatTime: function formatTime(date, format) {
+ var hours = date.getHours();
+ var mins = date.getMinutes().toString();
+
+ if (format === 'ampm') {
+ var isAM = hours < 12;
+ var additional = isAM ? ' am' : ' pm';
+
+ hours = hours % 12;
+ hours = (hours || 12).toString();
+ if (mins.length < 2) mins = '0' + mins;
+
+ return hours + (mins === '00' ? '' : ':' + mins) + additional;
+ }
+
+ hours = hours.toString();
+ if (hours.length < 2) hours = '0' + hours;
+ if (mins.length < 2) mins = '0' + mins;
+ return hours + ':' + mins;
+ },
+ dateOutOfRange: function dateOutOfRange(date, minDate, maxDate) {
+ return minDate && !(date >= minDate) || maxDate && !(date <= maxDate);
+ },
+ formatDate: function formatDate(date) {
+ return date.getDate() + ' ' + time.getFullMonth(date) + ' ' + date.getFullYear();
+ }
+};
+
+exports.default = time;
\ No newline at end of file
diff --git a/lib/utils/utils.js b/lib/utils/utils.js
new file mode 100644
index 000000000..e31db2442
--- /dev/null
+++ b/lib/utils/utils.js
@@ -0,0 +1,64 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.default = {
+ angleFromPositions: function angleFromPositions(cx, cy, ex, ey) {
+ var theta = Math.atan2(ey - cy, ex - cx) + Math.PI / 2;
+ return theta * 180 / Math.PI;
+ },
+ angle360FromPositions: function angle360FromPositions(cx, cy, ex, ey) {
+ var angle = this.angleFromPositions(cx, cy, ex, ey);
+ return angle < 0 ? 360 + angle : angle;
+ },
+ range: function range() {
+ var start = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];
+ var stop = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];
+ var step = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2];
+ var _start = 0;
+ var _stop = start;
+
+ if (stop !== null) {
+ _start = start;
+ _stop = stop;
+ }
+ var length = Math.max(Math.ceil((_stop - _start) / step), 0);
+ var range = Array(length);
+
+ for (var idx = 0; idx < length; idx++, _start += step) {
+ range[idx] = _start;
+ }
+
+ return range;
+ },
+ round: function round(number, decimals) {
+ if (!isNaN(parseFloat(number)) && isFinite(number)) {
+ var decimalPower = Math.pow(10, decimals);
+ return Math.round(parseFloat(number) * decimalPower) / decimalPower;
+ }
+ return NaN;
+ },
+ getViewport: function getViewport() {
+ return {
+ height: window.innerHeight || document.documentElement.offsetHeight,
+ width: window.innerWidth || document.documentElement.offsetWidth
+ };
+ },
+ cloneObject: function cloneObject(object) {
+ return JSON.parse(JSON.stringify(object));
+ },
+ inputTypeForPrototype: function inputTypeForPrototype(prototype) {
+ if (prototype === Date) return 'date';
+ if (prototype === Number) return 'number';
+ if (prototype === Boolean) return 'checkbox';
+ return 'text';
+ },
+ prepareValueForInput: function prepareValueForInput(value, type) {
+ if (type === 'date') return new Date(value).toISOString().slice(0, 10);
+ if (type === 'checkbox') {
+ return value ? 'on' : '';
+ }
+ return value;
+ }
+};
\ No newline at end of file
diff --git a/package.json b/package.json
index d8ae44e7d..7f103ef77 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,8 @@
],
"dependencies": {
"classnames": "^2.2.5",
- "core-js": "^2.4.0"
+ "core-js": "^2.4.0",
+ "react-css-themr": "^1.1.0"
},
"devDependencies": {
"autoprefixer": "^6.3.6",
diff --git a/server.js b/server.js
index a0942f30f..e52c1bbc3 100644
--- a/server.js
+++ b/server.js
@@ -7,8 +7,8 @@ const app = express();
const compiler = webpack(config);
app.use(require('webpack-dev-middleware')(compiler, {
- noInfo: true,
- publicPath: config.output.publicPath,
+ noInfo: true,
+ publicPath: config.output.publicPath,
stats: {
colors: true
}
@@ -17,14 +17,14 @@ app.use(require('webpack-dev-middleware')(compiler, {
app.use(require('webpack-hot-middleware')(compiler));
app.get('*', function (req, res) {
- res.sendFile(path.join(__dirname, './spec/index.html'));
+ res.sendFile(path.join(__dirname, './spec/index.html'));
});
app.listen(8080, '0.0.0.0', function (err) {
- if (err) {
- console.log(err);
- return;
- }
+ if (err) {
+ console.log(err);
+ return;
+ }
- console.log('Listening at http://0.0.0.0:8080');
+ console.log('Listening at http://0.0.0.0:8080');
});
diff --git a/spec/components/dropdown.js b/spec/components/dropdown.js
index 01f454adf..e749c30fc 100644
--- a/spec/components/dropdown.js
+++ b/spec/components/dropdown.js
@@ -33,12 +33,6 @@ class DropdownTest extends React.Component {
);
}
- handleFocus = (dropdowns) => {
- dropdowns.forEach((dropdown) => {
- this.refs[dropdown].close();
- });
- }
-
render () {
return (
);
}
diff --git a/spec/components/layout.js b/spec/components/layout.js
index f975c7164..4190a4656 100644
--- a/spec/components/layout.js
+++ b/spec/components/layout.js
@@ -96,7 +96,7 @@ class LayoutTest extends React.Component {
-
+
diff --git a/spec/components/list.js b/spec/components/list.js
index 0bd002a9f..715cdf390 100644
--- a/spec/components/list.js
+++ b/spec/components/list.js
@@ -1,8 +1,8 @@
import React from 'react';
import { ListCheckbox, ListSubHeader, List, ListItem, ListDivider, ListItemText, ListItemContent } from '../../components/list';
+import { Button } from '../../components/button';
import Avatar from '../../components/avatar';
import FontIcon from '../../components/font_icon';
-import {Button} from '../../components/button';
const listStyle = {
border: '1px solid #EEE',
diff --git a/spec/components/table.js b/spec/components/table.js
index 1b17cb61d..8598aa632 100644
--- a/spec/components/table.js
+++ b/spec/components/table.js
@@ -7,12 +7,11 @@ const UserModel = {
birthdate: {type: Date},
cats: {type: Number, onChange (...args) { console.log('changes:', ...args); } },
dogs: {type: Number},
- owner: {type: Boolean },
- image: {title: }
+ owner: {type: Boolean }
};
const users = [
- {name: 'Javi Jimenez', twitter: '@soyjavi', birthdate: new Date(1980, 3, 11), cats: 1, image: },
+ {name: 'Javi Jimenez', twitter: '@soyjavi', birthdate: new Date(1980, 3, 11), cats: 1},
{name: 'Javi Velasco', twitter: '@javivelasco', birthdate: new Date(1987, 1, 1), dogs: 1, owner: true}
];
diff --git a/spec/root.js b/spec/root.js
index 9757351dc..a4121d853 100644
--- a/spec/root.js
+++ b/spec/root.js
@@ -1,10 +1,11 @@
/* global VERSION */
+import '../components/commons.scss';
import React from 'react';
-import AppBarToolbox from '../components/app_bar';
-import Avatar from './components/avatar';
-import ButtonToolbox from '../components/button';
+import AppBar from '../components/app_bar';
import Autocomplete from './components/autocomplete';
+import Avatar from './components/avatar';
import Button from './components/button';
+import ButtonToolbox from '../components/button';
import Card from './components/card';
import Checkbox from './components/checkbox';
import Chip from './components/chip';
@@ -19,31 +20,26 @@ import Menu from './components/menu';
import Pickers from './components/pickers';
import Progress from './components/progress';
import Radio from './components/radio';
-import Snackbar from './components/snackbar';
import Slider from './components/slider';
+import Snackbar from './components/snackbar';
import Switch from './components/switch';
import Table from './components/table';
import Tabs from './components/tabs';
import Tooltip from './components/tooltip';
import style from './style';
-const _hrefProject = () => {
- window.href = 'http://react-toolbox';
-};
-
const Root = () => (
-
+
React Toolbox Spec {VERSION}
{window.href = 'http://react-toolbox';}}
/>
-
-
+
diff --git a/spec/style.scss b/spec/style.scss
index 60208e2e2..301047ea5 100644
--- a/spec/style.scss
+++ b/spec/style.scss
@@ -1,4 +1,4 @@
-@import "../components/commons";
+@import "../components/base";
@import "../components/app_bar/config";
@import "../components/button/config";
diff --git a/webpack.config.development.js b/webpack.config.development.js
index 90127281e..95fecdcfb 100644
--- a/webpack.config.development.js
+++ b/webpack.config.development.js
@@ -6,7 +6,7 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
context: __dirname,
- devtool: 'inline-source-map',
+ devtool: 'inline-source-map',
entry: [
'webpack-hot-middleware/client',
'./spec/index.js'
@@ -25,7 +25,7 @@ module.exports = {
{
test: /\.js$/,
loader: 'babel',
- exclude: /(node_modules)/
+ exclude: [/(node_modules)/, /react-css-themr/]
}, {
test: /\.(scss|css)$/,
loader: ExtractTextPlugin.extract('style', 'css?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss!sass?sourceMap')