Update dependencies and linter (#1180)
parent
bc18e56065
commit
9d6ec1eedd
|
@ -1,3 +1,10 @@
|
||||||
**/node_modules/
|
**/node_modules/
|
||||||
**/build/
|
**/build/
|
||||||
|
docs/
|
||||||
|
spec/
|
||||||
lib
|
lib
|
||||||
|
karma.conf.js
|
||||||
|
gulpfile.js
|
||||||
|
tests.webpack*
|
||||||
|
server.js
|
||||||
|
webpack*
|
||||||
|
|
239
.eslintrc
239
.eslintrc
|
@ -1,227 +1,30 @@
|
||||||
{
|
{
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"extends": "airbnb",
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"node": true,
|
"node": true,
|
||||||
"mocha": true,
|
"jest": true,
|
||||||
"es6": true
|
"es6": true
|
||||||
},
|
},
|
||||||
|
|
||||||
"ecmaFeatures": {
|
|
||||||
"jsx": true,
|
|
||||||
"templateStrings": true,
|
|
||||||
"superInFunctions": false,
|
|
||||||
"classes": true,
|
|
||||||
"modules": true
|
|
||||||
},
|
|
||||||
|
|
||||||
"parser": "babel-eslint",
|
|
||||||
|
|
||||||
"plugins": [
|
|
||||||
"babel",
|
|
||||||
"react"
|
|
||||||
],
|
|
||||||
|
|
||||||
"rules": {
|
"rules": {
|
||||||
"block-scoped-var": [0],
|
"func-names": "off",
|
||||||
"brace-style": [2, "1tbs", {
|
"global-require": "off",
|
||||||
"allowSingleLine": true
|
"no-use-before-define": 0,
|
||||||
}],
|
"no-underscore-dangle": 0,
|
||||||
"camelcase": [0],
|
"react/sort-prop-types": 2,
|
||||||
"comma-dangle": [2, "never"],
|
"react/jsx-no-bind": 2,
|
||||||
"comma-spacing": [2],
|
"react/require-default-props": 0,
|
||||||
"comma-style": [2, "last"],
|
"react/no-find-dom-node": 0,
|
||||||
"complexity": [0, 11],
|
"react/jsx-filename-extension": 0,
|
||||||
"constructor-super": [2],
|
"import/prefer-default-export": 0,
|
||||||
"consistent-return": [0],
|
"jsx-a11y/no-static-element-interactions": 0,
|
||||||
"consistent-this": [0, "that"],
|
"import/no-extraneous-dependencies": [
|
||||||
"curly": [2, "multi-line"],
|
"error", {
|
||||||
"default-case": [2],
|
"devDependencies": true,
|
||||||
"dot-notation": [2, {
|
"optionalDependencies": false,
|
||||||
"allowKeywords": true
|
"peerDependencies": false
|
||||||
}],
|
}
|
||||||
"eol-last": [2],
|
]
|
||||||
"eqeqeq": [2],
|
|
||||||
"func-names": [0],
|
|
||||||
"func-style": [0, "declaration"],
|
|
||||||
"generator-star-spacing": [2, "after"],
|
|
||||||
"guard-for-in": [0],
|
|
||||||
"handle-callback-err": [0],
|
|
||||||
"key-spacing": [2, {
|
|
||||||
"beforeColon": false,
|
|
||||||
"afterColon": true
|
|
||||||
}],
|
|
||||||
"quotes": [2, "single", "avoid-escape"],
|
|
||||||
"max-depth": [0, 4],
|
|
||||||
"max-len": [0, 80, 4],
|
|
||||||
"max-nested-callbacks": [0, 2],
|
|
||||||
"max-params": [0, 3],
|
|
||||||
"max-statements": [0, 10],
|
|
||||||
"new-parens": [2],
|
|
||||||
"new-cap": [0],
|
|
||||||
"newline-after-var": [0],
|
|
||||||
"no-alert": [2],
|
|
||||||
"no-array-constructor": [2],
|
|
||||||
"no-bitwise": [0],
|
|
||||||
"no-caller": [2],
|
|
||||||
"no-catch-shadow": [2],
|
|
||||||
"no-cond-assign": [2],
|
|
||||||
"no-console": [0],
|
|
||||||
"no-constant-condition": [1],
|
|
||||||
"no-continue": [2],
|
|
||||||
"no-control-regex": [2],
|
|
||||||
"no-debugger": [2],
|
|
||||||
"no-delete-var": [2],
|
|
||||||
"no-div-regex": [0],
|
|
||||||
"no-dupe-args": [2],
|
|
||||||
"no-dupe-keys": [2],
|
|
||||||
"no-duplicate-case": [2],
|
|
||||||
"no-else-return": [0],
|
|
||||||
"no-empty": [2],
|
|
||||||
"no-empty-character-class": [2],
|
|
||||||
"no-eq-null": [0],
|
|
||||||
"no-eval": [2],
|
|
||||||
"no-ex-assign": [2],
|
|
||||||
"no-extend-native": [1],
|
|
||||||
"no-extra-bind": [2],
|
|
||||||
"no-extra-boolean-cast": [2],
|
|
||||||
"no-extra-parens": [0],
|
|
||||||
"no-extra-semi": [1],
|
|
||||||
"no-fallthrough": [2],
|
|
||||||
"no-floating-decimal": [2],
|
|
||||||
"no-func-assign": [2],
|
|
||||||
"no-implied-eval": [2],
|
|
||||||
"no-inline-comments": [0],
|
|
||||||
"no-inner-declarations": [2, "functions"],
|
|
||||||
"no-invalid-regexp": [2],
|
|
||||||
"no-irregular-whitespace": [2],
|
|
||||||
"no-iterator": [2],
|
|
||||||
"no-label-var": [2],
|
|
||||||
"no-labels": [2],
|
|
||||||
"no-lone-blocks": [2],
|
|
||||||
"no-lonely-if": [2],
|
|
||||||
"no-loop-func": [2],
|
|
||||||
"no-mixed-requires": [0, false],
|
|
||||||
"no-mixed-spaces-and-tabs": [2, false],
|
|
||||||
"no-multi-spaces": [2],
|
|
||||||
"no-multi-str": [2],
|
|
||||||
"no-multiple-empty-lines": [2, {
|
|
||||||
"max": 2
|
|
||||||
}],
|
|
||||||
"no-native-reassign": [1],
|
|
||||||
"no-negated-in-lhs": [2],
|
|
||||||
"no-nested-ternary": [0],
|
|
||||||
"no-new": [2],
|
|
||||||
"no-new-func": [2],
|
|
||||||
"no-new-object": [2],
|
|
||||||
"no-new-require": [0],
|
|
||||||
"no-new-wrappers": [2],
|
|
||||||
"no-obj-calls": [2],
|
|
||||||
"no-octal": [2],
|
|
||||||
"no-octal-escape": [2],
|
|
||||||
"no-path-concat": [0],
|
|
||||||
"no-param-reassign": [2],
|
|
||||||
"no-plusplus": [0],
|
|
||||||
"no-process-env": [0],
|
|
||||||
"no-process-exit": [2],
|
|
||||||
"no-proto": [2],
|
|
||||||
"no-redeclare": [2],
|
|
||||||
"no-regex-spaces": [2],
|
|
||||||
"no-reserved-keys": [0],
|
|
||||||
"no-restricted-modules": [0],
|
|
||||||
"no-return-assign": [2],
|
|
||||||
"no-script-url": [2],
|
|
||||||
"no-self-compare": [0],
|
|
||||||
"no-sequences": [2],
|
|
||||||
"no-shadow": [2],
|
|
||||||
"no-shadow-restricted-names": [2],
|
|
||||||
"semi-spacing": [2],
|
|
||||||
"no-spaced-func": [2],
|
|
||||||
"no-sparse-arrays": [2],
|
|
||||||
"no-sync": [0],
|
|
||||||
"no-ternary": [0],
|
|
||||||
"no-this-before-super": [2],
|
|
||||||
"no-throw-literal": [2],
|
|
||||||
"no-trailing-spaces": [2],
|
|
||||||
"no-undef": [2],
|
|
||||||
"no-undef-init": [2],
|
|
||||||
"no-undefined": [0],
|
|
||||||
"no-underscore-dangle": [0],
|
|
||||||
"no-unreachable": [2],
|
|
||||||
"no-unused-expressions": [2, {
|
|
||||||
"allowShortCircuit": true
|
|
||||||
}],
|
|
||||||
"no-unused-vars": [1, {
|
|
||||||
"vars": "all",
|
|
||||||
"args": "after-used"
|
|
||||||
}],
|
|
||||||
"no-use-before-define": [2, "nofunc"],
|
|
||||||
"no-var": [2],
|
|
||||||
"no-void": [0],
|
|
||||||
"no-warning-comments": [0, {
|
|
||||||
"terms": ["todo", "fixme", "xxx"],
|
|
||||||
"location": "start"
|
|
||||||
}],
|
|
||||||
"no-with": [2],
|
|
||||||
"object-shorthand": [2],
|
|
||||||
"one-var": [0],
|
|
||||||
"operator-assignment": [0, "always"],
|
|
||||||
"operator-linebreak": [2, "before"],
|
|
||||||
"padded-blocks": [0],
|
|
||||||
"prefer-const": [2],
|
|
||||||
"prefer-spread": [2],
|
|
||||||
"quote-props": [0],
|
|
||||||
"radix": [0],
|
|
||||||
"semi": [2],
|
|
||||||
"sort-vars": [0],
|
|
||||||
"keyword-spacing": [2, {"after": true}],
|
|
||||||
"space-before-function-paren": [2, { "anonymous": "always", "named": "always" }],
|
|
||||||
"space-before-blocks": [0, "always"],
|
|
||||||
"space-in-brackets": [0, "never", {
|
|
||||||
"singleValue": true,
|
|
||||||
"arraysInArrays": false,
|
|
||||||
"arraysInObjects": false,
|
|
||||||
"objectsInArrays": true,
|
|
||||||
"objectsInObjects": true,
|
|
||||||
"propertyName": false
|
|
||||||
}],
|
|
||||||
"space-in-parens": [2, "never"],
|
|
||||||
"space-infix-ops": [2],
|
|
||||||
"space-unary-ops": [2, {
|
|
||||||
"words": true,
|
|
||||||
"nonwords": false
|
|
||||||
}],
|
|
||||||
"spaced-line-comment": [0, "always"],
|
|
||||||
"strict": [1],
|
|
||||||
"use-isnan": [2],
|
|
||||||
"valid-jsdoc": [0],
|
|
||||||
"valid-typeof": [2],
|
|
||||||
"vars-on-top": [0],
|
|
||||||
"wrap-iife": [2],
|
|
||||||
"wrap-regex": [2],
|
|
||||||
"yoda": [2, "never", {
|
|
||||||
"exceptRange": true
|
|
||||||
}],
|
|
||||||
"react/display-name": 0,
|
|
||||||
"react/jsx-boolean-value": 1,
|
|
||||||
"react/jsx-closing-bracket-location": 0,
|
|
||||||
"react/jsx-curly-spacing": 1,
|
|
||||||
"react/jsx-max-props-per-line": 0,
|
|
||||||
"react/jsx-indent-props": 0,
|
|
||||||
"react/jsx-no-duplicate-props": 1,
|
|
||||||
"react/jsx-no-undef": 1,
|
|
||||||
"react/jsx-pascal-case": 1,
|
|
||||||
"react/sort-prop-types": 1,
|
|
||||||
"react/jsx-sort-props": 0,
|
|
||||||
"react/jsx-uses-react": 1,
|
|
||||||
"react/jsx-uses-vars": 1,
|
|
||||||
"react/no-danger": 0,
|
|
||||||
"react/no-did-mount-set-state": 0,
|
|
||||||
"react/no-did-update-set-state": 1,
|
|
||||||
"react/no-multi-comp": 0,
|
|
||||||
"react/no-unknown-property": 1,
|
|
||||||
"react/prop-types": [2, {"ignore": ["onMouseDown", "onTouchStart"]}],
|
|
||||||
"react/react-in-jsx-scope": 1,
|
|
||||||
"react/self-closing-comp": 1,
|
|
||||||
"react/sort-comp": 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { APP_BAR } from '../identifiers.js';
|
import { APP_BAR } from '../identifiers';
|
||||||
import InjectIconButton from '../button/IconButton.js';
|
import InjectIconButton from '../button/IconButton';
|
||||||
|
|
||||||
const factory = (IconButton) => {
|
const factory = (IconButton) => {
|
||||||
class AppBar extends React.Component {
|
class AppBar extends React.Component {
|
||||||
|
@ -13,13 +13,13 @@ const factory = (IconButton) => {
|
||||||
flat: PropTypes.bool,
|
flat: PropTypes.bool,
|
||||||
leftIcon: PropTypes.oneOfType([
|
leftIcon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
onLeftIconClick: PropTypes.func,
|
onLeftIconClick: PropTypes.func,
|
||||||
onRightIconClick: PropTypes.func,
|
onRightIconClick: PropTypes.func,
|
||||||
rightIcon: PropTypes.oneOfType([
|
rightIcon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
scrollHide: PropTypes.bool,
|
scrollHide: PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
|
@ -30,27 +30,27 @@ const factory = (IconButton) => {
|
||||||
leftIcon: PropTypes.string,
|
leftIcon: PropTypes.string,
|
||||||
rightIcon: PropTypes.string,
|
rightIcon: PropTypes.string,
|
||||||
scrollHide: PropTypes.string,
|
scrollHide: PropTypes.string,
|
||||||
title: PropTypes.string
|
title: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
title: PropTypes.node
|
title: PropTypes.node,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
className: '',
|
className: '',
|
||||||
fixed: false,
|
fixed: false,
|
||||||
flat: false,
|
flat: false,
|
||||||
scrollHide: false
|
scrollHide: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {hidden: false, height: 0};
|
state = { hidden: false, height: 0 };
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
if (this.props.scrollHide) {
|
if (this.props.scrollHide) {
|
||||||
this.initializeScroll();
|
this.initializeScroll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (!this.props.scrollHide && nextProps.scrollHide) {
|
if (!this.props.scrollHide && nextProps.scrollHide) {
|
||||||
this.initializeScroll();
|
this.initializeScroll();
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ const factory = (IconButton) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
if (this.props.scrollHide) {
|
if (this.props.scrollHide) {
|
||||||
this.endScroll();
|
this.endScroll();
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ const factory = (IconButton) => {
|
||||||
window.addEventListener('scroll', this.handleScroll);
|
window.addEventListener('scroll', this.handleScroll);
|
||||||
const { height } = this.rootNode.getBoundingClientRect();
|
const { height } = this.rootNode.getBoundingClientRect();
|
||||||
this.curScroll = window.scrollY;
|
this.curScroll = window.scrollY;
|
||||||
this.setState({height});
|
this.setState({ height });
|
||||||
};
|
};
|
||||||
|
|
||||||
endScroll = () => {
|
endScroll = () => {
|
||||||
|
@ -79,31 +79,42 @@ const factory = (IconButton) => {
|
||||||
|
|
||||||
handleScroll = () => {
|
handleScroll = () => {
|
||||||
const scrollDiff = this.curScroll - window.scrollY;
|
const scrollDiff = this.curScroll - window.scrollY;
|
||||||
const hidden = scrollDiff < 0 && window.scrollY !== undefined && window.scrollY > this.state.height;
|
const hidden = scrollDiff < 0
|
||||||
this.setState({hidden});
|
&& window.scrollY !== undefined
|
||||||
|
&& window.scrollY > this.state.height;
|
||||||
|
this.setState({ hidden });
|
||||||
this.curScroll = window.scrollY;
|
this.curScroll = window.scrollY;
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { children, leftIcon, onLeftIconClick, onRightIconClick, rightIcon, theme, title } = this.props;
|
const {
|
||||||
|
children,
|
||||||
|
leftIcon,
|
||||||
|
onLeftIconClick,
|
||||||
|
onRightIconClick,
|
||||||
|
rightIcon,
|
||||||
|
theme,
|
||||||
|
title,
|
||||||
|
} = this.props;
|
||||||
const className = classnames(theme.appBar, {
|
const className = classnames(theme.appBar, {
|
||||||
[theme.fixed]: this.props.fixed,
|
[theme.fixed]: this.props.fixed,
|
||||||
[theme.flat]: this.props.flat,
|
[theme.flat]: this.props.flat,
|
||||||
[theme.scrollHide]: this.state.hidden
|
[theme.scrollHide]: this.state.hidden,
|
||||||
}, this.props.className);
|
}, this.props.className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
className={className}
|
className={className}
|
||||||
data-react-toolbox='app-bar'
|
data-react-toolbox="app-bar"
|
||||||
ref={node => {this.rootNode = node;}}
|
ref={(node) => { this.rootNode = node; }}
|
||||||
>
|
>
|
||||||
<div className={theme.inner}>
|
<div className={theme.inner}>
|
||||||
{leftIcon && <IconButton
|
{leftIcon && <IconButton
|
||||||
inverse
|
inverse
|
||||||
className={classnames(theme.leftIcon)}
|
className={classnames(theme.leftIcon)}
|
||||||
onClick={onLeftIconClick}
|
onClick={onLeftIconClick}
|
||||||
icon={leftIcon} />
|
icon={leftIcon}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
{title && <h1 className={classnames(theme.title)}>{title}</h1>}
|
{title && <h1 className={classnames(theme.title)}>{title}</h1>}
|
||||||
{children}
|
{children}
|
||||||
|
@ -111,7 +122,8 @@ const factory = (IconButton) => {
|
||||||
inverse
|
inverse
|
||||||
className={classnames(theme.rightIcon)}
|
className={classnames(theme.rightIcon)}
|
||||||
onClick={onRightIconClick}
|
onClick={onRightIconClick}
|
||||||
icon={rightIcon} />
|
icon={rightIcon}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { APP_BAR } from '../identifiers.js';
|
import { APP_BAR } from '../identifiers';
|
||||||
import { appBarFactory } from './AppBar.js';
|
import { appBarFactory } from './AppBar';
|
||||||
import { IconButton } from '../button';
|
import { IconButton } from '../button';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable */
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
@ -10,7 +11,7 @@ import events from '../utils/events.js';
|
||||||
const POSITION = {
|
const POSITION = {
|
||||||
AUTO: 'auto',
|
AUTO: 'auto',
|
||||||
DOWN: 'down',
|
DOWN: 'down',
|
||||||
UP: 'up'
|
UP: 'up',
|
||||||
};
|
};
|
||||||
|
|
||||||
const factory = (Chip, Input) => {
|
const factory = (Chip, Input) => {
|
||||||
|
@ -22,12 +23,12 @@ const factory = (Chip, Input) => {
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
error: React.PropTypes.oneOfType([
|
error: React.PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
React.PropTypes.string,
|
||||||
React.PropTypes.node
|
React.PropTypes.node,
|
||||||
]),
|
]),
|
||||||
keepFocusOnChange: PropTypes.bool,
|
keepFocusOnChange: PropTypes.bool,
|
||||||
label: React.PropTypes.oneOfType([
|
label: React.PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
React.PropTypes.string,
|
||||||
React.PropTypes.node
|
React.PropTypes.node,
|
||||||
]),
|
]),
|
||||||
multiple: PropTypes.bool,
|
multiple: PropTypes.bool,
|
||||||
onBlur: PropTypes.func,
|
onBlur: PropTypes.func,
|
||||||
|
@ -49,9 +50,9 @@ const factory = (Chip, Input) => {
|
||||||
suggestions: PropTypes.string,
|
suggestions: PropTypes.string,
|
||||||
up: PropTypes.string,
|
up: PropTypes.string,
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
values: PropTypes.string
|
values: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
value: PropTypes.any
|
value: PropTypes.any,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -64,7 +65,7 @@ const factory = (Chip, Input) => {
|
||||||
showSelectedWhenNotInSource: false,
|
showSelectedWhenNotInSource: false,
|
||||||
showSuggestionsWhenValueIsSet: false,
|
showSuggestionsWhenValueIsSet: false,
|
||||||
source: {},
|
source: {},
|
||||||
suggestionMatch: 'start'
|
suggestionMatch: 'start',
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -72,17 +73,17 @@ const factory = (Chip, Input) => {
|
||||||
focus: false,
|
focus: false,
|
||||||
showAllSuggestions: this.props.showSuggestionsWhenValueIsSet,
|
showAllSuggestions: this.props.showSuggestionsWhenValueIsSet,
|
||||||
query: this.props.query ? this.props.query : this.query(this.props.value),
|
query: this.props.query ? this.props.query : this.query(this.props.value),
|
||||||
isValueAnObject: false
|
isValueAnObject: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (!this.props.multiple) {
|
if (!this.props.multiple) {
|
||||||
const query = nextProps.query ? nextProps.query : this.query(nextProps.value);
|
const query = nextProps.query ? nextProps.query : this.query(nextProps.value);
|
||||||
this.updateQuery(query, false);
|
this.updateQuery(query, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate (nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
if (!this.state.focus && nextState.focus && this.props.direction === POSITION.AUTO) {
|
if (!this.state.focus && nextState.focus && this.props.direction === POSITION.AUTO) {
|
||||||
const direction = this.calculateDirection();
|
const direction = this.calculateDirection();
|
||||||
if (this.state.direction !== direction) {
|
if (this.state.direction !== direction) {
|
||||||
|
@ -112,7 +113,7 @@ const factory = (Chip, Input) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleQueryBlur = (event) => {
|
handleQueryBlur = (event) => {
|
||||||
if (this.state.focus) this.setState({focus: false});
|
if (this.state.focus) this.setState({ focus: false });
|
||||||
if (this.props.onBlur) this.props.onBlur(event, this.state.active);
|
if (this.props.onBlur) this.props.onBlur(event, this.state.active);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,12 +127,12 @@ const factory = (Chip, Input) => {
|
||||||
this.clearQuery = false;
|
this.clearQuery = false;
|
||||||
|
|
||||||
this.updateQuery(query, true);
|
this.updateQuery(query, true);
|
||||||
this.setState({showAllSuggestions: false, active: null});
|
this.setState({ showAllSuggestions: false, active: null });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleQueryFocus = (event) => {
|
handleQueryFocus = (event) => {
|
||||||
this.suggestionsNode.scrollTop = 0;
|
this.suggestionsNode.scrollTop = 0;
|
||||||
this.setState({active: '', focus: true});
|
this.setState({ active: '', focus: true });
|
||||||
if (this.props.onFocus) this.props.onFocus(event);
|
if (this.props.onFocus) this.props.onFocus(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -156,46 +157,45 @@ const factory = (Chip, Input) => {
|
||||||
let index = suggestionsKeys.indexOf(this.state.active) + (event.which === 40 ? +1 : -1);
|
let index = suggestionsKeys.indexOf(this.state.active) + (event.which === 40 ? +1 : -1);
|
||||||
if (index < 0) index = suggestionsKeys.length - 1;
|
if (index < 0) index = suggestionsKeys.length - 1;
|
||||||
if (index >= suggestionsKeys.length) index = 0;
|
if (index >= suggestionsKeys.length) index = 0;
|
||||||
this.setState({active: suggestionsKeys[index]});
|
this.setState({ active: suggestionsKeys[index] });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSuggestionHover = (event) => {
|
handleSuggestionHover = (event) => {
|
||||||
this.setState({active: event.target.id});
|
this.setState({ active: event.target.id });
|
||||||
};
|
};
|
||||||
|
|
||||||
calculateDirection () {
|
calculateDirection() {
|
||||||
if (this.props.direction === 'auto') {
|
if (this.props.direction === 'auto') {
|
||||||
const client = ReactDOM.findDOMNode(this.inputNode).getBoundingClientRect();
|
const client = ReactDOM.findDOMNode(this.inputNode).getBoundingClientRect();
|
||||||
const screen_height = window.innerHeight || document.documentElement.offsetHeight;
|
const screen_height = window.innerHeight || document.documentElement.offsetHeight;
|
||||||
const up = client.top > ((screen_height / 2) + client.height);
|
const up = client.top > ((screen_height / 2) + client.height);
|
||||||
return up ? 'up' : 'down';
|
return up ? 'up' : 'down';
|
||||||
} else {
|
}
|
||||||
return this.props.direction;
|
return this.props.direction;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
query (key) {
|
query(key) {
|
||||||
let query_value = '';
|
let query_value = '';
|
||||||
if (!this.props.multiple && key) {
|
if (!this.props.multiple && key) {
|
||||||
const source_value = this.source().get(`${key}`);
|
const source_value = this.source().get(`${key}`);
|
||||||
query_value = source_value ? source_value : key;
|
query_value = source_value || key;
|
||||||
}
|
}
|
||||||
return query_value;
|
return query_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectOrCreateActiveItem (event) {
|
selectOrCreateActiveItem(event) {
|
||||||
let target = this.state.active;
|
let target = this.state.active;
|
||||||
if (!target) {
|
if (!target) {
|
||||||
target = this.props.allowCreate
|
target = this.props.allowCreate
|
||||||
? this.state.query
|
? this.state.query
|
||||||
: [...this.suggestions().keys()][0];
|
: [...this.suggestions().keys()][0];
|
||||||
this.setState({active: target});
|
this.setState({ active: target });
|
||||||
}
|
}
|
||||||
this.select(event, target);
|
this.select(event, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
normalise (value) {
|
normalise(value) {
|
||||||
const sdiak = 'áâäąáâäąččććççĉĉďđďđééěëēėęéěëēėęĝĝğğġġģģĥĥħħíîíîĩĩīīĭĭįįi̇ıĵĵķķĸĺĺļļŀŀłłĺľĺľňńņŋŋņňńʼnóöôőøōōóöőôøřřŕŕŗŗššśśŝŝşşţţťťŧŧũũūūŭŭůůűűúüúüűųųŵŵýyŷŷýyžžźźżżß';
|
const sdiak = 'áâäąáâäąččććççĉĉďđďđééěëēėęéěëēėęĝĝğğġġģģĥĥħħíîíîĩĩīīĭĭįįi̇ıĵĵķķĸĺĺļļŀŀłłĺľĺľňńņŋŋņňńʼnóöôőøōōóöőôøřřŕŕŗŗššśśŝŝşşţţťťŧŧũũūūŭŭůůűűúüúüűųųŵŵýyŷŷýyžžźźżżß';
|
||||||
const bdiak = 'AAAAAAAACCCCCCCCDDDDEEEEEEEEEEEEEGGGGGGGGHHHHIIIIIIIIIIIIIIJJKKKLLLLLLLLLLLLNNNNNNNNNOOOOOOOOOOOORRRRRRSSSSSSSSTTTTTTUUUUUUUUUUUUUUUUUWWYYYYYYZZZZZZS';
|
const bdiak = 'AAAAAAAACCCCCCCCDDDDEEEEEEEEEEEEEGGGGGGGGHHHHIIIIIIIIIIIIIIJJKKKLLLLLLLLLLLLNNNNNNNNNOOOOOOOOOOOORRRRRRSSSSSSSSTTTTTTUUUUUUUUUUUUUUUUUWWYYYYYYZZZZZZS';
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ const factory = (Chip, Input) => {
|
||||||
return normalised.toLowerCase().trim();
|
return normalised.toLowerCase().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestions () {
|
suggestions() {
|
||||||
let suggest = new Map();
|
let suggest = new Map();
|
||||||
const rawQuery = this.state.query || (this.props.multiple ? '' : this.props.value);
|
const rawQuery = this.state.query || (this.props.multiple ? '' : this.props.value);
|
||||||
const query = this.normalise((`${rawQuery}`));
|
const query = this.normalise((`${rawQuery}`));
|
||||||
|
@ -242,7 +242,7 @@ const factory = (Chip, Input) => {
|
||||||
return suggest;
|
return suggest;
|
||||||
}
|
}
|
||||||
|
|
||||||
matches (value, query) {
|
matches(value, query) {
|
||||||
const { suggestionMatch } = this.props;
|
const { suggestionMatch } = this.props;
|
||||||
|
|
||||||
if (suggestionMatch === 'disabled') {
|
if (suggestionMatch === 'disabled') {
|
||||||
|
@ -259,16 +259,15 @@ const factory = (Chip, Input) => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
source () {
|
source() {
|
||||||
const { source: src } = this.props;
|
const { source: src } = this.props;
|
||||||
if (src.hasOwnProperty('length')) {
|
if (src.hasOwnProperty('length')) {
|
||||||
return new Map(src.map((item) => Array.isArray(item) ? [...item] : [item, item]));
|
return new Map(src.map(item => Array.isArray(item) ? [...item] : [item, item]));
|
||||||
} else {
|
|
||||||
return new Map(Object.keys(src).map((key) => [`${key}`, src[key]]));
|
|
||||||
}
|
}
|
||||||
|
return new Map(Object.keys(src).map(key => [`${key}`, src[key]]));
|
||||||
}
|
}
|
||||||
|
|
||||||
values () {
|
values() {
|
||||||
let vals = this.props.multiple ? this.props.value : [this.props.value];
|
let vals = this.props.multiple ? this.props.value : [this.props.value];
|
||||||
|
|
||||||
if (!vals) vals = [];
|
if (!vals) vals = [];
|
||||||
|
@ -307,7 +306,7 @@ const factory = (Chip, Input) => {
|
||||||
this.handleChange([newValue, ...values.keys()], event);
|
this.handleChange([newValue, ...values.keys()], event);
|
||||||
};
|
};
|
||||||
|
|
||||||
unselect (key, event) {
|
unselect(key, event) {
|
||||||
if (!this.props.disabled) {
|
if (!this.props.disabled) {
|
||||||
const values = this.values(this.props.value);
|
const values = this.values(this.props.value);
|
||||||
|
|
||||||
|
@ -321,21 +320,20 @@ const factory = (Chip, Input) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isValueAnObject () {
|
isValueAnObject() {
|
||||||
return !Array.isArray(this.props.value) && typeof this.props.value === 'object';
|
return !Array.isArray(this.props.value) && typeof this.props.value === 'object';
|
||||||
}
|
}
|
||||||
|
|
||||||
mapToObject (map) {
|
mapToObject(map) {
|
||||||
return Array.from(map).reduce((obj, [k, value]) => {
|
return Array.from(map).reduce((obj, [k, value]) => {
|
||||||
obj[k] = value;
|
obj[k] = value;
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSelected () {
|
renderSelected() {
|
||||||
if (this.props.multiple) {
|
if (this.props.multiple) {
|
||||||
const selectedItems = [...this.values()].map(([key, value]) => {
|
const selectedItems = [...this.values()].map(([key, value]) => (
|
||||||
return (
|
|
||||||
<Chip
|
<Chip
|
||||||
key={key}
|
key={key}
|
||||||
className={this.props.theme.value}
|
className={this.props.theme.value}
|
||||||
|
@ -344,17 +342,16 @@ const factory = (Chip, Input) => {
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
</Chip>
|
</Chip>
|
||||||
);
|
));
|
||||||
});
|
|
||||||
|
|
||||||
return <ul className={this.props.theme.values}>{selectedItems}</ul>;
|
return <ul className={this.props.theme.values}>{selectedItems}</ul>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSuggestions () {
|
renderSuggestions() {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
const suggestions = [...this.suggestions()].map(([key, value]) => {
|
const suggestions = [...this.suggestions()].map(([key, value]) => {
|
||||||
const className = classnames(theme.suggestion, {[theme.active]: this.state.active === key});
|
const className = classnames(theme.suggestion, { [theme.active]: this.state.active === key });
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
id={key}
|
id={key}
|
||||||
|
@ -370,30 +367,30 @@ const factory = (Chip, Input) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul
|
<ul
|
||||||
className={classnames(theme.suggestions, {[theme.up]: this.state.direction === 'up'})}
|
className={classnames(theme.suggestions, { [theme.up]: this.state.direction === 'up' })}
|
||||||
ref={node => { this.suggestionsNode = node; }}
|
ref={(node) => { this.suggestionsNode = node; }}
|
||||||
>
|
>
|
||||||
{suggestions}
|
{suggestions}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {
|
const {
|
||||||
allowCreate, error, label, source, suggestionMatch, query, //eslint-disable-line no-unused-vars
|
allowCreate, error, label, source, suggestionMatch, query, // eslint-disable-line no-unused-vars
|
||||||
selectedPosition, keepFocusOnChange, showSuggestionsWhenValueIsSet, showSelectedWhenNotInSource, onQueryChange, //eslint-disable-line no-unused-vars
|
selectedPosition, keepFocusOnChange, showSuggestionsWhenValueIsSet, showSelectedWhenNotInSource, onQueryChange, // eslint-disable-line no-unused-vars
|
||||||
theme, ...other
|
theme, ...other
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const className = classnames(theme.autocomplete, {
|
const className = classnames(theme.autocomplete, {
|
||||||
[theme.focus]: this.state.focus
|
[theme.focus]: this.state.focus,
|
||||||
}, this.props.className);
|
}, this.props.className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox='autocomplete' className={className}>
|
<div data-react-toolbox="autocomplete" className={className}>
|
||||||
{this.props.selectedPosition === 'above' ? this.renderSelected() : null}
|
{this.props.selectedPosition === 'above' ? this.renderSelected() : null}
|
||||||
<Input
|
<Input
|
||||||
{...other}
|
{...other}
|
||||||
ref={node => { this.inputNode = node; }}
|
ref={(node) => { this.inputNode = node; }}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className={theme.input}
|
className={theme.input}
|
||||||
error={error}
|
error={error}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { AUTOCOMPLETE } from '../identifiers.js';
|
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { autocompleteFactory } from './Autocomplete.js';
|
import { AUTOCOMPLETE } from '../identifiers';
|
||||||
import Chip from '../chip';
|
import { autocompleteFactory } from './Autocomplete';
|
||||||
import Input from '../input';
|
import { Chip } from '../chip';
|
||||||
|
import { Input } from '../input';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const Autocomplete = autocompleteFactory(Chip, Input);
|
const Autocomplete = autocompleteFactory(Chip, Input);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { AVATAR } from '../identifiers.js';
|
import { AVATAR } from '../identifiers';
|
||||||
import InjectFontIcon from '../font_icon/FontIcon.js';
|
import InjectFontIcon from '../font_icon/FontIcon';
|
||||||
|
|
||||||
const factory = (FontIcon) => {
|
const factory = (FontIcon) => {
|
||||||
const Avatar = ({alt, children, className, cover, icon, image, theme, title, ...other}) => (
|
const Avatar = ({ alt, children, className, cover, icon, image, theme, title, ...other }) => (
|
||||||
<div data-react-toolbox='avatar' className={classnames(theme.avatar, className)} {...other}>
|
<div data-react-toolbox="avatar" className={classnames(theme.avatar, className)} {...other}>
|
||||||
{children}
|
{children}
|
||||||
{cover && typeof image === 'string' && <span aria-label={alt} className={theme.image} style={{backgroundImage: `url(${image})`}} />}
|
{cover && typeof image === 'string' && <span aria-label={alt} className={theme.image} style={{ backgroundImage: `url(${image})` }} />}
|
||||||
{!cover && (typeof image === 'string' ? <img alt={alt} className={theme.image} src={image} /> : image)}
|
{!cover && (typeof image === 'string' ? <img alt={alt} className={theme.image} src={image} /> : image)}
|
||||||
{typeof icon === 'string' ? <FontIcon className={theme.letter} value={icon} alt={alt} /> : icon}
|
{typeof icon === 'string' ? <FontIcon className={theme.letter} value={icon} alt={alt} /> : icon}
|
||||||
{title ? <span className={theme.letter}>{title[0]}</span> : null}
|
{title ? <span className={theme.letter}>{title[0]}</span> : null}
|
||||||
|
@ -25,14 +25,14 @@ const factory = (FontIcon) => {
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
avatar: PropTypes.string,
|
avatar: PropTypes.string,
|
||||||
image: PropTypes.string,
|
image: PropTypes.string,
|
||||||
letter: PropTypes.string
|
letter: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
title: PropTypes.string
|
title: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
Avatar.defaultProps = {
|
Avatar.defaultProps = {
|
||||||
alt: '',
|
alt: '',
|
||||||
cover: false
|
cover: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Avatar;
|
return Avatar;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { AVATAR } from '../identifiers.js';
|
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { avatarFactory } from './Avatar.js';
|
import { AVATAR } from '../identifiers';
|
||||||
import FontIcon from '../font_icon/FontIcon.js';
|
import { avatarFactory } from './Avatar';
|
||||||
|
import { FontIcon } from '../font_icon/FontIcon';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const Avatar = avatarFactory(FontIcon);
|
const Avatar = avatarFactory(FontIcon);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { BUTTON } from '../identifiers.js';
|
import { BUTTON } from '../identifiers';
|
||||||
import InjectFontIcon from '../font_icon/FontIcon.js';
|
import InjectFontIcon from '../font_icon/FontIcon';
|
||||||
import rippleFactory from '../ripple/Ripple.js';
|
import rippleFactory from '../ripple/Ripple';
|
||||||
|
|
||||||
const factory = (ripple, FontIcon) => {
|
const factory = (ripple, FontIcon) => {
|
||||||
class SimpleBrowseButton extends Component {
|
class SimpleBrowseButton extends Component {
|
||||||
|
@ -16,7 +16,7 @@ const factory = (ripple, FontIcon) => {
|
||||||
floating: PropTypes.bool,
|
floating: PropTypes.bool,
|
||||||
icon: PropTypes.oneOfType([
|
icon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
inverse: PropTypes.bool,
|
inverse: PropTypes.bool,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
|
@ -39,9 +39,9 @@ const factory = (ripple, FontIcon) => {
|
||||||
primary: PropTypes.string,
|
primary: PropTypes.string,
|
||||||
raised: PropTypes.string,
|
raised: PropTypes.string,
|
||||||
rippleWrapper: PropTypes.string,
|
rippleWrapper: PropTypes.string,
|
||||||
toggle: PropTypes.string
|
toggle: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
type: PropTypes.string
|
type: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -52,16 +52,28 @@ const factory = (ripple, FontIcon) => {
|
||||||
mini: false,
|
mini: false,
|
||||||
neutral: true,
|
neutral: true,
|
||||||
primary: false,
|
primary: false,
|
||||||
raised: false
|
raised: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getLevel = () => {
|
||||||
|
if (this.props.primary) return 'primary';
|
||||||
|
if (this.props.accent) return 'accent';
|
||||||
|
return 'neutral';
|
||||||
|
}
|
||||||
|
|
||||||
|
getShape = () => {
|
||||||
|
if (this.props.raised) return 'raised';
|
||||||
|
if (this.props.floating) return 'floating';
|
||||||
|
return 'flat';
|
||||||
|
}
|
||||||
|
|
||||||
handleMouseUp = (event) => {
|
handleMouseUp = (event) => {
|
||||||
this.refs.label.blur();
|
this.labelNode.blur();
|
||||||
if (this.props.onMouseUp) this.props.onMouseUp(event);
|
if (this.props.onMouseUp) this.props.onMouseUp(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleMouseLeave = (event) => {
|
handleMouseLeave = (event) => {
|
||||||
this.refs.label.blur();
|
this.labelNode.blur();
|
||||||
if (this.props.onMouseLeave) this.props.onMouseLeave(event);
|
if (this.props.onMouseLeave) this.props.onMouseLeave(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,34 +81,48 @@ const factory = (ripple, FontIcon) => {
|
||||||
if (this.props.onChange) this.props.onChange(event);
|
if (this.props.onChange) this.props.onChange(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { accent, children, className, flat, floating, icon,
|
const {
|
||||||
inverse, label, mini, neutral, primary, theme, raised, ...others} = this.props;
|
accent, // eslint-disable-line
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
flat, // eslint-disable-line
|
||||||
|
floating, // eslint-disable-line
|
||||||
|
icon,
|
||||||
|
inverse,
|
||||||
|
label,
|
||||||
|
mini,
|
||||||
|
neutral,
|
||||||
|
primary, // eslint-disable-line
|
||||||
|
raised, // eslint-disable-line
|
||||||
|
theme,
|
||||||
|
...others
|
||||||
|
} = this.props;
|
||||||
const element = 'label';
|
const element = 'label';
|
||||||
const level = primary ? 'primary' : accent ? 'accent' : 'neutral';
|
const level = this.getLevel();
|
||||||
const shape = flat ? 'flat' : raised ? 'raised' : floating ? 'floating' : 'flat';
|
const shape = this.getShape();
|
||||||
|
|
||||||
const classes = classnames(theme.button, [theme[shape]], {
|
const classes = classnames(theme.button, [theme[shape]], {
|
||||||
[theme[level]]: neutral,
|
[theme[level]]: neutral,
|
||||||
[theme.mini]: mini,
|
[theme.mini]: mini,
|
||||||
[theme.inverse]: inverse
|
[theme.inverse]: inverse,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
...others,
|
...others,
|
||||||
ref: 'label',
|
ref: (node) => { this.labelNode = node; },
|
||||||
className: classes,
|
className: classes,
|
||||||
disabled: this.props.disabled,
|
disabled: this.props.disabled,
|
||||||
onMouseUp: this.handleMouseUp,
|
onMouseUp: this.handleMouseUp,
|
||||||
onMouseLeave: this.handleMouseLeave,
|
onMouseLeave: this.handleMouseLeave,
|
||||||
'data-react-toolbox': 'label'
|
'data-react-toolbox': 'label',
|
||||||
};
|
};
|
||||||
|
|
||||||
return React.createElement(element, props,
|
return React.createElement(element, props,
|
||||||
icon ? <FontIcon className={theme.icon} value={icon}/> : null,
|
icon ? <FontIcon className={theme.icon} value={icon} /> : null,
|
||||||
<span>{label}</span>,
|
<span>{label}</span>,
|
||||||
<input className={classes} type="file" onChange={this.handleFileChange}/>,
|
<input className={classes} type="file" onChange={this.handleFileChange} />,
|
||||||
children
|
children,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { BUTTON } from '../identifiers.js';
|
import { BUTTON } from '../identifiers';
|
||||||
import InjectFontIcon from '../font_icon/FontIcon.js';
|
import InjectFontIcon from '../font_icon/FontIcon';
|
||||||
import rippleFactory from '../ripple/Ripple.js';
|
import rippleFactory from '../ripple/Ripple';
|
||||||
|
|
||||||
const factory = (ripple, FontIcon) => {
|
const factory = (ripple, FontIcon) => {
|
||||||
class Button extends Component {
|
class Button extends Component {
|
||||||
|
@ -17,7 +17,7 @@ const factory = (ripple, FontIcon) => {
|
||||||
href: PropTypes.string,
|
href: PropTypes.string,
|
||||||
icon: PropTypes.oneOfType([
|
icon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
inverse: PropTypes.bool,
|
inverse: PropTypes.bool,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
|
@ -39,9 +39,9 @@ const factory = (ripple, FontIcon) => {
|
||||||
primary: PropTypes.string,
|
primary: PropTypes.string,
|
||||||
raised: PropTypes.string,
|
raised: PropTypes.string,
|
||||||
rippleWrapper: PropTypes.string,
|
rippleWrapper: PropTypes.string,
|
||||||
toggle: PropTypes.string
|
toggle: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
type: PropTypes.string
|
type: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -53,48 +53,76 @@ const factory = (ripple, FontIcon) => {
|
||||||
neutral: true,
|
neutral: true,
|
||||||
primary: false,
|
primary: false,
|
||||||
raised: false,
|
raised: false,
|
||||||
type: 'button'
|
type: 'button',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getLevel = () => {
|
||||||
|
if (this.props.primary) return 'primary';
|
||||||
|
if (this.props.accent) return 'accent';
|
||||||
|
return 'neutral';
|
||||||
|
}
|
||||||
|
|
||||||
|
getShape = () => {
|
||||||
|
if (this.props.raised) return 'raised';
|
||||||
|
if (this.props.floating) return 'floating';
|
||||||
|
return 'flat';
|
||||||
|
}
|
||||||
|
|
||||||
handleMouseUp = (event) => {
|
handleMouseUp = (event) => {
|
||||||
this.refs.button.blur();
|
this.buttonNode.blur();
|
||||||
if (this.props.onMouseUp) this.props.onMouseUp(event);
|
if (this.props.onMouseUp) this.props.onMouseUp(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleMouseLeave = (event) => {
|
handleMouseLeave = (event) => {
|
||||||
this.refs.button.blur();
|
this.buttonNode.blur();
|
||||||
if (this.props.onMouseLeave) this.props.onMouseLeave(event);
|
if (this.props.onMouseLeave) this.props.onMouseLeave(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { accent, children, className, flat, floating, href, icon,
|
const {
|
||||||
inverse, label, mini, neutral, primary, theme, type, raised, ...others} = this.props;
|
accent, // eslint-disable-line
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
flat, // eslint-disable-line
|
||||||
|
floating, // eslint-disable-line
|
||||||
|
href,
|
||||||
|
icon,
|
||||||
|
inverse,
|
||||||
|
label,
|
||||||
|
mini,
|
||||||
|
neutral,
|
||||||
|
primary, // eslint-disable-line
|
||||||
|
raised, // eslint-disable-line
|
||||||
|
theme,
|
||||||
|
type,
|
||||||
|
...others
|
||||||
|
} = this.props;
|
||||||
const element = href ? 'a' : 'button';
|
const element = href ? 'a' : 'button';
|
||||||
const level = primary ? 'primary' : accent ? 'accent' : 'neutral';
|
const level = this.getLevel();
|
||||||
const shape = flat ? 'flat' : raised ? 'raised' : floating ? 'floating' : 'flat';
|
const shape = this.getShape();
|
||||||
|
|
||||||
const classes = classnames(theme.button, [theme[shape]], {
|
const classes = classnames(theme.button, [theme[shape]], {
|
||||||
[theme[level]]: neutral,
|
[theme[level]]: neutral,
|
||||||
[theme.mini]: mini,
|
[theme.mini]: mini,
|
||||||
[theme.inverse]: inverse
|
[theme.inverse]: inverse,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
...others,
|
...others,
|
||||||
href,
|
href,
|
||||||
ref: 'button',
|
ref: (node) => { this.buttonNode = node; },
|
||||||
className: classes,
|
className: classes,
|
||||||
disabled: this.props.disabled,
|
disabled: this.props.disabled,
|
||||||
onMouseUp: this.handleMouseUp,
|
onMouseUp: this.handleMouseUp,
|
||||||
onMouseLeave: this.handleMouseLeave,
|
onMouseLeave: this.handleMouseLeave,
|
||||||
type: !href ? type : null,
|
type: !href ? type : null,
|
||||||
'data-react-toolbox': 'button'
|
'data-react-toolbox': 'button',
|
||||||
};
|
};
|
||||||
|
|
||||||
return React.createElement(element, props,
|
return React.createElement(element, props,
|
||||||
icon ? <FontIcon className={theme.icon} value={icon}/> : null,
|
icon ? <FontIcon className={theme.icon} value={icon} /> : null,
|
||||||
label,
|
label,
|
||||||
children
|
children,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { BUTTON } from '../identifiers.js';
|
import { BUTTON } from '../identifiers';
|
||||||
import InjectFontIcon from '../font_icon/FontIcon.js';
|
import InjectFontIcon from '../font_icon/FontIcon';
|
||||||
import rippleFactory from '../ripple/Ripple.js';
|
import rippleFactory from '../ripple/Ripple';
|
||||||
|
|
||||||
const factory = (ripple, FontIcon) => {
|
const factory = (ripple, FontIcon) => {
|
||||||
class IconButton extends Component {
|
class IconButton extends Component {
|
||||||
|
@ -15,15 +15,28 @@ const factory = (ripple, FontIcon) => {
|
||||||
href: PropTypes.string,
|
href: PropTypes.string,
|
||||||
icon: PropTypes.oneOfType([
|
icon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
inverse: PropTypes.bool,
|
inverse: PropTypes.bool,
|
||||||
neutral: PropTypes.bool,
|
neutral: PropTypes.bool,
|
||||||
onMouseLeave: PropTypes.func,
|
onMouseLeave: PropTypes.func,
|
||||||
onMouseUp: PropTypes.func,
|
onMouseUp: PropTypes.func,
|
||||||
primary: PropTypes.bool,
|
primary: PropTypes.bool,
|
||||||
theme: PropTypes.object,
|
theme: PropTypes.shape({
|
||||||
type: PropTypes.string
|
accent: PropTypes.string,
|
||||||
|
button: PropTypes.string,
|
||||||
|
flat: PropTypes.string,
|
||||||
|
floating: PropTypes.string,
|
||||||
|
icon: PropTypes.string,
|
||||||
|
inverse: PropTypes.string,
|
||||||
|
mini: PropTypes.string,
|
||||||
|
neutral: PropTypes.string,
|
||||||
|
primary: PropTypes.string,
|
||||||
|
raised: PropTypes.string,
|
||||||
|
rippleWrapper: PropTypes.string,
|
||||||
|
toggle: PropTypes.string,
|
||||||
|
}),
|
||||||
|
type: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -31,44 +44,65 @@ const factory = (ripple, FontIcon) => {
|
||||||
className: '',
|
className: '',
|
||||||
neutral: true,
|
neutral: true,
|
||||||
primary: false,
|
primary: false,
|
||||||
type: 'button'
|
type: 'button',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getLevel = () => {
|
||||||
|
if (this.props.primary) return 'primary';
|
||||||
|
if (this.props.accent) return 'accent';
|
||||||
|
return 'neutral';
|
||||||
|
}
|
||||||
|
|
||||||
handleMouseUp = (event) => {
|
handleMouseUp = (event) => {
|
||||||
this.refs.button.blur();
|
this.buttonNode.blur();
|
||||||
if (this.props.onMouseUp) this.props.onMouseUp(event);
|
if (this.props.onMouseUp) this.props.onMouseUp(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleMouseLeave = (event) => {
|
handleMouseLeave = (event) => {
|
||||||
this.refs.button.blur();
|
this.buttonNode.blur();
|
||||||
if (this.props.onMouseLeave) this.props.onMouseLeave(event);
|
if (this.props.onMouseLeave) this.props.onMouseLeave(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {accent, children, className, href, icon, inverse, neutral,
|
const {
|
||||||
primary, theme, type, ...others} = this.props;
|
accent, // eslint-disable-line
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
href,
|
||||||
|
icon,
|
||||||
|
inverse,
|
||||||
|
neutral,
|
||||||
|
primary, // eslint-disable-line
|
||||||
|
theme,
|
||||||
|
type,
|
||||||
|
...others
|
||||||
|
} = this.props;
|
||||||
const element = href ? 'a' : 'button';
|
const element = href ? 'a' : 'button';
|
||||||
const level = primary ? 'primary' : accent ? 'accent' : 'neutral';
|
const level = this.getLevel();
|
||||||
const classes = classnames([theme.toggle], {
|
const classes = classnames([theme.toggle], {
|
||||||
[theme[level]]: neutral,
|
[theme[level]]: neutral,
|
||||||
[theme.inverse]: inverse
|
[theme.inverse]: inverse,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
...others,
|
...others,
|
||||||
href,
|
href,
|
||||||
ref: 'button',
|
ref: (node) => { this.buttonNode = node; },
|
||||||
className: classes,
|
className: classes,
|
||||||
disabled: this.props.disabled,
|
disabled: this.props.disabled,
|
||||||
onMouseUp: this.handleMouseUp,
|
onMouseUp: this.handleMouseUp,
|
||||||
onMouseLeave: this.handleMouseLeave,
|
onMouseLeave: this.handleMouseLeave,
|
||||||
type: !href ? type : null,
|
type: !href ? type : null,
|
||||||
'data-react-toolbox': 'button'
|
'data-react-toolbox': 'button',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const iconElement = typeof icon === 'string'
|
||||||
|
? <FontIcon className={theme.icon} value={icon} />
|
||||||
|
: icon;
|
||||||
|
|
||||||
return React.createElement(element, props,
|
return React.createElement(element, props,
|
||||||
icon ? typeof icon === 'string' ? <FontIcon className={theme.icon} value={icon} /> : icon : null,
|
icon && iconElement,
|
||||||
children
|
children,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +110,7 @@ const factory = (ripple, FontIcon) => {
|
||||||
return ripple(IconButton);
|
return ripple(IconButton);
|
||||||
};
|
};
|
||||||
|
|
||||||
const IconButton = factory(rippleFactory({centered: true}), InjectFontIcon);
|
const IconButton = factory(rippleFactory({ centered: true }), InjectFontIcon);
|
||||||
export default themr(BUTTON)(IconButton);
|
export default themr(BUTTON)(IconButton);
|
||||||
export { factory as iconButtonFactory };
|
export { factory as iconButtonFactory };
|
||||||
export { IconButton };
|
export { IconButton };
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable */
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
@ -10,23 +11,23 @@ const getRenderedClassName = (tree, Component) => {
|
||||||
return ReactDOM.findDOMNode(rendered).getAttribute('class');
|
return ReactDOM.findDOMNode(rendered).getAttribute('class');
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Button', function () {
|
describe('Button', () => {
|
||||||
describe('#render', function () {
|
describe('#render', () => {
|
||||||
it('uses flat and neutral styles by default', function () {
|
it('uses flat and neutral styles by default', () => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Button theme={theme} />);
|
const tree = TestUtils.renderIntoDocument(<Button theme={theme} />);
|
||||||
const className = getRenderedClassName(tree, RawButton);
|
const className = getRenderedClassName(tree, RawButton);
|
||||||
expect(className).toContain(theme.flat);
|
expect(className).toContain(theme.flat);
|
||||||
expect(className).toContain(theme.neutral);
|
expect(className).toContain(theme.neutral);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders accent button with accent style', function () {
|
it('renders accent button with accent style', () => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Button accent theme={theme} />);
|
const tree = TestUtils.renderIntoDocument(<Button accent theme={theme} />);
|
||||||
const className = getRenderedClassName(tree, RawButton);
|
const className = getRenderedClassName(tree, RawButton);
|
||||||
expect(className).toContain(theme.flat);
|
expect(className).toContain(theme.flat);
|
||||||
expect(className).toContain(theme.accent);
|
expect(className).toContain(theme.accent);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders mini button with mini style', function () {
|
it('renders mini button with mini style', () => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Button floating mini theme={theme} />);
|
const tree = TestUtils.renderIntoDocument(<Button floating mini theme={theme} />);
|
||||||
const className = getRenderedClassName(tree, RawButton);
|
const className = getRenderedClassName(tree, RawButton);
|
||||||
expect(className).toContain(theme.floating);
|
expect(className).toContain(theme.floating);
|
||||||
|
@ -34,7 +35,7 @@ describe('Button', function () {
|
||||||
expect(className).toContain(theme.mini);
|
expect(className).toContain(theme.mini);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders mini accented button with both styles', function () {
|
it('renders mini accented button with both styles', () => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Button accent mini theme={theme} />);
|
const tree = TestUtils.renderIntoDocument(<Button accent mini theme={theme} />);
|
||||||
const className = getRenderedClassName(tree, RawButton);
|
const className = getRenderedClassName(tree, RawButton);
|
||||||
expect(className).toContain(theme.flat);
|
expect(className).toContain(theme.flat);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { BUTTON } from '../identifiers.js';
|
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { buttonFactory } from './Button.js';
|
import { BUTTON } from '../identifiers';
|
||||||
import { browseButtonFactory } from './BrowseButton.js';
|
import { buttonFactory } from './Button';
|
||||||
import { iconButtonFactory } from './IconButton.js';
|
import { browseButtonFactory } from './BrowseButton';
|
||||||
import FontIcon from '../font_icon/FontIcon.js';
|
import { iconButtonFactory } from './IconButton';
|
||||||
|
import { FontIcon } from '../font_icon/FontIcon';
|
||||||
import themedRippleFactory from '../ripple';
|
import themedRippleFactory from '../ripple';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const Button = buttonFactory(themedRippleFactory({ centered: false }), FontIcon);
|
const Button = buttonFactory(themedRippleFactory({ centered: false }), FontIcon);
|
||||||
const IconButton = iconButtonFactory(themedRippleFactory({centered: true}), FontIcon);
|
const IconButton = iconButtonFactory(themedRippleFactory({ centered: true }), FontIcon);
|
||||||
const BrowseButton = browseButtonFactory(themedRippleFactory({ centered: false }), FontIcon);
|
const BrowseButton = browseButtonFactory(themedRippleFactory({ centered: false }), FontIcon);
|
||||||
const ThemedButton = themr(BUTTON, theme)(Button);
|
const ThemedButton = themr(BUTTON, theme)(Button);
|
||||||
const ThemedIconButton = themr(BUTTON, theme)(IconButton);
|
const ThemedIconButton = themr(BUTTON, theme)(IconButton);
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { CARD } from '../identifiers.js';
|
import { CARD } from '../identifiers';
|
||||||
|
|
||||||
const Card = ({children, className, raised, theme, ...other}) => {
|
const Card = ({ children, className, raised, theme, ...other }) => {
|
||||||
const classes = classnames(theme.card, {
|
const classes = classnames(theme.card, {
|
||||||
[theme.raised]: raised
|
[theme.raised]: raised,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox='card' className={classes} {...other}>
|
<div data-react-toolbox="card" className={classes} {...other}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Card.propTypes = {
|
Card.propTypes = {
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
raised: PropTypes.bool,
|
raised: PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
card: PropTypes.string,
|
card: PropTypes.string,
|
||||||
raised: PropTypes.string
|
raised: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themr(CARD)(Card);
|
export default themr(CARD)(Card);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { CARD } from '../identifiers.js';
|
import { CARD } from '../identifiers';
|
||||||
|
|
||||||
const CardActions = ({ children, className, theme, ...other }) => (
|
const CardActions = ({ children, className, theme, ...other }) => (
|
||||||
<div className={classnames(theme.cardActions, className)} {...other}>
|
<div className={classnames(theme.cardActions, className)} {...other}>
|
||||||
|
@ -10,11 +10,11 @@ const CardActions = ({ children, className, theme, ...other }) => (
|
||||||
);
|
);
|
||||||
|
|
||||||
CardActions.propTypes = {
|
CardActions.propTypes = {
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
cardActions: PropTypes.string
|
cardActions: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themr(CARD)(CardActions);
|
export default themr(CARD)(CardActions);
|
||||||
|
|
|
@ -1,20 +1,29 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { CARD } from '../identifiers.js';
|
import { CARD } from '../identifiers';
|
||||||
|
|
||||||
const CardMedia = ({ aspectRatio, children, className, color, contentOverlay, image, theme, ...other }) => {
|
const CardMedia = ({
|
||||||
|
aspectRatio,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
color,
|
||||||
|
contentOverlay,
|
||||||
|
image,
|
||||||
|
theme,
|
||||||
|
...other
|
||||||
|
}) => {
|
||||||
const classes = classnames(theme.cardMedia, {
|
const classes = classnames(theme.cardMedia, {
|
||||||
[theme[aspectRatio]]: aspectRatio
|
[theme[aspectRatio]]: aspectRatio,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
const innerClasses = classnames(theme.content, {
|
const innerClasses = classnames(theme.content, {
|
||||||
[theme.contentOverlay]: contentOverlay
|
[theme.contentOverlay]: contentOverlay,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bgStyle = {
|
const bgStyle = {
|
||||||
backgroundColor: color ? color : undefined,
|
backgroundColor: color || undefined,
|
||||||
backgroundImage: typeof image === 'string' ? `url('${image}')` : undefined
|
backgroundImage: typeof image === 'string' ? `url('${image}')` : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -27,22 +36,22 @@ const CardMedia = ({ aspectRatio, children, className, color, contentOverlay, im
|
||||||
};
|
};
|
||||||
|
|
||||||
CardMedia.propTypes = {
|
CardMedia.propTypes = {
|
||||||
aspectRatio: PropTypes.oneOf([ 'wide', 'square' ]),
|
aspectRatio: PropTypes.oneOf(['wide', 'square']),
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
color: PropTypes.string,
|
color: PropTypes.string,
|
||||||
contentOverlay: PropTypes.bool,
|
contentOverlay: PropTypes.bool,
|
||||||
image: PropTypes.oneOfType([
|
image: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
cardMedia: PropTypes.string,
|
cardMedia: PropTypes.string,
|
||||||
content: PropTypes.string,
|
content: PropTypes.string,
|
||||||
contentOverlay: PropTypes.string,
|
contentOverlay: PropTypes.string,
|
||||||
square: PropTypes.string,
|
square: PropTypes.string,
|
||||||
wide: PropTypes.string
|
wide: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themr(CARD)(CardMedia);
|
export default themr(CARD)(CardMedia);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { CARD } from '../identifiers.js';
|
import { CARD } from '../identifiers';
|
||||||
|
|
||||||
const CardText = ({ children, className, theme, ...other }) => (
|
const CardText = ({ children, className, theme, ...other }) => (
|
||||||
<div className={classnames(theme.cardText, className)} {...other}>
|
<div className={classnames(theme.cardText, className)} {...other}>
|
||||||
|
@ -10,11 +10,11 @@ const CardText = ({ children, className, theme, ...other }) => (
|
||||||
);
|
);
|
||||||
|
|
||||||
CardText.propTypes = {
|
CardText.propTypes = {
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
cardText: PropTypes.string
|
cardText: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themr(CARD)(CardText);
|
export default themr(CARD)(CardText);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { CARD } from '../identifiers.js';
|
import { CARD } from '../identifiers';
|
||||||
import InjectAvatar from '../avatar/Avatar.js';
|
import InjectAvatar from '../avatar/Avatar';
|
||||||
|
|
||||||
const factory = (Avatar) => {
|
const factory = (Avatar) => {
|
||||||
const CardTitle = ({avatar, children, className, subtitle, theme, title, ...other}) => {
|
const CardTitle = ({ avatar, children, className, subtitle, theme, title, ...other }) => {
|
||||||
const classes = classnames(theme.cardTitle, {
|
const classes = classnames(theme.cardTitle, {
|
||||||
[theme.small]: avatar,
|
[theme.small]: avatar,
|
||||||
[theme.large]: !avatar
|
[theme.large]: !avatar,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -29,28 +29,28 @@ const factory = (Avatar) => {
|
||||||
CardTitle.propTypes = {
|
CardTitle.propTypes = {
|
||||||
avatar: PropTypes.oneOfType([
|
avatar: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
children: PropTypes.oneOfType([
|
children: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element,
|
PropTypes.element,
|
||||||
PropTypes.array
|
PropTypes.array,
|
||||||
]),
|
]),
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
subtitle: PropTypes.oneOfType([
|
subtitle: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
large: PropTypes.string,
|
large: PropTypes.string,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
small: PropTypes.string,
|
small: PropTypes.string,
|
||||||
subtitle: PropTypes.string
|
subtitle: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
title: PropTypes.oneOfType([
|
title: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
])
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
return CardTitle;
|
return CardTitle;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { CARD } from '../identifiers.js';
|
import { CARD } from '../identifiers';
|
||||||
import { Card } from './Card.js';
|
import { Card } from './Card';
|
||||||
import { CardActions } from './CardActions.js';
|
import { CardActions } from './CardActions';
|
||||||
import { CardMedia } from './CardMedia.js';
|
import { CardMedia } from './CardMedia';
|
||||||
import { CardText } from './CardText.js';
|
import { CardText } from './CardText';
|
||||||
import { cardTitleFactory } from './CardTitle.js';
|
import { cardTitleFactory } from './CardTitle';
|
||||||
import Avatar from '../avatar';
|
import { Avatar } from '../avatar';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const CardTitle = cardTitleFactory(Avatar);
|
const CardTitle = cardTitleFactory(Avatar);
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import styleShape from 'react-style-proptype';
|
||||||
|
|
||||||
const factory = (ripple) => {
|
const factory = (ripple) => {
|
||||||
const Check = ({checked, children, onMouseDown, theme, style}) => (
|
const Check = ({ checked, children, onMouseDown, theme, style }) => (
|
||||||
<div
|
<div
|
||||||
data-react-toolbox='check'
|
data-react-toolbox="check"
|
||||||
className={classnames(theme.check, { [theme.checked]: checked })}
|
className={classnames(theme.check, { [theme.checked]: checked })}
|
||||||
onMouseDown={onMouseDown}
|
onMouseDown={onMouseDown}
|
||||||
style={style}
|
style={style}
|
||||||
|
@ -15,13 +16,13 @@ const factory = (ripple) => {
|
||||||
|
|
||||||
Check.propTypes = {
|
Check.propTypes = {
|
||||||
checked: PropTypes.bool,
|
checked: PropTypes.bool,
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
onMouseDown: PropTypes.func,
|
onMouseDown: PropTypes.func,
|
||||||
style: PropTypes.object,
|
style: styleShape,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
check: PropTypes.string,
|
check: PropTypes.string,
|
||||||
checked: PropTypes.string
|
checked: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
return ripple(Check);
|
return ripple(Check);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import styleShape from 'react-style-proptype';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { CHECKBOX } from '../identifiers.js';
|
import { CHECKBOX } from '../identifiers';
|
||||||
import rippleFactory from '../ripple/Ripple.js';
|
import rippleFactory from '../ripple/Ripple';
|
||||||
import checkFactory from './Check.js';
|
import checkFactory from './Check';
|
||||||
|
|
||||||
const factory = (Check) => {
|
const factory = (Check) => {
|
||||||
class Checkbox extends Component {
|
class Checkbox extends Component {
|
||||||
|
@ -14,25 +15,25 @@ const factory = (Check) => {
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
label: PropTypes.oneOfType([
|
label: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.node
|
PropTypes.node,
|
||||||
]),
|
]),
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
onMouseEnter: PropTypes.func,
|
onMouseEnter: PropTypes.func,
|
||||||
onMouseLeave: PropTypes.func,
|
onMouseLeave: PropTypes.func,
|
||||||
style: PropTypes.object,
|
style: styleShape,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
disabled: PropTypes.string,
|
disabled: PropTypes.string,
|
||||||
field: PropTypes.string,
|
field: PropTypes.string,
|
||||||
input: PropTypes.string,
|
input: PropTypes.string,
|
||||||
ripple: PropTypes.string
|
ripple: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
checked: false,
|
checked: false,
|
||||||
className: '',
|
className: '',
|
||||||
disabled: false
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleToggle = (event) => {
|
handleToggle = (event) => {
|
||||||
|
@ -42,24 +43,29 @@ const factory = (Check) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
blur () {
|
blur() {
|
||||||
this.inputNode && this.inputNode.blur();
|
if (this.inputNode) {
|
||||||
|
this.inputNode.blur();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
focus () {
|
focus() {
|
||||||
this.inputNode && this.inputNode.focus();
|
if (this.inputNode) {
|
||||||
|
this.inputNode.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { checked, children, disabled, label, name, style, onChange, // eslint-disable-line
|
const { checked, children, disabled, label, name, style, onChange, // eslint-disable-line
|
||||||
onMouseEnter, onMouseLeave, theme, ...others } = this.props;
|
onMouseEnter, onMouseLeave, theme, ...others } = this.props;
|
||||||
const className = classnames(theme.field, {
|
const className = classnames(theme.field, {
|
||||||
[theme.disabled]: this.props.disabled
|
[theme.disabled]: this.props.disabled,
|
||||||
}, this.props.className);
|
}, this.props.className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
data-react-toolbox='checkbox'
|
data-react-toolbox="checkbox"
|
||||||
|
htmlFor={name}
|
||||||
className={className}
|
className={className}
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
|
@ -72,8 +78,8 @@ const factory = (Check) => {
|
||||||
name={name}
|
name={name}
|
||||||
onChange={() => {}}
|
onChange={() => {}}
|
||||||
onClick={this.handleToggle}
|
onClick={this.handleToggle}
|
||||||
ref={node => { this.inputNode = node; }}
|
ref={(node) => { this.inputNode = node; }}
|
||||||
type='checkbox'
|
type="checkbox"
|
||||||
/>
|
/>
|
||||||
<Check
|
<Check
|
||||||
checked={checked}
|
checked={checked}
|
||||||
|
@ -82,7 +88,7 @@ const factory = (Check) => {
|
||||||
style={style}
|
style={style}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
{label ? <span data-react-toolbox='label' className={theme.text}>{label}</span> : null}
|
{label ? <span data-react-toolbox="label" className={theme.text}>{label}</span> : null}
|
||||||
{children}
|
{children}
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
|
@ -92,7 +98,7 @@ const factory = (Check) => {
|
||||||
return Checkbox;
|
return Checkbox;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Check = checkFactory(rippleFactory({ centered: true, spread: 2.6}));
|
const Check = checkFactory(rippleFactory({ centered: true, spread: 2.6 }));
|
||||||
const Checkbox = factory(Check);
|
const Checkbox = factory(Check);
|
||||||
export default themr(CHECKBOX)(Checkbox);
|
export default themr(CHECKBOX)(Checkbox);
|
||||||
export { factory as checkboxFactory };
|
export { factory as checkboxFactory };
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { CHECKBOX } from '../identifiers.js';
|
import { CHECKBOX } from '../identifiers';
|
||||||
import themedRippleFactory from '../ripple';
|
import themedRippleFactory from '../ripple';
|
||||||
import { checkboxFactory } from './Checkbox.js';
|
import { checkboxFactory } from './Checkbox';
|
||||||
import checkFactory from './Check.js';
|
import checkFactory from './Check';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const ThemedCheck = checkFactory(themedRippleFactory({ centered: true, spread: 2.6}));
|
const ThemedCheck = checkFactory(themedRippleFactory({ centered: true, spread: 2.6 }));
|
||||||
const ThemedCheckbox = themr(CHECKBOX, theme)(checkboxFactory(ThemedCheck));
|
const ThemedCheckbox = themr(CHECKBOX, theme)(checkboxFactory(ThemedCheck));
|
||||||
|
|
||||||
export default ThemedCheckbox;
|
export default ThemedCheckbox;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { CHIP } from '../identifiers.js';
|
import { CHIP } from '../identifiers';
|
||||||
import InjectAvatar from '../avatar/Avatar.js';
|
import InjectAvatar from '../avatar/Avatar';
|
||||||
|
|
||||||
const factory = (Avatar) => {
|
const factory = (Avatar) => {
|
||||||
const Chip = ({children, className, deletable, onDeleteClick, theme, ...other}) => {
|
const Chip = ({ children, className, deletable, onDeleteClick, theme, ...other }) => {
|
||||||
let hasAvatar = false;
|
let hasAvatar = false;
|
||||||
if (React.Children.count(children)) {
|
if (React.Children.count(children)) {
|
||||||
const flatChildren = React.Children.toArray(children);
|
const flatChildren = React.Children.toArray(children);
|
||||||
|
@ -15,11 +15,11 @@ const factory = (Avatar) => {
|
||||||
|
|
||||||
const classes = classnames(theme.chip, {
|
const classes = classnames(theme.chip, {
|
||||||
[theme.deletable]: !!deletable,
|
[theme.deletable]: !!deletable,
|
||||||
[theme.avatar]: !!hasAvatar
|
[theme.avatar]: !!hasAvatar,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox='chip' className={classes} {...other}>
|
<div data-react-toolbox="chip" className={classes} {...other}>
|
||||||
{typeof children === 'string' ? <span>{children}</span> : children}
|
{typeof children === 'string' ? <span>{children}</span> : children}
|
||||||
{
|
{
|
||||||
deletable ? (
|
deletable ? (
|
||||||
|
@ -45,13 +45,13 @@ const factory = (Avatar) => {
|
||||||
deletable: PropTypes.string,
|
deletable: PropTypes.string,
|
||||||
delete: PropTypes.string,
|
delete: PropTypes.string,
|
||||||
deleteIcon: PropTypes.string,
|
deleteIcon: PropTypes.string,
|
||||||
deleteX: PropTypes.string
|
deleteX: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
Chip.defaultProps = {
|
Chip.defaultProps = {
|
||||||
className: '',
|
className: '',
|
||||||
deletable: false
|
deletable: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Chip;
|
return Chip;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable */
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
@ -7,30 +8,30 @@ import { CHIP } from '../../identifiers.js';
|
||||||
import { chipFactory } from '../Chip';
|
import { chipFactory } from '../Chip';
|
||||||
import { tooltipFactory } from '../../tooltip';
|
import { tooltipFactory } from '../../tooltip';
|
||||||
|
|
||||||
const Avatar = ({title}) => <span>{title}</span>; // eslint-disable-line react/prop-types
|
const Avatar = ({ title }) => <span>{title}</span>; // eslint-disable-line react/prop-types
|
||||||
const Chip = themr(CHIP)(chipFactory(Avatar));
|
const Chip = themr(CHIP)(chipFactory(Avatar));
|
||||||
|
|
||||||
describe('Chip', function () {
|
describe('Chip', () => {
|
||||||
describe('with avatar', function () {
|
describe('with avatar', () => {
|
||||||
it('adds the avatar class to the element', function () {
|
it('adds the avatar class to the element', () => {
|
||||||
const tree = ReactTestUtils.renderIntoDocument(
|
const tree = ReactTestUtils.renderIntoDocument(
|
||||||
<Chip theme={{avatar: 'avatar-class'}}>
|
<Chip theme={{ avatar: 'avatar-class' }}>
|
||||||
<Avatar title='Test'/>
|
<Avatar title="Test" />
|
||||||
<span>Test</span>
|
<span>Test</span>
|
||||||
</Chip>
|
</Chip>,
|
||||||
);
|
);
|
||||||
const chip = ReactTestUtils.findRenderedComponentWithType(tree, Chip);
|
const chip = ReactTestUtils.findRenderedComponentWithType(tree, Chip);
|
||||||
const chipNode = ReactDOM.findDOMNode(chip);
|
const chipNode = ReactDOM.findDOMNode(chip);
|
||||||
expect(chipNode.className).toMatch(/\bavatar-class\b/);
|
expect(chipNode.className).toMatch(/\bavatar-class\b/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works with non-flat children', function () {
|
it('works with non-flat children', () => {
|
||||||
const TooltippedChip = tooltipFactory()(Chip);
|
const TooltippedChip = tooltipFactory()(Chip);
|
||||||
const tree = ReactTestUtils.renderIntoDocument(
|
const tree = ReactTestUtils.renderIntoDocument(
|
||||||
<TooltippedChip theme={{avatar: 'avatar-class'}} tooltip='Test tooltip'>
|
<TooltippedChip theme={{ avatar: 'avatar-class' }} tooltip="Test tooltip">
|
||||||
<Avatar title='Test'/>
|
<Avatar title="Test" />
|
||||||
<span>Test</span>
|
<span>Test</span>
|
||||||
</TooltippedChip>
|
</TooltippedChip>,
|
||||||
);
|
);
|
||||||
const chip = ReactTestUtils.findRenderedComponentWithType(tree, Chip);
|
const chip = ReactTestUtils.findRenderedComponentWithType(tree, Chip);
|
||||||
const chipNode = ReactDOM.findDOMNode(chip);
|
const chipNode = ReactDOM.findDOMNode(chip);
|
||||||
|
@ -38,12 +39,12 @@ describe('Chip', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('without avatar', function () {
|
describe('without avatar', () => {
|
||||||
it('does not add avatar class to the element', function () {
|
it('does not add avatar class to the element', () => {
|
||||||
const tree = ReactTestUtils.renderIntoDocument(
|
const tree = ReactTestUtils.renderIntoDocument(
|
||||||
<Chip theme={{avatar: 'avatar-class'}}>
|
<Chip theme={{ avatar: 'avatar-class' }}>
|
||||||
<span>Test</span>
|
<span>Test</span>
|
||||||
</Chip>
|
</Chip>,
|
||||||
);
|
);
|
||||||
const chip = ReactTestUtils.findRenderedComponentWithType(tree, Chip);
|
const chip = ReactTestUtils.findRenderedComponentWithType(tree, Chip);
|
||||||
const chipNode = ReactDOM.findDOMNode(chip);
|
const chipNode = ReactDOM.findDOMNode(chip);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { CHIP } from '../identifiers.js';
|
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { chipFactory } from './Chip.js';
|
import { CHIP } from '../identifiers';
|
||||||
import Avatar from '../avatar';
|
import { chipFactory } from './Chip';
|
||||||
|
import { Avatar } from '../avatar';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const Chip = chipFactory(Avatar);
|
const Chip = chipFactory(Avatar);
|
||||||
|
|
|
@ -1,64 +1,62 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import CssTransitionGroup from 'react-addons-css-transition-group';
|
import CssTransitionGroup from 'react-addons-css-transition-group';
|
||||||
import { range, getAnimationModule } from '../utils/utils';
|
import { range, getAnimationModule } from '../utils/utils';
|
||||||
import time from '../utils/time.js';
|
import time from '../utils/time';
|
||||||
import CalendarMonth from './CalendarMonth.js';
|
import CalendarMonth from './CalendarMonth';
|
||||||
|
|
||||||
const DIRECTION_STEPS = { left: -1, right: 1 };
|
const DIRECTION_STEPS = { left: -1, right: 1 };
|
||||||
|
|
||||||
const factory = (IconButton) => {
|
const factory = (IconButton) => {
|
||||||
class Calendar extends Component {
|
class Calendar extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
disabledDates: React.PropTypes.array,
|
disabledDates: React.PropTypes.arrayOf(PropTypes.instanceOf(Date)),
|
||||||
display: PropTypes.oneOf(['months', 'years']),
|
display: PropTypes.oneOf(['months', 'years']),
|
||||||
enabledDates: React.PropTypes.array,
|
enabledDates: React.PropTypes.arrayOf(PropTypes.instanceOf(Date)),
|
||||||
handleSelect: PropTypes.func,
|
handleSelect: PropTypes.func,
|
||||||
locale: React.PropTypes.oneOfType([
|
locale: React.PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
React.PropTypes.string,
|
||||||
React.PropTypes.object
|
React.PropTypes.object,
|
||||||
]),
|
]),
|
||||||
maxDate: PropTypes.object,
|
maxDate: PropTypes.instanceOf(Date),
|
||||||
minDate: PropTypes.object,
|
minDate: PropTypes.instanceOf(Date),
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
selectedDate: PropTypes.object,
|
selectedDate: PropTypes.instanceOf(Date),
|
||||||
sundayFirstDayOfWeek: React.PropTypes.bool,
|
sundayFirstDayOfWeek: React.PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
active: PropTypes.string,
|
active: PropTypes.string,
|
||||||
calendar: PropTypes.string,
|
calendar: PropTypes.string,
|
||||||
next: PropTypes.string,
|
next: PropTypes.string,
|
||||||
prev: PropTypes.string,
|
prev: PropTypes.string,
|
||||||
years: PropTypes.string
|
years: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
viewDate: PropTypes.object
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
display: 'months',
|
display: 'months',
|
||||||
selectedDate: new Date()
|
selectedDate: new Date(),
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
viewDate: this.props.selectedDate
|
viewDate: this.props.selectedDate,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount() {
|
||||||
document.body.addEventListener('keydown', this.handleKeys);
|
document.body.addEventListener('keydown', this.handleKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate() {
|
||||||
if (this.refs.activeYear) {
|
if (this.activeYearNode) {
|
||||||
this.scrollToActive();
|
this.scrollToActive();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
document.body.removeEventListener('keydown', this.handleKeys);
|
document.body.removeEventListener('keydown', this.handleKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollToActive () {
|
scrollToActive() {
|
||||||
this.refs.years.scrollTop = this.refs.activeYear.offsetTop
|
const offset = (this.yearsNode.offsetHeight / 2) + (this.activeYearNode.offsetHeight / 2);
|
||||||
- this.refs.years.offsetHeight / 2
|
this.yearsNode.scrollTop = this.activeYearNode.offsetTop - offset;
|
||||||
+ this.refs.activeYear.offsetHeight / 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDayClick = (day) => {
|
handleDayClick = (day) => {
|
||||||
|
@ -66,16 +64,18 @@ const factory = (IconButton) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
handleYearClick = (event) => {
|
handleYearClick = (event) => {
|
||||||
const year = parseInt(event.currentTarget.id);
|
const year = parseInt(event.currentTarget.id, 10);
|
||||||
const viewDate = time.setYear(this.props.selectedDate, year);
|
const viewDate = time.setYear(this.props.selectedDate, year);
|
||||||
this.setState({viewDate});
|
this.setState({ viewDate });
|
||||||
this.props.onChange(viewDate, false);
|
this.props.onChange(viewDate, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleKeys = (e) => {
|
handleKeys = (e) => {
|
||||||
const { selectedDate } = this.props;
|
const { selectedDate } = this.props;
|
||||||
|
|
||||||
if (e.which === 37 || e.which === 38 || e.which === 39 || e.which === 40 || e.which === 13) e.preventDefault();
|
if (e.which === 37 || e.which === 38 || e.which === 39 || e.which === 40 || e.which === 13) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
switch (e.which) {
|
switch (e.which) {
|
||||||
case 13: this.props.handleSelect(); break; // enter
|
case 13: this.props.handleSelect(); break; // enter
|
||||||
|
@ -96,35 +96,44 @@ const factory = (IconButton) => {
|
||||||
const direction = event.currentTarget.id;
|
const direction = event.currentTarget.id;
|
||||||
this.setState({
|
this.setState({
|
||||||
direction,
|
direction,
|
||||||
viewDate: time.addMonths(this.state.viewDate, DIRECTION_STEPS[direction])
|
viewDate: time.addMonths(this.state.viewDate, DIRECTION_STEPS[direction]),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
renderYears () {
|
renderYears() {
|
||||||
return (
|
return (
|
||||||
<ul data-react-toolbox='years' ref="years" className={this.props.theme.years}>
|
<ul
|
||||||
|
data-react-toolbox="years"
|
||||||
|
className={this.props.theme.years}
|
||||||
|
ref={(node) => { this.yearsNode = node; }}
|
||||||
|
>
|
||||||
{range(1900, 2100).map(year => (
|
{range(1900, 2100).map(year => (
|
||||||
<li
|
<li
|
||||||
children={year}
|
|
||||||
className={year === this.state.viewDate.getFullYear() ? this.props.theme.active : ''}
|
className={year === this.state.viewDate.getFullYear() ? this.props.theme.active : ''}
|
||||||
id={year}
|
id={year}
|
||||||
key={year}
|
key={year}
|
||||||
onClick={this.handleYearClick}
|
onClick={this.handleYearClick}
|
||||||
ref={year === this.state.viewDate.getFullYear() ? 'activeYear' : undefined}
|
ref={(node) => {
|
||||||
/>
|
if (year === this.state.viewDate.getFullYear()) {
|
||||||
|
this.activeYearNode = node;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{year}
|
||||||
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMonths () {
|
renderMonths() {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
const animation = this.state.direction === 'left' ? 'slideLeft' : 'slideRight';
|
const animation = this.state.direction === 'left' ? 'slideLeft' : 'slideRight';
|
||||||
const animationModule = getAnimationModule(animation, theme);
|
const animationModule = getAnimationModule(animation, theme);
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox='calendar'>
|
<div data-react-toolbox="calendar">
|
||||||
<IconButton id='left' className={theme.prev} icon='chevron_left' onClick={this.changeViewMonth} />
|
<IconButton id="left" className={theme.prev} icon="chevron_left" onClick={this.changeViewMonth} />
|
||||||
<IconButton id='right' className={theme.next} icon='chevron_right' onClick={this.changeViewMonth} />
|
<IconButton id="right" className={theme.next} icon="chevron_right" onClick={this.changeViewMonth} />
|
||||||
<CssTransitionGroup
|
<CssTransitionGroup
|
||||||
transitionName={animationModule}
|
transitionName={animationModule}
|
||||||
transitionEnterTimeout={350}
|
transitionEnterTimeout={350}
|
||||||
|
@ -148,7 +157,7 @@ const factory = (IconButton) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className={this.props.theme.calendar}>
|
<div className={this.props.theme.calendar}>
|
||||||
{this.props.display === 'months' ? this.renderMonths() : this.renderYears()}
|
{this.props.display === 'months' ? this.renderMonths() : this.renderYears()}
|
||||||
|
|
|
@ -1,33 +1,34 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import time from '../utils/time.js';
|
import time from '../utils/time';
|
||||||
|
|
||||||
class Day extends Component {
|
class Day extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
day: PropTypes.number,
|
day: PropTypes.number,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
selectedDate: PropTypes.object,
|
selectedDate: PropTypes.instanceOf(Date),
|
||||||
sundayFirstDayOfWeek: PropTypes.bool,
|
sundayFirstDayOfWeek: PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
active: PropTypes.string,
|
active: PropTypes.string,
|
||||||
day: PropTypes.string,
|
day: PropTypes.string,
|
||||||
disabled: PropTypes.string
|
disabled: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
viewDate: PropTypes.object
|
viewDate: PropTypes.instanceOf(Date),
|
||||||
};
|
};
|
||||||
|
|
||||||
dayStyle () {
|
dayStyle() {
|
||||||
if (this.props.day === 1) {
|
if (this.props.day === 1) {
|
||||||
const e = (this.props.sundayFirstDayOfWeek) ? 0 : 1;
|
const e = (this.props.sundayFirstDayOfWeek) ? 0 : 1;
|
||||||
const firstDay = time.getFirstWeekDay(this.props.viewDate) - e;
|
const firstDay = time.getFirstWeekDay(this.props.viewDate) - e;
|
||||||
return {
|
return {
|
||||||
marginLeft: `${ (firstDay >= 0 ? firstDay : 6) * 100 / 7 }%`
|
marginLeft: `${(firstDay >= 0 ? firstDay : 6) * (100 / 7)}%`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
isSelected () {
|
isSelected() {
|
||||||
const sameYear = this.props.viewDate.getFullYear() === this.props.selectedDate.getFullYear();
|
const sameYear = this.props.viewDate.getFullYear() === this.props.selectedDate.getFullYear();
|
||||||
const sameMonth = this.props.viewDate.getMonth() === this.props.selectedDate.getMonth();
|
const sameMonth = this.props.viewDate.getMonth() === this.props.selectedDate.getMonth();
|
||||||
const sameDay = this.props.day === this.props.selectedDate.getDate();
|
const sameDay = this.props.day === this.props.selectedDate.getDate();
|
||||||
|
@ -40,14 +41,14 @@ class Day extends Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const className = classnames(this.props.theme.day, {
|
const className = classnames(this.props.theme.day, {
|
||||||
[this.props.theme.active]: this.isSelected(),
|
[this.props.theme.active]: this.isSelected(),
|
||||||
[this.props.theme.disabled]: this.props.disabled
|
[this.props.theme.disabled]: this.props.disabled,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox='day' className={className} style={this.dayStyle()}>
|
<div data-react-toolbox="day" className={className} style={this.dayStyle()}>
|
||||||
<span onClick={this.handleClick}>
|
<span onClick={this.handleClick}>
|
||||||
{this.props.day}
|
{this.props.day}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,41 +1,41 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { range } from '../utils/utils';
|
import { range } from '../utils/utils';
|
||||||
import time from '../utils/time.js';
|
import time from '../utils/time';
|
||||||
import CalendarDay from './CalendarDay.js';
|
import CalendarDay from './CalendarDay';
|
||||||
|
|
||||||
class Month extends Component {
|
class Month extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
disabledDates: React.PropTypes.array,
|
disabledDates: React.PropTypes.arrayOf(PropTypes.instanceOf(Date)),
|
||||||
enabledDates: React.PropTypes.array,
|
enabledDates: React.PropTypes.arrayOf(PropTypes.instanceOf(Date)),
|
||||||
locale: React.PropTypes.oneOfType([
|
locale: React.PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
React.PropTypes.string,
|
||||||
React.PropTypes.object
|
React.PropTypes.object,
|
||||||
]),
|
]),
|
||||||
maxDate: PropTypes.object,
|
maxDate: PropTypes.instanceOf(Date),
|
||||||
minDate: PropTypes.object,
|
minDate: PropTypes.instanceOf(Date),
|
||||||
onDayClick: PropTypes.func,
|
onDayClick: PropTypes.func,
|
||||||
selectedDate: PropTypes.object,
|
selectedDate: PropTypes.instanceOf(Date),
|
||||||
sundayFirstDayOfWeek: React.PropTypes.bool,
|
sundayFirstDayOfWeek: React.PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
days: PropTypes.string,
|
days: PropTypes.string,
|
||||||
month: PropTypes.string,
|
month: PropTypes.string,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
week: PropTypes.string
|
week: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
viewDate: PropTypes.object
|
viewDate: PropTypes.instanceOf(Date),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
disabledDates: [],
|
disabledDates: [],
|
||||||
enabledDates: []
|
enabledDates: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDayClick = (day) => {
|
handleDayClick = (day) => {
|
||||||
if (this.props.onDayClick) this.props.onDayClick(day);
|
if (this.props.onDayClick) this.props.onDayClick(day);
|
||||||
};
|
};
|
||||||
|
|
||||||
isDayDisabled (date) {
|
isDayDisabled(date) {
|
||||||
const {minDate, maxDate, enabledDates, disabledDates} = this.props;
|
const { minDate, maxDate, enabledDates, disabledDates } = this.props;
|
||||||
const compareDate = compDate => date.getTime() === compDate.getTime();
|
const compareDate = compDate => date.getTime() === compDate.getTime();
|
||||||
const dateInDisabled = disabledDates.filter(compareDate).length > 0;
|
const dateInDisabled = disabledDates.filter(compareDate).length > 0;
|
||||||
const dateInEnabled = enabledDates.filter(compareDate).length > 0;
|
const dateInEnabled = enabledDates.filter(compareDate).length > 0;
|
||||||
|
@ -44,14 +44,14 @@ class Month extends Component {
|
||||||
|| dateInDisabled;
|
|| dateInDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderWeeks () {
|
renderWeeks() {
|
||||||
const days = range(0, 7).map(d => time.getDayOfWeekLetter(d, this.props.locale));
|
const days = range(0, 7).map(d => time.getDayOfWeekLetter(d, this.props.locale));
|
||||||
const source = (this.props.sundayFirstDayOfWeek) ? days : [...days.slice(1), days[0]];
|
const source = (this.props.sundayFirstDayOfWeek) ? days : [...days.slice(1), days[0]];
|
||||||
return source.map((d, i) => (<span key={i}>{d}</span>));
|
return source.map((day, i) => (<span key={i}>{day}</span>)); // eslint-disable-line
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDays () {
|
renderDays() {
|
||||||
return range(1, time.getDaysInMonth(this.props.viewDate) + 1).map(i => {
|
return range(1, time.getDaysInMonth(this.props.viewDate) + 1).map((i) => {
|
||||||
const date = new Date(this.props.viewDate.getFullYear(), this.props.viewDate.getMonth(), i);
|
const date = new Date(this.props.viewDate.getFullYear(), this.props.viewDate.getMonth(), i);
|
||||||
return (
|
return (
|
||||||
<CalendarDay
|
<CalendarDay
|
||||||
|
@ -68,11 +68,13 @@ class Month extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
|
const fullMonth = time.getFullMonth(this.props.viewDate, this.props.locale);
|
||||||
|
const fullYear = this.props.viewDate.getFullYear();
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox='month' className={this.props.theme.month}>
|
<div data-react-toolbox="month" className={this.props.theme.month}>
|
||||||
<span className={this.props.theme.title}>
|
<span className={this.props.theme.title}>
|
||||||
{time.getFullMonth(this.props.viewDate, this.props.locale)} {this.props.viewDate.getFullYear()}
|
{fullMonth} {fullYear}
|
||||||
</span>
|
</span>
|
||||||
<div className={this.props.theme.week}>{this.renderWeeks()}</div>
|
<div className={this.props.theme.week}>{this.renderWeeks()}</div>
|
||||||
<div className={this.props.theme.days}>{this.renderDays()}</div>
|
<div className={this.props.theme.days}>{this.renderDays()}</div>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { DATE_PICKER } from '../identifiers.js';
|
import { DATE_PICKER } from '../identifiers';
|
||||||
import events from '../utils/events.js';
|
import events from '../utils/events';
|
||||||
import time from '../utils/time.js';
|
import time from '../utils/time';
|
||||||
|
|
||||||
import InjectIconButton from '../button/IconButton.js';
|
import InjectIconButton from '../button/IconButton';
|
||||||
import InjectInput from '../input/Input.js';
|
import InjectInput from '../input/Input';
|
||||||
import InjectDialog from '../dialog/Dialog.js';
|
import InjectDialog from '../dialog/Dialog';
|
||||||
import calendarFactory from './Calendar.js';
|
import calendarFactory from './Calendar';
|
||||||
import datePickerDialogFactory from './DatePickerDialog.js';
|
import datePickerDialogFactory from './DatePickerDialog';
|
||||||
|
|
||||||
const factory = (Input, DatePickerDialog) => {
|
const factory = (Input, DatePickerDialog) => {
|
||||||
class DatePicker extends Component {
|
class DatePicker extends Component {
|
||||||
|
@ -18,22 +18,22 @@ const factory = (Input, DatePickerDialog) => {
|
||||||
autoOk: PropTypes.bool,
|
autoOk: PropTypes.bool,
|
||||||
cancelLabel: PropTypes.string,
|
cancelLabel: PropTypes.string,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
disabledDates: React.PropTypes.array,
|
disabledDates: React.PropTypes.arrayOf(PropTypes.instanceOf(Date)),
|
||||||
enabledDates: React.PropTypes.array,
|
enabledDates: React.PropTypes.arrayOf(PropTypes.instanceOf(Date)),
|
||||||
error: PropTypes.string,
|
error: PropTypes.string,
|
||||||
icon: PropTypes.oneOfType([
|
icon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
inputClassName: PropTypes.string,
|
inputClassName: PropTypes.string,
|
||||||
inputFormat: PropTypes.func,
|
inputFormat: PropTypes.func,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
locale: React.PropTypes.oneOfType([
|
locale: React.PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
React.PropTypes.string,
|
||||||
React.PropTypes.object
|
React.PropTypes.object,
|
||||||
]),
|
]),
|
||||||
maxDate: PropTypes.object,
|
maxDate: PropTypes.instanceOf(Date),
|
||||||
minDate: PropTypes.object,
|
minDate: PropTypes.instanceOf(Date),
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
okLabel: PropTypes.string,
|
okLabel: PropTypes.string,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
|
@ -46,32 +46,32 @@ const factory = (Input, DatePickerDialog) => {
|
||||||
sundayFirstDayOfWeek: React.PropTypes.bool,
|
sundayFirstDayOfWeek: React.PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
container: PropTypes.string,
|
container: PropTypes.string,
|
||||||
input: PropTypes.string
|
input: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
value: PropTypes.oneOfType([
|
value: PropTypes.oneOfType([
|
||||||
PropTypes.instanceOf(Date),
|
PropTypes.instanceOf(Date),
|
||||||
PropTypes.string
|
PropTypes.string,
|
||||||
])
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
active: false,
|
active: false,
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
sundayFirstDayOfWeek: false
|
sundayFirstDayOfWeek: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
active: this.props.active
|
active: this.props.active,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.active !== this.props.active && this.state.active !== nextProps.active) {
|
if (nextProps.active !== this.props.active && this.state.active !== nextProps.active) {
|
||||||
this.setState({ active: nextProps.active });
|
this.setState({ active: nextProps.active });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDismiss = () => {
|
handleDismiss = () => {
|
||||||
this.setState({active: false});
|
this.setState({ active: false });
|
||||||
if (this.props.onDismiss) {
|
if (this.props.onDismiss) {
|
||||||
this.props.onDismiss();
|
this.props.onDismiss();
|
||||||
}
|
}
|
||||||
|
@ -79,34 +79,34 @@ const factory = (Input, DatePickerDialog) => {
|
||||||
|
|
||||||
handleInputFocus = (event) => {
|
handleInputFocus = (event) => {
|
||||||
events.pauseEvent(event);
|
events.pauseEvent(event);
|
||||||
this.setState({active: true});
|
this.setState({ active: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleInputBlur = (event) => {
|
handleInputBlur = (event) => {
|
||||||
events.pauseEvent(event);
|
events.pauseEvent(event);
|
||||||
this.setState({active: false});
|
this.setState({ active: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleInputClick = (event) => {
|
handleInputClick = (event) => {
|
||||||
events.pauseEvent(event);
|
events.pauseEvent(event);
|
||||||
this.setState({active: true});
|
this.setState({ active: true });
|
||||||
if (this.props.onClick) this.props.onClick(event);
|
if (this.props.onClick) this.props.onClick(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleInputKeyPress = (event) => {
|
handleInputKeyPress = (event) => {
|
||||||
if (event.charCode === 13) {
|
if (event.charCode === 13) {
|
||||||
events.pauseEvent(event);
|
events.pauseEvent(event);
|
||||||
this.setState({active: true});
|
this.setState({ active: true });
|
||||||
}
|
}
|
||||||
if (this.props.onKeyPress) this.props.onKeyPress(event);
|
if (this.props.onKeyPress) this.props.onKeyPress(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSelect = (value, event) => {
|
handleSelect = (value, event) => {
|
||||||
if (this.props.onChange) this.props.onChange(value, event);
|
if (this.props.onChange) this.props.onChange(value, event);
|
||||||
this.setState({active: false});
|
this.setState({ active: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { active, onDismiss,// eslint-disable-line
|
const { active, onDismiss,// eslint-disable-line
|
||||||
autoOk, cancelLabel, enabledDates, disabledDates, inputClassName, inputFormat,
|
autoOk, cancelLabel, enabledDates, disabledDates, inputClassName, inputFormat,
|
||||||
locale, maxDate, minDate, okLabel, onEscKeyDown, onOverlayClick, readonly,
|
locale, maxDate, minDate, okLabel, onEscKeyDown, onOverlayClick, readonly,
|
||||||
|
@ -116,10 +116,10 @@ const factory = (Input, DatePickerDialog) => {
|
||||||
const formattedDate = date === undefined ? '' : finalInputFormat(value, locale);
|
const formattedDate = date === undefined ? '' : finalInputFormat(value, locale);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox='date-picker' className={this.props.theme.container}>
|
<div data-react-toolbox="date-picker" className={this.props.theme.container}>
|
||||||
<Input
|
<Input
|
||||||
{...others}
|
{...others}
|
||||||
className={classnames(this.props.theme.input, {[inputClassName]: inputClassName })}
|
className={classnames(this.props.theme.input, { [inputClassName]: inputClassName })}
|
||||||
disabled={readonly}
|
disabled={readonly}
|
||||||
error={this.props.error}
|
error={this.props.error}
|
||||||
icon={this.props.icon}
|
icon={this.props.icon}
|
||||||
|
@ -129,7 +129,7 @@ const factory = (Input, DatePickerDialog) => {
|
||||||
onKeyPress={this.handleInputKeyPress}
|
onKeyPress={this.handleInputKeyPress}
|
||||||
onClick={this.handleInputClick}
|
onClick={this.handleInputClick}
|
||||||
readOnly
|
readOnly
|
||||||
type='text'
|
type="text"
|
||||||
value={formattedDate}
|
value={formattedDate}
|
||||||
/>
|
/>
|
||||||
<DatePickerDialog
|
<DatePickerDialog
|
||||||
|
@ -166,7 +166,7 @@ const DatePicker = factory(InjectInput, DatePickerDialog);
|
||||||
|
|
||||||
export default themr(DATE_PICKER)(DatePicker);
|
export default themr(DATE_PICKER)(DatePicker);
|
||||||
export {
|
export {
|
||||||
DatePickerDialog as DatePickerDialog,
|
DatePickerDialog,
|
||||||
factory as datePickerFactory
|
factory as datePickerFactory,
|
||||||
};
|
};
|
||||||
export { DatePicker };
|
export { DatePicker };
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import time from '../utils/time.js';
|
import time from '../utils/time';
|
||||||
|
|
||||||
const factory = (Dialog, Calendar) => {
|
const factory = (Dialog, Calendar) => {
|
||||||
class CalendarDialog extends Component {
|
class CalendarDialog extends Component {
|
||||||
|
@ -9,14 +9,14 @@ const factory = (Dialog, Calendar) => {
|
||||||
autoOk: PropTypes.bool,
|
autoOk: PropTypes.bool,
|
||||||
cancelLabel: PropTypes.string,
|
cancelLabel: PropTypes.string,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
disabledDates: PropTypes.array,
|
disabledDates: React.PropTypes.arrayOf(PropTypes.instanceOf(Date)),
|
||||||
enabledDates: PropTypes.array,
|
enabledDates: React.PropTypes.arrayOf(PropTypes.instanceOf(Date)),
|
||||||
locale: React.PropTypes.oneOfType([
|
locale: React.PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
React.PropTypes.string,
|
||||||
React.PropTypes.object
|
React.PropTypes.object,
|
||||||
]),
|
]),
|
||||||
maxDate: PropTypes.object,
|
maxDate: PropTypes.instanceOf(Date),
|
||||||
minDate: PropTypes.object,
|
minDate: PropTypes.instanceOf(Date),
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
okLabel: PropTypes.string,
|
okLabel: PropTypes.string,
|
||||||
onDismiss: PropTypes.func,
|
onDismiss: PropTypes.func,
|
||||||
|
@ -32,9 +32,9 @@ const factory = (Dialog, Calendar) => {
|
||||||
header: PropTypes.string,
|
header: PropTypes.string,
|
||||||
monthsDisplay: PropTypes.string,
|
monthsDisplay: PropTypes.string,
|
||||||
year: PropTypes.string,
|
year: PropTypes.string,
|
||||||
yearsDisplay: PropTypes.string
|
yearsDisplay: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
value: PropTypes.object
|
value: PropTypes.instanceOf(Date),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -42,24 +42,24 @@ const factory = (Dialog, Calendar) => {
|
||||||
cancelLabel: 'Cancel',
|
cancelLabel: 'Cancel',
|
||||||
className: '',
|
className: '',
|
||||||
okLabel: 'Ok',
|
okLabel: 'Ok',
|
||||||
value: new Date()
|
value: new Date(),
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
display: 'months',
|
display: 'months',
|
||||||
date: this.props.value
|
date: this.props.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount() {
|
||||||
this.updateStateDate(this.props.value);
|
this.updateStateDate(this.props.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
this.updateStateDate(nextProps.value);
|
this.updateStateDate(nextProps.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNewDate = (value, dayClick) => {
|
handleNewDate = (value, dayClick) => {
|
||||||
const state = {display: 'months', date: value};
|
const state = { display: 'months', date: value };
|
||||||
if (time.dateOutOfRange(value, this.props.minDate, this.props.maxDate)) {
|
if (time.dateOutOfRange(value, this.props.minDate, this.props.maxDate)) {
|
||||||
if (this.props.maxDate && this.props.minDate) {
|
if (this.props.maxDate && this.props.minDate) {
|
||||||
state.date = time.closestDate(value, this.props.maxDate, this.props.minDate);
|
state.date = time.closestDate(value, this.props.maxDate, this.props.minDate);
|
||||||
|
@ -87,12 +87,18 @@ const factory = (Dialog, Calendar) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
actions = [
|
actions = [{
|
||||||
{ label: this.props.cancelLabel, className: this.props.theme.button, onClick: this.props.onDismiss },
|
label: this.props.cancelLabel,
|
||||||
{ label: this.props.okLabel, className: this.props.theme.button, name: this.props.name, onClick: this.handleSelect }
|
className: this.props.theme.button,
|
||||||
];
|
onClick: this.props.onDismiss,
|
||||||
|
}, {
|
||||||
|
label: this.props.okLabel,
|
||||||
|
className: this.props.theme.button,
|
||||||
|
name: this.props.name,
|
||||||
|
onClick: this.handleSelect,
|
||||||
|
}];
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
const display = `${this.state.display}Display`;
|
const display = `${this.state.display}Display`;
|
||||||
const className = classnames(theme.dialog, this.props.className);
|
const className = classnames(theme.dialog, this.props.className);
|
||||||
|
@ -111,10 +117,10 @@ const factory = (Dialog, Calendar) => {
|
||||||
type="custom"
|
type="custom"
|
||||||
>
|
>
|
||||||
<header className={headerClassName}>
|
<header className={headerClassName}>
|
||||||
<span id='years' className={theme.year} onClick={this.handleSwitchDisplay}>
|
<span id="years" className={theme.year} onClick={this.handleSwitchDisplay}>
|
||||||
{this.state.date.getFullYear()}
|
{this.state.date.getFullYear()}
|
||||||
</span>
|
</span>
|
||||||
<h3 id='months' className={theme.date} onClick={this.handleSwitchDisplay}>
|
<h3 id="months" className={theme.date} onClick={this.handleSwitchDisplay}>
|
||||||
{shortDayOfWeek}, {shortMonth} {date}
|
{shortDayOfWeek}, {shortMonth} {date}
|
||||||
</h3>
|
</h3>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -1,82 +1,83 @@
|
||||||
|
/* eslint-disable */
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import theme from '../theme.css';
|
import theme from '../theme.css';
|
||||||
import { DatePickerDialog } from '../DatePicker';
|
import { DatePickerDialog } from '../DatePicker';
|
||||||
import utils from '../../utils/testing';
|
import utils from '../../utils/testing';
|
||||||
|
|
||||||
describe('DatePickerDialog', function () {
|
describe('DatePickerDialog', () => {
|
||||||
describe('#on mount', function () {
|
describe('#on mount', () => {
|
||||||
it('passes value through to calendar if no maxDate/minDate specified', function () {
|
it('passes value through to calendar if no maxDate/minDate specified', () => {
|
||||||
const value = new Date(2016, 1, 1);
|
const value = new Date(2016, 1, 1);
|
||||||
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {theme, value});
|
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { theme, value });
|
||||||
expect(getDatePassedToCalendar(wrapper)).toBe(value);
|
expect(getDatePassedToCalendar(wrapper)).toBe(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when minDate but not maxDate specified', function () {
|
describe('when minDate but not maxDate specified', () => {
|
||||||
const minDate = new Date(2016, 1, 2);
|
const minDate = new Date(2016, 1, 2);
|
||||||
|
|
||||||
it('passes through a value after minDate', function () {
|
it('passes through a value after minDate', () => {
|
||||||
const value = new Date(2016, 1, 3);
|
const value = new Date(2016, 1, 3);
|
||||||
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {theme, value, minDate});
|
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { theme, value, minDate });
|
||||||
expect(getDatePassedToCalendar(wrapper)).toBe(value);
|
expect(getDatePassedToCalendar(wrapper)).toBe(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sanitises a value before minDate to minDate', function () {
|
it('sanitises a value before minDate to minDate', () => {
|
||||||
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
|
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
|
||||||
theme, value: new Date(2016, 1, 1), minDate
|
theme, value: new Date(2016, 1, 1), minDate,
|
||||||
});
|
});
|
||||||
expect(getDatePassedToCalendar(wrapper)).toBe(minDate);
|
expect(getDatePassedToCalendar(wrapper)).toBe(minDate);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when maxDate but not minDate specified', function () {
|
describe('when maxDate but not minDate specified', () => {
|
||||||
const maxDate = new Date(2016, 1, 2);
|
const maxDate = new Date(2016, 1, 2);
|
||||||
|
|
||||||
it('passes through a value before maxDate', function () {
|
it('passes through a value before maxDate', () => {
|
||||||
const value = new Date(2016, 1, 1);
|
const value = new Date(2016, 1, 1);
|
||||||
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {theme, value, maxDate});
|
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { theme, value, maxDate });
|
||||||
expect(getDatePassedToCalendar(wrapper)).toBe(value);
|
expect(getDatePassedToCalendar(wrapper)).toBe(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sanitises a value after maxDate to maxDate', function () {
|
it('sanitises a value after maxDate to maxDate', () => {
|
||||||
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
|
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
|
||||||
theme, value: new Date(2016, 1, 3), maxDate
|
theme, value: new Date(2016, 1, 3), maxDate,
|
||||||
});
|
});
|
||||||
expect(getDatePassedToCalendar(wrapper)).toBe(maxDate);
|
expect(getDatePassedToCalendar(wrapper)).toBe(maxDate);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('if both minDate and maxDate are set', function () {
|
describe('if both minDate and maxDate are set', () => {
|
||||||
const minDate = new Date(2016, 1, 2);
|
const minDate = new Date(2016, 1, 2);
|
||||||
const maxDate = new Date(2016, 1, 4);
|
const maxDate = new Date(2016, 1, 4);
|
||||||
|
|
||||||
it('sanitises value to minDate if value is before minDate', function () {
|
it('sanitises value to minDate if value is before minDate', () => {
|
||||||
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
|
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
|
||||||
theme,
|
theme,
|
||||||
value: new Date(2016, 1, 1),
|
value: new Date(2016, 1, 1),
|
||||||
minDate,
|
minDate,
|
||||||
maxDate
|
maxDate,
|
||||||
});
|
});
|
||||||
expect(getDatePassedToCalendar(wrapper)).toBe(minDate);
|
expect(getDatePassedToCalendar(wrapper)).toBe(minDate);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sanitises value to maxDate if value is after maxDate', function () {
|
it('sanitises value to maxDate if value is after maxDate', () => {
|
||||||
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
|
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
|
||||||
theme,
|
theme,
|
||||||
value: new Date(2016, 1, 5),
|
value: new Date(2016, 1, 5),
|
||||||
minDate,
|
minDate,
|
||||||
maxDate
|
maxDate,
|
||||||
});
|
});
|
||||||
expect(getDatePassedToCalendar(wrapper)).toBe(maxDate);
|
expect(getDatePassedToCalendar(wrapper)).toBe(maxDate);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('doesn\'t sanitise when value is between maxDate/minDate', function () {
|
it('doesn\'t sanitise when value is between maxDate/minDate', () => {
|
||||||
const value = new Date(2016, 1, 3);
|
const value = new Date(2016, 1, 3);
|
||||||
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {theme, value, minDate, maxDate});
|
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { theme, value, minDate, maxDate });
|
||||||
expect(getDatePassedToCalendar(wrapper)).toBe(value);
|
expect(getDatePassedToCalendar(wrapper)).toBe(value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function getDatePassedToCalendar (wrapper) {
|
function getDatePassedToCalendar(wrapper) {
|
||||||
return wrapper.props.children[1].props.children.props.selectedDate;
|
return wrapper.props.children[1].props.children.props.selectedDate;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { DATE_PICKER, DIALOG } from '../identifiers.js';
|
import { DATE_PICKER, DIALOG } from '../identifiers';
|
||||||
import { datePickerFactory } from './DatePicker.js';
|
import { datePickerFactory } from './DatePicker';
|
||||||
import datePickerDialogFactory from './DatePickerDialog.js';
|
import datePickerDialogFactory from './DatePickerDialog';
|
||||||
import calendarFactory from './Calendar.js';
|
import calendarFactory from './Calendar';
|
||||||
|
|
||||||
import { IconButton } from '../button';
|
import { IconButton } from '../button';
|
||||||
import Input from '../input';
|
import { Input } from '../input';
|
||||||
import Dialog from '../dialog';
|
import { Dialog } from '../dialog';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const Calendar = calendarFactory(IconButton);
|
const Calendar = calendarFactory(IconButton);
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
|
/* eslint-disable jsx-a11y/aria-role */
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { DIALOG } from '../identifiers.js';
|
import { DIALOG } from '../identifiers';
|
||||||
import Portal from '../hoc/Portal.js';
|
import Portal from '../hoc/Portal';
|
||||||
import ActivableRenderer from '../hoc/ActivableRenderer.js';
|
import ActivableRenderer from '../hoc/ActivableRenderer';
|
||||||
import InjectButton from '../button/Button.js';
|
import InjectButton from '../button/Button';
|
||||||
import InjectOverlay from '../overlay/Overlay.js';
|
import InjectOverlay from '../overlay/Overlay';
|
||||||
|
|
||||||
const factory = (Overlay, Button) => {
|
const factory = (Overlay, Button) => {
|
||||||
const Dialog = (props) => {
|
const Dialog = (props) => {
|
||||||
const actions = props.actions.map((action, idx) => {
|
const actions = props.actions.map((action, idx) => {
|
||||||
const className = classnames(props.theme.button, {[action.className]: action.className});
|
const className = classnames(props.theme.button, { [action.className]: action.className });
|
||||||
return <Button key={idx} {...action} className={className} />;
|
return <Button key={idx} {...action} className={className} />; // eslint-disable-line
|
||||||
});
|
});
|
||||||
|
|
||||||
const className = classnames([props.theme.dialog, props.theme[props.type]], {
|
const className = classnames([props.theme.dialog, props.theme[props.type]], {
|
||||||
[props.theme.active]: props.active
|
[props.theme.active]: props.active,
|
||||||
}, props.className);
|
}, props.className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -31,13 +32,13 @@ const factory = (Overlay, Button) => {
|
||||||
theme={props.theme}
|
theme={props.theme}
|
||||||
themeNamespace="overlay"
|
themeNamespace="overlay"
|
||||||
/>
|
/>
|
||||||
<div data-react-toolbox='dialog' className={className}>
|
<div data-react-toolbox="dialog" className={className}>
|
||||||
<section role='body' className={props.theme.body}>
|
<section role="body" className={props.theme.body}>
|
||||||
{props.title ? <h6 className={props.theme.title}>{props.title}</h6> : null}
|
{props.title ? <h6 className={props.theme.title}>{props.title}</h6> : null}
|
||||||
{props.children}
|
{props.children}
|
||||||
</section>
|
</section>
|
||||||
{actions.length
|
{actions.length
|
||||||
? <nav role='navigation' className={props.theme.navigation}>
|
? <nav role="navigation" className={props.theme.navigation}>
|
||||||
{actions}
|
{actions}
|
||||||
</nav>
|
</nav>
|
||||||
: null
|
: null
|
||||||
|
@ -48,7 +49,11 @@ const factory = (Overlay, Button) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
Dialog.propTypes = {
|
Dialog.propTypes = {
|
||||||
actions: PropTypes.array,
|
actions: PropTypes.arrayOf(PropTypes.shape({
|
||||||
|
className: PropTypes.string,
|
||||||
|
label: PropTypes.string,
|
||||||
|
children: PropTypes.node,
|
||||||
|
})),
|
||||||
active: PropTypes.bool,
|
active: PropTypes.bool,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
@ -65,16 +70,16 @@ const factory = (Overlay, Button) => {
|
||||||
navigation: PropTypes.string,
|
navigation: PropTypes.string,
|
||||||
overlay: PropTypes.string,
|
overlay: PropTypes.string,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
wrapper: PropTypes.string
|
wrapper: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
type: PropTypes.string
|
type: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
Dialog.defaultProps = {
|
Dialog.defaultProps = {
|
||||||
actions: [],
|
actions: [],
|
||||||
active: false,
|
active: false,
|
||||||
type: 'normal'
|
type: 'normal',
|
||||||
};
|
};
|
||||||
|
|
||||||
return ActivableRenderer()(Dialog);
|
return ActivableRenderer()(Dialog);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { DIALOG } from '../identifiers.js';
|
import { DIALOG } from '../identifiers';
|
||||||
import { dialogFactory } from './Dialog.js';
|
import { dialogFactory } from './Dialog';
|
||||||
import Overlay from '../overlay';
|
import { Overlay } from '../overlay';
|
||||||
import Button from '../button';
|
import { Button } from '../button';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const Dialog = dialogFactory(Overlay, Button);
|
const Dialog = dialogFactory(Overlay, Button);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Portal from '../hoc/Portal.js';
|
import Portal from '../hoc/Portal';
|
||||||
import { DRAWER } from '../identifiers.js';
|
import { DRAWER } from '../identifiers';
|
||||||
import ActivableRenderer from '../hoc/ActivableRenderer.js';
|
import ActivableRenderer from '../hoc/ActivableRenderer';
|
||||||
import InjectOverlay from '../overlay/Overlay.js';
|
import InjectOverlay from '../overlay/Overlay';
|
||||||
|
|
||||||
const factory = (Overlay) => {
|
const factory = (Overlay) => {
|
||||||
const Drawer = ({
|
const Drawer = ({
|
||||||
|
@ -16,23 +16,21 @@ const factory = (Overlay) => {
|
||||||
onEscKeyDown,
|
onEscKeyDown,
|
||||||
theme,
|
theme,
|
||||||
type,
|
type,
|
||||||
withOverlay
|
withOverlay,
|
||||||
}) => {
|
}) => {
|
||||||
const _className = classnames([theme.drawer, theme[type]], {
|
const _className = classnames([theme.drawer, theme[type]], {
|
||||||
[theme.active]: active
|
[theme.active]: active,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<aside
|
<aside data-react-toolbox="drawer" className={_className}>
|
||||||
data-react-toolbox='drawer'
|
{children}
|
||||||
className={_className}
|
</aside>
|
||||||
children={children}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return React.createElement(insideTree ? 'div' : Portal, {
|
return React.createElement(
|
||||||
className: theme.wrapper,
|
insideTree ? 'div' : Portal,
|
||||||
children: [
|
{ className: theme.wrapper },
|
||||||
withOverlay && (
|
withOverlay && (
|
||||||
<Overlay
|
<Overlay
|
||||||
active={active}
|
active={active}
|
||||||
|
@ -42,9 +40,8 @@ const factory = (Overlay) => {
|
||||||
themeNamespace="overlay"
|
themeNamespace="overlay"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
content
|
content,
|
||||||
]
|
);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Drawer.propTypes = {
|
Drawer.propTypes = {
|
||||||
|
@ -52,18 +49,18 @@ const factory = (Overlay) => {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
insideTree: PropTypes.bool,
|
insideTree: PropTypes.bool,
|
||||||
onOverlayClick: PropTypes.func,
|
|
||||||
onEscKeyDown: PropTypes.func,
|
onEscKeyDown: PropTypes.func,
|
||||||
|
onOverlayClick: PropTypes.func,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
active: PropTypes.string,
|
active: PropTypes.string,
|
||||||
drawer: PropTypes.string,
|
drawer: PropTypes.string,
|
||||||
left: PropTypes.string,
|
left: PropTypes.string,
|
||||||
right: PropTypes.string
|
right: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
type: PropTypes.oneOf([
|
type: PropTypes.oneOf([
|
||||||
'left', 'right'
|
'left', 'right',
|
||||||
]),
|
]),
|
||||||
withOverlay: PropTypes.bool
|
withOverlay: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
Drawer.defaultProps = {
|
Drawer.defaultProps = {
|
||||||
|
@ -71,7 +68,7 @@ const factory = (Overlay) => {
|
||||||
className: '',
|
className: '',
|
||||||
insideTree: false,
|
insideTree: false,
|
||||||
type: 'left',
|
type: 'left',
|
||||||
withOverlay: true
|
withOverlay: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return ActivableRenderer()(Drawer);
|
return ActivableRenderer()(Drawer);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { DRAWER } from '../identifiers.js';
|
import { DRAWER } from '../identifiers';
|
||||||
import { Overlay } from '../overlay';
|
import { Overlay } from '../overlay';
|
||||||
import { drawerFactory } from './Drawer.js';
|
import { drawerFactory } from './Drawer';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const Drawer = drawerFactory(Overlay);
|
const Drawer = drawerFactory(Overlay);
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
/* eslint-disable */
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { DROPDOWN } from '../identifiers.js';
|
import { DROPDOWN } from '../identifiers';
|
||||||
import InjectInput from '../input/Input.js';
|
import InjectInput from '../input/Input';
|
||||||
import events from '../utils/events.js';
|
import events from '../utils/events';
|
||||||
|
|
||||||
const factory = (Input) => {
|
const factory = (Input) => {
|
||||||
class Dropdown extends Component {
|
class Dropdown extends Component {
|
||||||
|
@ -21,7 +22,10 @@ const factory = (Input) => {
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
onFocus: PropTypes.func,
|
onFocus: PropTypes.func,
|
||||||
required: PropTypes.bool,
|
required: PropTypes.bool,
|
||||||
source: PropTypes.array.isRequired,
|
source: PropTypes.arrayOf(PropTypes.oneOfType([
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.object,
|
||||||
|
])).isRequired,
|
||||||
template: PropTypes.func,
|
template: PropTypes.func,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
active: PropTypes.string,
|
active: PropTypes.string,
|
||||||
|
@ -36,12 +40,12 @@ const factory = (Input) => {
|
||||||
templateValue: PropTypes.string,
|
templateValue: PropTypes.string,
|
||||||
up: PropTypes.string,
|
up: PropTypes.string,
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
values: PropTypes.string
|
values: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
value: PropTypes.oneOfType([
|
value: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.number
|
PropTypes.number,
|
||||||
])
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -49,27 +53,27 @@ const factory = (Input) => {
|
||||||
className: '',
|
className: '',
|
||||||
allowBlank: true,
|
allowBlank: true,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
required: false
|
required: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
active: false,
|
active: false,
|
||||||
up: false
|
up: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillUpdate (nextProps, nextState) {
|
componentWillUpdate(nextProps, nextState) {
|
||||||
if (!this.state.active && nextState.active) {
|
if (!this.state.active && nextState.active) {
|
||||||
events.addEventsToDocument(this.getDocumentEvents());
|
events.addEventsToDocument(this.getDocumentEvents());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate (prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
if (prevState.active && !this.state.active) {
|
if (prevState.active && !this.state.active) {
|
||||||
events.removeEventsFromDocument(this.getDocumentEvents());
|
events.removeEventsFromDocument(this.getDocumentEvents());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
if (this.state.active) {
|
if (this.state.active) {
|
||||||
events.removeEventsFromDocument(this.getDocumentEvents());
|
events.removeEventsFromDocument(this.getDocumentEvents());
|
||||||
}
|
}
|
||||||
|
@ -77,26 +81,24 @@ const factory = (Input) => {
|
||||||
|
|
||||||
getDocumentEvents = () => ({
|
getDocumentEvents = () => ({
|
||||||
click: this.handleDocumentClick,
|
click: this.handleDocumentClick,
|
||||||
touchend: this.handleDocumentClick
|
touchend: this.handleDocumentClick,
|
||||||
});
|
});
|
||||||
|
|
||||||
open = (event) => {
|
getSelectedItem = () => {
|
||||||
const client = event.target.getBoundingClientRect();
|
for (const item of this.props.source) {
|
||||||
const screenHeight = window.innerHeight || document.documentElement.offsetHeight;
|
if (item.value === this.props.value) return item;
|
||||||
const up = this.props.auto ? client.top > ((screenHeight / 2) + client.height) : false;
|
}
|
||||||
if (this.inputNode) this.inputNode.blur();
|
return !this.props.allowBlank
|
||||||
this.setState({active: true, up});
|
? this.props.source[0]
|
||||||
|
: undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
close = () => {
|
handleSelect = (item, event) => {
|
||||||
if (this.state.active) {
|
if (this.props.onBlur) this.props.onBlur(event);
|
||||||
this.setState({active: false});
|
if (!this.props.disabled && this.props.onChange) {
|
||||||
}
|
if (this.props.name) event.target.name = this.props.name;
|
||||||
}
|
this.props.onChange(item, event);
|
||||||
|
this.close();
|
||||||
handleDocumentClick = (event) => {
|
|
||||||
if (this.state.active && !events.targetIsDescendant(event, ReactDOM.findDOMNode(this))) {
|
|
||||||
this.setState({active: false});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -106,32 +108,44 @@ const factory = (Input) => {
|
||||||
if (this.props.onClick) this.props.onClick(event);
|
if (this.props.onClick) this.props.onClick(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSelect = (item, event) => {
|
handleDocumentClick = (event) => {
|
||||||
|
if (this.state.active && !events.targetIsDescendant(event, ReactDOM.findDOMNode(this))) {
|
||||||
|
this.setState({ active: false });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
close = () => {
|
||||||
|
if (this.state.active) {
|
||||||
|
this.setState({ active: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open = (event) => {
|
||||||
|
const client = event.target.getBoundingClientRect();
|
||||||
|
const screenHeight = window.innerHeight || document.documentElement.offsetHeight;
|
||||||
|
const up = this.props.auto ? client.top > ((screenHeight / 2) + client.height) : false;
|
||||||
|
if (this.inputNode) this.inputNode.blur();
|
||||||
|
this.setState({ active: true, up });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleFocus = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (!this.props.disabled) this.open(event);
|
||||||
|
if (this.props.onFocus) this.props.onFocus(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleBlur = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (this.state.active) this.close();
|
||||||
if (this.props.onBlur) this.props.onBlur(event);
|
if (this.props.onBlur) this.props.onBlur(event);
|
||||||
if (!this.props.disabled && this.props.onChange) {
|
|
||||||
if (this.props.name) {
|
|
||||||
event.target.name = this.props.name;
|
|
||||||
}
|
}
|
||||||
this.props.onChange(item, event);
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
getSelectedItem = () => {
|
renderTemplateValue(selected) {
|
||||||
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 { theme } = this.props;
|
||||||
const className = classnames(theme.field, {
|
const className = classnames(theme.field, {
|
||||||
[theme.errored]: this.props.error,
|
[theme.errored]: this.props.error,
|
||||||
[theme.disabled]: this.props.disabled,
|
[theme.disabled]: this.props.disabled,
|
||||||
[theme.required]: this.props.required
|
[theme.required]: this.props.required,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -140,11 +154,12 @@ const factory = (Input) => {
|
||||||
{this.props.template(selected)}
|
{this.props.template(selected)}
|
||||||
</div>
|
</div>
|
||||||
{this.props.label
|
{this.props.label
|
||||||
? <label className={theme.label}>
|
? (
|
||||||
|
<label htmlFor={this.props.name} className={theme.label}>
|
||||||
{this.props.label}
|
{this.props.label}
|
||||||
{this.props.required ? <span className={theme.required}> * </span> : null}
|
{this.props.required ? <span className={theme.required}> * </span> : null}
|
||||||
</label>
|
</label>
|
||||||
: null}
|
) : null}
|
||||||
{this.props.error ? <span className={theme.error}>{this.props.error}</span> : null}
|
{this.props.error ? <span className={theme.error}>{this.props.error}</span> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -154,30 +169,22 @@ const factory = (Input) => {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
const className = classnames({
|
const className = classnames({
|
||||||
[theme.selected]: item.value === this.props.value,
|
[theme.selected]: item.value === this.props.value,
|
||||||
[theme.disabled]: item.disabled
|
[theme.disabled]: item.disabled,
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<li key={idx} className={className} onClick={!item.disabled ? this.handleSelect.bind(this, item.value) : null}>
|
<li
|
||||||
|
key={idx}
|
||||||
|
className={className}
|
||||||
|
onClick={!item.disabled && this.handleSelect.bind(this, item.value)}
|
||||||
|
>
|
||||||
{this.props.template ? this.props.template(item) : item.label}
|
{this.props.template ? this.props.template(item) : item.label}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleFocus = event => {
|
render() {
|
||||||
event.stopPropagation();
|
|
||||||
if (!this.props.disabled) this.open(event);
|
|
||||||
if (this.props.onFocus) this.props.onFocus(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleBlur = event => {
|
|
||||||
event.stopPropagation();
|
|
||||||
if (this.state.active) this.close();
|
|
||||||
if (this.props.onBlur) this.props.onBlur(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const {
|
const {
|
||||||
allowBlank, auto, required, onChange, onFocus, onBlur, //eslint-disable-line no-unused-vars
|
allowBlank, auto, required, onChange, onFocus, onBlur, // eslint-disable-line no-unused-vars
|
||||||
source, template, theme, ...others
|
source, template, theme, ...others
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const selected = this.getSelectedItem();
|
const selected = this.getSelectedItem();
|
||||||
|
@ -185,13 +192,13 @@ const factory = (Input) => {
|
||||||
[theme.up]: this.state.up,
|
[theme.up]: this.state.up,
|
||||||
[theme.active]: this.state.active,
|
[theme.active]: this.state.active,
|
||||||
[theme.disabled]: this.props.disabled,
|
[theme.disabled]: this.props.disabled,
|
||||||
[theme.required]: this.props.required
|
[theme.required]: this.props.required,
|
||||||
}, this.props.className);
|
}, this.props.className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={className}
|
className={className}
|
||||||
data-react-toolbox='dropdown'
|
data-react-toolbox="dropdown"
|
||||||
onBlur={this.handleBlur}
|
onBlur={this.handleBlur}
|
||||||
onFocus={this.handleFocus}
|
onFocus={this.handleFocus}
|
||||||
>
|
>
|
||||||
|
@ -202,14 +209,14 @@ const factory = (Input) => {
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
required={this.props.required}
|
required={this.props.required}
|
||||||
readOnly
|
readOnly
|
||||||
ref={node => { this.inputNode = node && node.getWrappedInstance(); }}
|
ref={(node) => { this.inputNode = node && node.getWrappedInstance(); }}
|
||||||
type={template && selected ? 'hidden' : null}
|
type={template && selected ? 'hidden' : null}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
themeNamespace="input"
|
themeNamespace="input"
|
||||||
value={selected && selected.label ? selected.label : ''}
|
value={selected && selected.label ? selected.label : ''}
|
||||||
/>
|
/>
|
||||||
{template && selected ? this.renderTemplateValue(selected) : null}
|
{template && selected ? this.renderTemplateValue(selected) : null}
|
||||||
<ul className={theme.values} ref='values'>
|
<ul className={theme.values}>
|
||||||
{source.map(this.renderValue)}
|
{source.map(this.renderValue)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
|
/* eslint-disable */
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
renderIntoDocument,
|
renderIntoDocument,
|
||||||
scryRenderedDOMComponentsWithClass,
|
scryRenderedDOMComponentsWithClass,
|
||||||
Simulate
|
Simulate,
|
||||||
} from 'react-addons-test-utils';
|
} from 'react-addons-test-utils';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import theme from '../theme.css';
|
import theme from '../theme.css';
|
||||||
import Dropdown from '../Dropdown';
|
import Dropdown from '../Dropdown';
|
||||||
|
|
||||||
|
|
||||||
describe('Dropdown', function () {
|
describe('Dropdown', () => {
|
||||||
describe('#renderValue', function () {
|
describe('#renderValue', () => {
|
||||||
const source = [
|
const source = [
|
||||||
{ value: 'EN-gb', label: 'England' },
|
{ value: 'EN-gb', label: 'England' },
|
||||||
{ value: 'ES-es', label: 'Spain', disabled: true },
|
{ value: 'ES-es', label: 'Spain', disabled: true },
|
||||||
{ value: 'TH-th', label: 'Thailand', disabled: true },
|
{ value: 'TH-th', label: 'Thailand', disabled: true },
|
||||||
{ value: 'EN-en', label: 'USA'}
|
{ value: 'EN-en', label: 'USA' },
|
||||||
];
|
];
|
||||||
it('renders dropdown item with disabled style', function () {
|
it('renders dropdown item with disabled style', () => {
|
||||||
const tree = renderIntoDocument(<Dropdown theme={theme} source={source} />);
|
const tree = renderIntoDocument(<Dropdown theme={theme} source={source} />);
|
||||||
const disabled = scryRenderedDOMComponentsWithClass(tree, theme.disabled);
|
const disabled = scryRenderedDOMComponentsWithClass(tree, theme.disabled);
|
||||||
expect(disabled.length).toEqual(2);
|
expect(disabled.length).toEqual(2);
|
||||||
});
|
});
|
||||||
it('does not call onChange callback when disabled dorpdown item is clicked', function () {
|
it('does not call onChange callback when disabled dorpdown item is clicked', () => {
|
||||||
const spy = sinon.spy();
|
const spy = sinon.spy();
|
||||||
const tree = renderIntoDocument(<Dropdown
|
const tree = renderIntoDocument(<Dropdown
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { DROPDOWN } from '../identifiers.js';
|
import { DROPDOWN } from '../identifiers';
|
||||||
import { dropdownFactory } from './Dropdown.js';
|
import { dropdownFactory } from './Dropdown';
|
||||||
import { Input } from '../input';
|
import { Input } from '../input';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@ import classnames from 'classnames';
|
||||||
|
|
||||||
const FontIcon = ({ alt, children, className, theme, value, ...other}) => ( // eslint-disable-line
|
const FontIcon = ({ alt, children, className, theme, value, ...other}) => ( // eslint-disable-line
|
||||||
<span
|
<span
|
||||||
data-react-toolbox='font-icon'
|
data-react-toolbox="font-icon"
|
||||||
aria-label={alt}
|
aria-label={alt}
|
||||||
className={classnames({'material-icons': typeof value === 'string' || typeof children === 'string'}, className)}
|
className={classnames({ 'material-icons': typeof value === 'string' || typeof children === 'string' }, className)}
|
||||||
{...other}
|
{...other}
|
||||||
>
|
>
|
||||||
<span aria-hidden="true">{value}</span>
|
<span aria-hidden="true">{value}</span>
|
||||||
|
@ -15,18 +15,18 @@ const FontIcon = ({ alt, children, className, theme, value, ...other}) => ( // e
|
||||||
|
|
||||||
FontIcon.propTypes = {
|
FontIcon.propTypes = {
|
||||||
alt: PropTypes.string,
|
alt: PropTypes.string,
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
theme: PropTypes.object,
|
theme: PropTypes.object, // eslint-disable-line
|
||||||
value: PropTypes.oneOfType([
|
value: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
])
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
FontIcon.defaultProps = {
|
FontIcon.defaultProps = {
|
||||||
alt: '',
|
alt: '',
|
||||||
className: ''
|
className: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FontIcon;
|
export default FontIcon;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import FontIcon from './FontIcon.js';
|
import { FontIcon } from './FontIcon';
|
||||||
|
|
||||||
export default FontIcon;
|
export default FontIcon;
|
||||||
export { FontIcon };
|
export { FontIcon };
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
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 factory = (
|
|
||||||
Autocomplete, Button, Checkbox, DatePicker, Dropdown,
|
|
||||||
Input, RadioGroup, Slider, Switch, TimePicker
|
|
||||||
) => {
|
|
||||||
|
|
||||||
const COMPONENTS = {
|
|
||||||
'autocomplete': Autocomplete,
|
|
||||||
'button': Button,
|
|
||||||
'checkbox': Checkbox,
|
|
||||||
'datepicker': DatePicker,
|
|
||||||
'dropdown': Dropdown,
|
|
||||||
'input': Input,
|
|
||||||
'radioGroup': RadioGroup,
|
|
||||||
'slider': Slider,
|
|
||||||
'switch': Switch,
|
|
||||||
'timepicker': TimePicker
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
attributes: [],
|
|
||||||
className: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
onSubmit = (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
if (this.props.onSubmit) this.props.onSubmit(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
onChange = (field, value, event) => {
|
|
||||||
if (this.props.onChange) this.props.onChange(field, value, event);
|
|
||||||
};
|
|
||||||
|
|
||||||
renderFields () {
|
|
||||||
return Object.keys(this.props.model).map((field, index) => {
|
|
||||||
const properties = this.props.model[field];
|
|
||||||
const Field = COMPONENTS[properties.kind.toLowerCase()];
|
|
||||||
return <Field key={index} {...properties} onChange={this.onChange.bind(this, field)} />;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
return (
|
|
||||||
<form data-react-toolbox='form' className={this.props.className} onSubmit={this.onSubmit}>
|
|
||||||
{this.renderFields()}
|
|
||||||
{this.props.children}
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Form;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Form = factory(
|
|
||||||
InjectAutocomplete, InjectButton, InjectCheckbox, InjectDatePicker, InjectDropdown,
|
|
||||||
InjectInput, InjectRadioGroup, InjectSlider, InjectSwitch, InjectTimePicker
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Form;
|
|
||||||
export { factory as formFactory };
|
|
||||||
export { Form };
|
|
|
@ -1,19 +0,0 @@
|
||||||
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 };
|
|
|
@ -1,24 +0,0 @@
|
||||||
# Form
|
|
||||||
|
|
||||||
```
|
|
||||||
var Form = require('react-toolbox/components/form');
|
|
||||||
var fields : [
|
|
||||||
{ref: "name", label: "Your Name", required: true, storage: true},
|
|
||||||
{ref: "description", multiline: true, label: "Description", value: "Doer"},
|
|
||||||
{ref: "birthdate", type: "date", label: "Birthdate"}
|
|
||||||
]
|
|
||||||
|
|
||||||
<Form attributes={fields} storage="my_toolbox_form" />
|
|
||||||
```
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Name | Type | Default | Description|
|
|
||||||
|:- |:-: | :- |:-|
|
|
||||||
| **attributes** | array | | Array of fields you want hold, fields can be instances of <Autocomplete>, <Button/>, <Dropdown>, <Input/> or <Switch/> |
|
|
||||||
| **className** | String | | Set the class-styles of the Component.|
|
|
||||||
| **onChange** | Function | | Dispatch callback when values of the component changes.|
|
|
||||||
| **onError** | Function | | Dispatch callback when a required field is null or has incorrect type.|
|
|
||||||
| **onSubmit** | Function | | Dispatch callback when user clicks on submit <Button/> |
|
|
||||||
| **onValid** | Function | | Dispatch callback when all required fields are full-filled.|
|
|
||||||
| **Storage** | String | | Sets a localStorage key for save all current field values.|
|
|
|
@ -1,40 +1,40 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
const ActivableRendererFactory = (options = {delay: 500}) =>
|
const ActivableRendererFactory = (options = { delay: 500 }) =>
|
||||||
ActivableComponent => class ActivableRenderer extends Component {
|
ActivableComponent => class ActivableRenderer extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
active: PropTypes.bool.isRequired,
|
active: PropTypes.bool.isRequired,
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
delay: PropTypes.number
|
delay: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
delay: options.delay
|
delay: options.delay,
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
active: this.props.active,
|
active: this.props.active,
|
||||||
rendered: this.props.active
|
rendered: this.props.active,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.active && !this.props.active) this.renderAndActivate();
|
if (nextProps.active && !this.props.active) this.renderAndActivate();
|
||||||
if (!nextProps.active && this.props.active) this.deactivateAndUnrender();
|
if (!nextProps.active && this.props.active) this.deactivateAndUnrender();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
clearTimeout(this.activateTimeout);
|
clearTimeout(this.activateTimeout);
|
||||||
clearTimeout(this.unrenderTimeout);
|
clearTimeout(this.unrenderTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAndActivate () {
|
renderAndActivate() {
|
||||||
if (this.unrenderTimeout) clearTimeout(this.unrenderTimeout);
|
if (this.unrenderTimeout) clearTimeout(this.unrenderTimeout);
|
||||||
this.setState({ rendered: true, active: false }, () => {
|
this.setState({ rendered: true, active: false }, () => {
|
||||||
this.activateTimeout = setTimeout(() => this.setState({ active: true }), 20);
|
this.activateTimeout = setTimeout(() => this.setState({ active: true }), 20);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deactivateAndUnrender () {
|
deactivateAndUnrender() {
|
||||||
this.setState({ rendered: true, active: false }, () => {
|
this.setState({ rendered: true, active: false }, () => {
|
||||||
this.unrenderTimeout = setTimeout(() => {
|
this.unrenderTimeout = setTimeout(() => {
|
||||||
this.setState({ rendered: false });
|
this.setState({ rendered: false });
|
||||||
|
@ -43,7 +43,7 @@ const ActivableRendererFactory = (options = {delay: 500}) =>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { delay, ...others } = this.props; // eslint-disable-line no-unused-vars
|
const { delay, ...others } = this.props; // eslint-disable-line no-unused-vars
|
||||||
const { active, rendered } = this.state;
|
const { active, rendered } = this.state;
|
||||||
return rendered
|
return rendered
|
||||||
|
|
|
@ -3,22 +3,20 @@ import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
class Portal extends Component {
|
class Portal extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
container: PropTypes.any,
|
container: PropTypes.node,
|
||||||
lockBody: PropTypes.bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
className: '',
|
className: '',
|
||||||
lockBody: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
this._renderOverlay();
|
this._renderOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (this._overlayTarget && nextProps.container !== this.props.container) {
|
if (this._overlayTarget && nextProps.container !== this.props.container) {
|
||||||
this._portalContainerNode.removeChild(this._overlayTarget);
|
this._portalContainerNode.removeChild(this._overlayTarget);
|
||||||
this._portalContainerNode = getContainer(nextProps.container);
|
this._portalContainerNode = getContainer(nextProps.container);
|
||||||
|
@ -26,42 +24,45 @@ class Portal extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate() {
|
||||||
this._renderOverlay();
|
this._renderOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
this._unrenderOverlay();
|
this._unrenderOverlay();
|
||||||
this._unmountOverlayTarget();
|
this._unmountOverlayTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
_mountOverlayTarget () {
|
getMountNode() {
|
||||||
if (!this._overlayTarget) {
|
return this._overlayTarget;
|
||||||
this._overlayTarget = document.createElement('div');
|
|
||||||
this._portalContainerNode = getContainer(this.props.container);
|
|
||||||
this._portalContainerNode.appendChild(this._overlayTarget);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_unmountOverlayTarget () {
|
getOverlayDOMNode() {
|
||||||
if (this._overlayTarget) {
|
if (!this.isMounted()) { // eslint-disable-line
|
||||||
this._portalContainerNode.removeChild(this._overlayTarget);
|
throw new Error('getOverlayDOMNode(): A component must be mounted to have a DOM node.');
|
||||||
this._overlayTarget = null;
|
|
||||||
}
|
|
||||||
this._portalContainerNode = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getOverlay () {
|
if (this._overlayInstance) {
|
||||||
|
if (this._overlayInstance.getWrappedDOMNode) {
|
||||||
|
return this._overlayInstance.getWrappedDOMNode();
|
||||||
|
}
|
||||||
|
return ReactDOM.findDOMNode(this._overlayInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getOverlay() {
|
||||||
if (!this.props.children) return null;
|
if (!this.props.children) return null;
|
||||||
return <div className={this.props.className}>{this.props.children}</div>;
|
return <div className={this.props.className}>{this.props.children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderOverlay () {
|
_renderOverlay() {
|
||||||
const overlay = this._getOverlay();
|
const overlay = this._getOverlay();
|
||||||
if (overlay !== null) {
|
if (overlay !== null) {
|
||||||
this._mountOverlayTarget();
|
this._mountOverlayTarget();
|
||||||
this._overlayInstance = ReactDOM.unstable_renderSubtreeIntoContainer(
|
this._overlayInstance = ReactDOM.unstable_renderSubtreeIntoContainer(
|
||||||
this, overlay, this._overlayTarget
|
this, overlay, this._overlayTarget,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this._unrenderOverlay();
|
this._unrenderOverlay();
|
||||||
|
@ -69,40 +70,36 @@ class Portal extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_unrenderOverlay () {
|
_unrenderOverlay() {
|
||||||
if (this._overlayTarget) {
|
if (this._overlayTarget) {
|
||||||
ReactDOM.unmountComponentAtNode(this._overlayTarget);
|
ReactDOM.unmountComponentAtNode(this._overlayTarget);
|
||||||
this._overlayInstance = null;
|
this._overlayInstance = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getMountNode () {
|
_mountOverlayTarget() {
|
||||||
return this._overlayTarget;
|
if (!this._overlayTarget) {
|
||||||
}
|
this._overlayTarget = document.createElement('div');
|
||||||
|
this._portalContainerNode = getContainer(this.props.container);
|
||||||
getOverlayDOMNode () {
|
this._portalContainerNode.appendChild(this._overlayTarget);
|
||||||
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 ReactDOM.findDOMNode(this._overlayInstance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
_unmountOverlayTarget() {
|
||||||
|
if (this._overlayTarget) {
|
||||||
|
this._portalContainerNode.removeChild(this._overlayTarget);
|
||||||
|
this._overlayTarget = null;
|
||||||
|
}
|
||||||
|
this._portalContainerNode = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getContainer (container) {
|
function getContainer(container) {
|
||||||
const _container = typeof container === 'function' ? container() : container;
|
const _container = typeof container === 'function' ? container() : container;
|
||||||
return ReactDOM.findDOMNode(_container) || document.body;
|
return ReactDOM.findDOMNode(_container) || document.body;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
import './utils/polyfills'; // Import polyfills for IE11
|
import './utils/polyfills'; // Import polyfills for IE11
|
||||||
|
|
||||||
export { overrideComponentTypeChecker } from './utils/is-component-of-type';
|
export { overrideComponentTypeChecker } from './utils/is-component-of-type';
|
||||||
export AppBar from './app_bar';
|
export { default as AppBar } from './app_bar';
|
||||||
export Autocomplete from './autocomplete';
|
export { default as Autocomplete } from './autocomplete';
|
||||||
export Avatar from './avatar';
|
export { default as Avatar } from './avatar';
|
||||||
export * from './button';
|
export * from './button';
|
||||||
export * from './card';
|
export * from './card';
|
||||||
export Chip from './chip';
|
export { default as Chip } from './chip';
|
||||||
export Checkbox from './checkbox';
|
export { default as Checkbox } from './checkbox';
|
||||||
export DatePicker from './date_picker';
|
export { default as DatePicker } from './date_picker';
|
||||||
export Dialog from './dialog';
|
export { default as Dialog } from './dialog';
|
||||||
export Drawer from './drawer';
|
export { default as Drawer } from './drawer';
|
||||||
export Dropdown from './dropdown';
|
export { default as Dropdown } from './dropdown';
|
||||||
export FontIcon from './font_icon';
|
export { default as FontIcon } from './font_icon';
|
||||||
export Form from './form';
|
export { default as Input } from './input';
|
||||||
export Input from './input';
|
|
||||||
export * from './layout';
|
export * from './layout';
|
||||||
export Link from './link';
|
export { default as Link } from './link';
|
||||||
export * from './list';
|
export * from './list';
|
||||||
export * from './menu';
|
export * from './menu';
|
||||||
export Navigation from './navigation';
|
export { default as Navigation } from './navigation';
|
||||||
export ProgressBar from './progress_bar';
|
export { default as ProgressBar } from './progress_bar';
|
||||||
export * from './radio';
|
export * from './radio';
|
||||||
export Ripple from './ripple';
|
export Ripple from './ripple';
|
||||||
export Slider from './slider';
|
export { default as Slider } from './slider';
|
||||||
export Snackbar from './snackbar';
|
export { default as Snackbar } from './snackbar';
|
||||||
export Switch from './switch';
|
export { default as Switch } from './switch';
|
||||||
export Table from './table';
|
export { default as Table } from './table';
|
||||||
export * from './tabs';
|
export * from './tabs';
|
||||||
export Tooltip from './tooltip';
|
export Tooltip from './tooltip';
|
||||||
export TimePicker from './time_picker';
|
export { default as TimePicker } from './time_picker';
|
||||||
|
|
|
@ -1,57 +1,60 @@
|
||||||
import React from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { INPUT } from '../identifiers.js';
|
import { INPUT } from '../identifiers';
|
||||||
import InjectedFontIcon from '../font_icon/FontIcon.js';
|
import InjectedFontIcon from '../font_icon/FontIcon';
|
||||||
|
|
||||||
const factory = (FontIcon) => {
|
const factory = (FontIcon) => {
|
||||||
class Input extends React.Component {
|
class Input extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: React.PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: React.PropTypes.string,
|
className: PropTypes.string,
|
||||||
disabled: React.PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
error: React.PropTypes.oneOfType([
|
error: PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
PropTypes.string,
|
||||||
React.PropTypes.node
|
PropTypes.node,
|
||||||
]),
|
]),
|
||||||
floating: React.PropTypes.bool,
|
floating: PropTypes.bool,
|
||||||
hint: React.PropTypes.oneOfType([
|
hint: PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
PropTypes.string,
|
||||||
React.PropTypes.node
|
PropTypes.node,
|
||||||
]),
|
]),
|
||||||
icon: React.PropTypes.oneOfType([
|
icon: PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
PropTypes.string,
|
||||||
React.PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
label: React.PropTypes.oneOfType([
|
label: PropTypes.oneOfType([
|
||||||
React.PropTypes.string,
|
PropTypes.string,
|
||||||
React.PropTypes.node
|
PropTypes.node,
|
||||||
]),
|
]),
|
||||||
maxLength: React.PropTypes.number,
|
maxLength: PropTypes.number,
|
||||||
multiline: React.PropTypes.bool,
|
multiline: PropTypes.bool,
|
||||||
name: React.PropTypes.string,
|
name: PropTypes.string,
|
||||||
onBlur: React.PropTypes.func,
|
onBlur: PropTypes.func,
|
||||||
onChange: React.PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
onFocus: React.PropTypes.func,
|
onFocus: PropTypes.func,
|
||||||
onKeyPress: React.PropTypes.func,
|
onKeyPress: PropTypes.func,
|
||||||
required: React.PropTypes.bool,
|
required: PropTypes.bool,
|
||||||
rows: React.PropTypes.number,
|
rows: PropTypes.number,
|
||||||
theme: React.PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
bar: React.PropTypes.string,
|
bar: PropTypes.string,
|
||||||
counter: React.PropTypes.string,
|
counter: PropTypes.string,
|
||||||
disabled: React.PropTypes.string,
|
disabled: PropTypes.string,
|
||||||
error: React.PropTypes.string,
|
error: PropTypes.string,
|
||||||
errored: React.PropTypes.string,
|
errored: PropTypes.string,
|
||||||
hidden: React.PropTypes.string,
|
hidden: PropTypes.string,
|
||||||
hint: React.PropTypes.string,
|
hint: PropTypes.string,
|
||||||
icon: React.PropTypes.string,
|
icon: PropTypes.string,
|
||||||
input: React.PropTypes.string,
|
input: PropTypes.string,
|
||||||
inputElement: React.PropTypes.string,
|
inputElement: PropTypes.string,
|
||||||
required: React.PropTypes.string,
|
required: PropTypes.string,
|
||||||
withIcon: React.PropTypes.string
|
withIcon: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
type: React.PropTypes.string,
|
type: PropTypes.string,
|
||||||
value: React.PropTypes.any
|
value: PropTypes.oneOfType([
|
||||||
|
PropTypes.object,
|
||||||
|
PropTypes.string,
|
||||||
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -61,17 +64,17 @@ const factory = (FontIcon) => {
|
||||||
floating: true,
|
floating: true,
|
||||||
multiline: false,
|
multiline: false,
|
||||||
required: false,
|
required: false,
|
||||||
type: 'text'
|
type: 'text',
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
if (this.props.multiline) {
|
if (this.props.multiline) {
|
||||||
window.addEventListener('resize', this.handleAutoresize);
|
window.addEventListener('resize', this.handleAutoresize);
|
||||||
this.handleAutoresize();
|
this.handleAutoresize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (!this.props.multiline && nextProps.multiline) {
|
if (!this.props.multiline && nextProps.multiline) {
|
||||||
window.addEventListener('resize', this.handleAutoresize);
|
window.addEventListener('resize', this.handleAutoresize);
|
||||||
} else if (this.props.multiline && !nextProps.multiline) {
|
} else if (this.props.multiline && !nextProps.multiline) {
|
||||||
|
@ -79,12 +82,12 @@ const factory = (FontIcon) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate() {
|
||||||
// resize the textarea, if nessesary
|
// resize the textarea, if nessesary
|
||||||
if (this.props.multiline) this.handleAutoresize();
|
if (this.props.multiline) this.handleAutoresize();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
if (this.props.multiline) window.removeEventListener('resize', this.handleAutoresize);
|
if (this.props.multiline) window.removeEventListener('resize', this.handleAutoresize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +106,7 @@ const factory = (FontIcon) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
handleAutoresize = () => {
|
handleAutoresize = () => {
|
||||||
const element = this.refs.input;
|
const element = this.inputNode;
|
||||||
const rows = this.props.rows;
|
const rows = this.props.rows;
|
||||||
|
|
||||||
if (typeof rows === 'number' && !isNaN(rows)) {
|
if (typeof rows === 'number' && !isNaN(rows)) {
|
||||||
|
@ -135,51 +138,52 @@ const factory = (FontIcon) => {
|
||||||
if (!isReplacing && value.length === maxLength) {
|
if (!isReplacing && value.length === maxLength) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onKeyPress) onKeyPress(event);
|
if (onKeyPress) onKeyPress(event);
|
||||||
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
blur () {
|
blur() {
|
||||||
this.refs.input.blur();
|
this.inputNode.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
focus () {
|
focus() {
|
||||||
this.refs.input.focus();
|
this.inputNode.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { children, disabled, error, floating, hint, icon,
|
const { children, disabled, error, floating, hint, icon,
|
||||||
name, label: labelText, maxLength, multiline, required,
|
name, label: labelText, maxLength, multiline, required,
|
||||||
theme, type, value, onKeyPress, rows = 1, ...others} = this.props;
|
theme, type, value, onKeyPress, rows = 1, ...others } = this.props;
|
||||||
const length = maxLength && value ? value.length : 0;
|
const length = maxLength && value ? value.length : 0;
|
||||||
const labelClassName = classnames(theme.label, {[theme.fixed]: !floating});
|
const labelClassName = classnames(theme.label, { [theme.fixed]: !floating });
|
||||||
|
|
||||||
const className = classnames(theme.input, {
|
const className = classnames(theme.input, {
|
||||||
[theme.disabled]: disabled,
|
[theme.disabled]: disabled,
|
||||||
[theme.errored]: error,
|
[theme.errored]: error,
|
||||||
[theme.hidden]: type === 'hidden',
|
[theme.hidden]: type === 'hidden',
|
||||||
[theme.withIcon]: icon
|
[theme.withIcon]: icon,
|
||||||
}, this.props.className);
|
}, this.props.className);
|
||||||
|
|
||||||
const valuePresent = value !== null
|
const valuePresent = value !== null
|
||||||
&& value !== undefined
|
&& value !== undefined
|
||||||
&& value !== ''
|
&& value !== ''
|
||||||
&& !(typeof value === Number && isNaN(value));
|
&& !(typeof value === Number && isNaN(value)); // eslint-disable-line
|
||||||
|
|
||||||
const inputElementProps = {
|
const inputElementProps = {
|
||||||
...others,
|
...others,
|
||||||
className: classnames(theme.inputElement, {[theme.filled]: valuePresent}),
|
className: classnames(theme.inputElement, { [theme.filled]: valuePresent }),
|
||||||
onChange: this.handleChange,
|
onChange: this.handleChange,
|
||||||
ref: 'input',
|
ref: (node) => { this.inputNode = node; },
|
||||||
role: 'input',
|
role: 'input',
|
||||||
name,
|
name,
|
||||||
disabled,
|
disabled,
|
||||||
required,
|
required,
|
||||||
type,
|
type,
|
||||||
value
|
value,
|
||||||
};
|
};
|
||||||
if (!multiline) {
|
if (!multiline) {
|
||||||
inputElementProps.maxLength = maxLength;
|
inputElementProps.maxLength = maxLength;
|
||||||
|
@ -190,12 +194,12 @@ const factory = (FontIcon) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox='input' className={className}>
|
<div data-react-toolbox="input" className={className}>
|
||||||
{React.createElement(multiline ? 'textarea' : 'input', inputElementProps)}
|
{React.createElement(multiline ? 'textarea' : 'input', inputElementProps)}
|
||||||
{icon ? <FontIcon className={theme.icon} value={icon} /> : null}
|
{icon ? <FontIcon className={theme.icon} value={icon} /> : null}
|
||||||
<span className={theme.bar} />
|
<span className={theme.bar} />
|
||||||
{labelText
|
{labelText
|
||||||
? <label className={labelClassName}>
|
? <label className={labelClassName} htmlFor={name}>
|
||||||
{labelText}
|
{labelText}
|
||||||
{required ? <span className={theme.required}> * </span> : null}
|
{required ? <span className={theme.required}> * </span> : null}
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { INPUT } from '../identifiers.js';
|
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { inputFactory } from './Input.js';
|
import { INPUT } from '../identifiers';
|
||||||
import FontIcon from '../font_icon/FontIcon.js';
|
import { inputFactory } from './Input';
|
||||||
|
import { FontIcon } from '../font_icon/FontIcon';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const Input = inputFactory(FontIcon);
|
const Input = inputFactory(FontIcon);
|
||||||
|
|
|
@ -2,8 +2,8 @@ import React, { cloneElement, Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { getViewport } from '../utils/utils';
|
import { getViewport } from '../utils/utils';
|
||||||
import filterReactChildren from '../utils/filter-react-children.js';
|
import filterReactChildren from '../utils/filter-react-children';
|
||||||
import isComponentOfType from '../utils/is-component-of-type.js';
|
import isComponentOfType from '../utils/is-component-of-type';
|
||||||
import InjectAppBar from '../app_bar/AppBar';
|
import InjectAppBar from '../app_bar/AppBar';
|
||||||
import InjectNavDrawer from './NavDrawer';
|
import InjectNavDrawer from './NavDrawer';
|
||||||
import InjectSidebar from './Sidebar';
|
import InjectSidebar from './Sidebar';
|
||||||
|
@ -21,23 +21,31 @@ const factory = (AppBar, NavDrawer, Sidebar) => {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
theme: PropTypes.object
|
theme: PropTypes.shape({
|
||||||
|
appbarFixed: PropTypes.string,
|
||||||
|
layout: PropTypes.string,
|
||||||
|
layoutInner: PropTypes.string,
|
||||||
|
navDrawerClipped: PropTypes.string,
|
||||||
|
navDrawerPinned: PropTypes.string,
|
||||||
|
sidebarClipped: PropTypes.string,
|
||||||
|
sidebarPinned: PropTypes.string,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
className: ''
|
className: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
width: isBrowser() && getViewport().width
|
width: isBrowser() && getViewport().width,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
if (!this.state.width) this.handleResize();
|
if (!this.state.width) this.handleResize();
|
||||||
window.addEventListener('resize', this.handleResize);
|
window.addEventListener('resize', this.handleResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('resize', this.handleResize);
|
window.removeEventListener('resize', this.handleResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +53,7 @@ const factory = (AppBar, NavDrawer, Sidebar) => {
|
||||||
this.setState({ width: getViewport().width });
|
this.setState({ width: getViewport().width });
|
||||||
}
|
}
|
||||||
|
|
||||||
isPinned = sideNav => {
|
isPinned = (sideNav) => {
|
||||||
if (sideNav) {
|
if (sideNav) {
|
||||||
const { permanentAt, pinned } = sideNav.props;
|
const { permanentAt, pinned } = sideNav.props;
|
||||||
const { width } = this.state;
|
const { width } = this.state;
|
||||||
|
@ -54,7 +62,7 @@ const factory = (AppBar, NavDrawer, Sidebar) => {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { children, className, theme, ...rest } = this.props;
|
const { children, className, theme, ...rest } = this.props;
|
||||||
const appBar = filterReactChildren(children, isAppBar)[0];
|
const appBar = filterReactChildren(children, isAppBar)[0];
|
||||||
const navDrawer = filterReactChildren(children, isNavDrawer)[0];
|
const navDrawer = filterReactChildren(children, isNavDrawer)[0];
|
||||||
|
@ -69,17 +77,17 @@ const factory = (AppBar, NavDrawer, Sidebar) => {
|
||||||
|
|
||||||
const clonedAppBar = appBar && cloneElement(appBar, {
|
const clonedAppBar = appBar && cloneElement(appBar, {
|
||||||
theme,
|
theme,
|
||||||
themeNamespace: 'appbar'
|
themeNamespace: 'appbar',
|
||||||
});
|
});
|
||||||
|
|
||||||
const clonedLeftSideNav = navDrawer && cloneElement(navDrawer, {
|
const clonedLeftSideNav = navDrawer && cloneElement(navDrawer, {
|
||||||
clipped: navDrawerClipped,
|
clipped: navDrawerClipped,
|
||||||
pinned: navDrawerPinned
|
pinned: navDrawerPinned,
|
||||||
});
|
});
|
||||||
|
|
||||||
const clonedRightSideNav = sidebar && cloneElement(sidebar, {
|
const clonedRightSideNav = sidebar && cloneElement(sidebar, {
|
||||||
clipped: sidebarClipped,
|
clipped: sidebarClipped,
|
||||||
pinned: sidebarPinned
|
pinned: sidebarPinned,
|
||||||
});
|
});
|
||||||
|
|
||||||
const _className = classnames(theme.layout,
|
const _className = classnames(theme.layout,
|
||||||
|
@ -88,7 +96,7 @@ const factory = (AppBar, NavDrawer, Sidebar) => {
|
||||||
[theme.navDrawerClipped]: navDrawerClipped,
|
[theme.navDrawerClipped]: navDrawerClipped,
|
||||||
[theme.sidebarPinned]: sidebarPinned,
|
[theme.sidebarPinned]: sidebarPinned,
|
||||||
[theme.sidebarClipped]: sidebarClipped,
|
[theme.sidebarClipped]: sidebarClipped,
|
||||||
[theme.appbarFixed]: appBarFixed
|
[theme.appbarFixed]: appBarFixed,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { themr } from 'react-css-themr';
|
||||||
import InjectDrawer from '../drawer/Drawer';
|
import InjectDrawer from '../drawer/Drawer';
|
||||||
import { LAYOUT } from '../identifiers';
|
import { LAYOUT } from '../identifiers';
|
||||||
|
|
||||||
const factory = Drawer => {
|
const factory = (Drawer) => {
|
||||||
const NavDrawer = ({
|
const NavDrawer = ({
|
||||||
active,
|
active,
|
||||||
className,
|
className,
|
||||||
|
@ -16,7 +16,7 @@ const factory = Drawer => {
|
||||||
}) => {
|
}) => {
|
||||||
const _className = classnames({
|
const _className = classnames({
|
||||||
[theme.pinned]: pinned,
|
[theme.pinned]: pinned,
|
||||||
[theme.clipped]: clipped
|
[theme.clipped]: clipped,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -40,12 +40,15 @@ const factory = Drawer => {
|
||||||
permanentAt: PropTypes.oneOf(['sm', 'smTablet', 'md', 'lg', 'lgTablet', 'xl', 'xxl', 'xxxl']),
|
permanentAt: PropTypes.oneOf(['sm', 'smTablet', 'md', 'lg', 'lgTablet', 'xl', 'xxl', 'xxxl']),
|
||||||
pinned: PropTypes.bool,
|
pinned: PropTypes.bool,
|
||||||
right: PropTypes.bool,
|
right: PropTypes.bool,
|
||||||
theme: PropTypes.object
|
theme: PropTypes.shape({
|
||||||
|
clipped: PropTypes.string,
|
||||||
|
pinned: PropTypes.string,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
NavDrawer.defaultProps = {
|
NavDrawer.defaultProps = {
|
||||||
className: '',
|
className: '',
|
||||||
pinned: false
|
pinned: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return NavDrawer;
|
return NavDrawer;
|
||||||
|
|
|
@ -1,31 +1,29 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import cn from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LAYOUT } from '../identifiers.js';
|
import { LAYOUT } from '../identifiers';
|
||||||
|
|
||||||
const Panel = ({ bodyScroll, children, className, theme, ...other }) => (
|
const Panel = ({ bodyScroll, children, className, theme, ...other }) => {
|
||||||
<div
|
const _className = cn(theme.panel, { [theme.bodyScroll]: bodyScroll }, className);
|
||||||
{...other}
|
return (
|
||||||
data-react-toolbox='panel'
|
<div {...other} data-react-toolbox="panel" className={_className}>
|
||||||
className={classnames(theme.panel, {
|
{children}
|
||||||
[theme.bodyScroll]: bodyScroll
|
</div>
|
||||||
}, className)}
|
);
|
||||||
children={children}
|
};
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
Panel.propTypes = {
|
Panel.propTypes = {
|
||||||
bodyScroll: PropTypes.bool,
|
bodyScroll: PropTypes.bool,
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
panel: PropTypes.string
|
panel: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
Panel.defaultProps = {
|
Panel.defaultProps = {
|
||||||
bodyScroll: true,
|
bodyScroll: true,
|
||||||
className: ''
|
className: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themr(LAYOUT)(Panel);
|
export default themr(LAYOUT)(Panel);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { themr } from 'react-css-themr';
|
||||||
import InjectDrawer from '../drawer/Drawer';
|
import InjectDrawer from '../drawer/Drawer';
|
||||||
import { LAYOUT } from '../identifiers';
|
import { LAYOUT } from '../identifiers';
|
||||||
|
|
||||||
const factory = Drawer => {
|
const factory = (Drawer) => {
|
||||||
const Sidebar = ({
|
const Sidebar = ({
|
||||||
active,
|
active,
|
||||||
className,
|
className,
|
||||||
|
@ -16,7 +16,7 @@ const factory = Drawer => {
|
||||||
}) => {
|
}) => {
|
||||||
const _className = classnames({
|
const _className = classnames({
|
||||||
[theme.pinned]: pinned,
|
[theme.pinned]: pinned,
|
||||||
[theme.clipped]: clipped
|
[theme.clipped]: clipped,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -40,14 +40,17 @@ const factory = Drawer => {
|
||||||
clipped: PropTypes.bool,
|
clipped: PropTypes.bool,
|
||||||
permanentAt: PropTypes.oneOf(['sm', 'smTablet', 'md', 'lg', 'lgTablet', 'xl', 'xxl', 'xxxl']),
|
permanentAt: PropTypes.oneOf(['sm', 'smTablet', 'md', 'lg', 'lgTablet', 'xl', 'xxl', 'xxxl']),
|
||||||
pinned: PropTypes.bool,
|
pinned: PropTypes.bool,
|
||||||
theme: PropTypes.object,
|
theme: PropTypes.shape({
|
||||||
width: PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 25, 33, 50, 66, 75, 100])
|
clipped: PropTypes.string,
|
||||||
|
pinned: 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 = {
|
Sidebar.defaultProps = {
|
||||||
className: '',
|
className: '',
|
||||||
pinned: false,
|
pinned: false,
|
||||||
right: false
|
right: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Sidebar;
|
return Sidebar;
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { LAYOUT } from '../identifiers';
|
||||||
import { layoutFactory } from './Layout';
|
import { layoutFactory } from './Layout';
|
||||||
import { sidebarFactory } from './Sidebar';
|
import { sidebarFactory } from './Sidebar';
|
||||||
import { navDrawerFactory } from './NavDrawer';
|
import { navDrawerFactory } from './NavDrawer';
|
||||||
import Panel from './Panel';
|
import { Panel } from './Panel';
|
||||||
import AppBar from '../app_bar';
|
import { AppBar } from '../app_bar';
|
||||||
import Drawer from '../drawer';
|
import { Drawer } from '../drawer';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const injectTheme = component => themr(LAYOUT, theme)(component);
|
const injectTheme = component => themr(LAYOUT, theme)(component);
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LINK } from '../identifiers.js';
|
import { LINK } from '../identifiers';
|
||||||
import FontIcon from '../font_icon/FontIcon.js';
|
import { FontIcon } from '../font_icon/FontIcon';
|
||||||
|
|
||||||
const Link = ({active, children, className, count, icon, label, theme, ...others}) => {
|
const Link = ({ active, children, className, count, icon, label, theme, ...others }) => {
|
||||||
const _className = classnames(theme.link, {
|
const _className = classnames(theme.link, {
|
||||||
[theme.active]: active
|
[theme.active]: active,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a data-react-toolbox='link' className={_className} {...others}>
|
<a data-react-toolbox="link" className={_className} {...others}>
|
||||||
{icon ? <FontIcon className={theme.icon} value={icon} /> : null}
|
{icon ? <FontIcon className={theme.icon} value={icon} /> : null}
|
||||||
{label ? <abbr>{label}</abbr> : null}
|
{label ? <abbr>{label}</abbr> : null}
|
||||||
{count && parseInt(count) !== 0 ? <small>{count}</small> : null}
|
{count && parseInt(count, 10) !== 0 ? <small>{count}</small> : null}
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
@ -26,19 +26,19 @@ Link.propTypes = {
|
||||||
count: PropTypes.number,
|
count: PropTypes.number,
|
||||||
icon: PropTypes.oneOfType([
|
icon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
active: PropTypes.string,
|
active: PropTypes.string,
|
||||||
icon: PropTypes.string,
|
icon: PropTypes.string,
|
||||||
link: PropTypes.string
|
link: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
Link.defaultProps = {
|
Link.defaultProps = {
|
||||||
active: false,
|
active: false,
|
||||||
className: ''
|
className: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themr(LINK)(Link);
|
export default themr(LINK)(Link);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LINK } from '../identifiers.js';
|
import { LINK } from '../identifiers';
|
||||||
import { Link } from './Link.js';
|
import { Link } from './Link';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const ThemedLink = themr(LINK, theme)(Link);
|
const ThemedLink = themr(LINK, theme)(Link);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LIST } from '../identifiers.js';
|
import { LIST } from '../identifiers';
|
||||||
import InjectListItem from './ListItem.js';
|
import InjectListItem from './ListItem';
|
||||||
|
|
||||||
const mergeProp = (propName, child, parent) => (
|
const mergeProp = (propName, child, parent) => (
|
||||||
child[propName] !== undefined
|
child[propName] !== undefined
|
||||||
|
@ -15,20 +15,18 @@ const factory = (ListItem) => {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
ripple: PropTypes.bool,
|
|
||||||
selectable: PropTypes.bool,
|
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
list: PropTypes.string
|
list: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
className: '',
|
className: '',
|
||||||
ripple: false,
|
ripple: false,
|
||||||
selectable: false
|
selectable: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
renderItems () {
|
renderItems() {
|
||||||
return React.Children.map(this.props.children, (item) => {
|
return React.Children.map(this.props.children, (item) => {
|
||||||
if (item === null || item === undefined) {
|
if (item === null || item === undefined) {
|
||||||
return item;
|
return item;
|
||||||
|
@ -36,15 +34,14 @@ const factory = (ListItem) => {
|
||||||
const selectable = mergeProp('selectable', item.props, this.props);
|
const selectable = mergeProp('selectable', item.props, this.props);
|
||||||
const ripple = mergeProp('ripple', item.props, this.props);
|
const ripple = mergeProp('ripple', item.props, this.props);
|
||||||
return React.cloneElement(item, { selectable, ripple });
|
return React.cloneElement(item, { selectable, ripple });
|
||||||
} else {
|
|
||||||
return React.cloneElement(item);
|
|
||||||
}
|
}
|
||||||
|
return React.cloneElement(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ul data-react-toolbox='list' className={classnames(this.props.theme.list, this.props.className)}>
|
<ul data-react-toolbox="list" className={classnames(this.props.theme.list, this.props.className)}>
|
||||||
{this.renderItems()}
|
{this.renderItems()}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,14 +1,25 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LIST } from '../identifiers.js';
|
import { LIST } from '../identifiers';
|
||||||
import InjectCheckbox from '../checkbox/Checkbox.js';
|
import InjectCheckbox from '../checkbox/Checkbox';
|
||||||
import InjectListItemContent from './ListItemContent.js';
|
import InjectListItemContent from './ListItemContent';
|
||||||
|
|
||||||
const factory = (Checkbox, ListItemContent) => {
|
const factory = (Checkbox, ListItemContent) => {
|
||||||
const ListCheckbox = ({ caption, checked, className, disabled, legend, name, onBlur, onChange, onFocus, theme }) => {
|
const ListCheckbox = ({
|
||||||
|
caption,
|
||||||
|
checked,
|
||||||
|
className,
|
||||||
|
disabled,
|
||||||
|
legend,
|
||||||
|
name,
|
||||||
|
onBlur,
|
||||||
|
onChange,
|
||||||
|
onFocus,
|
||||||
|
theme,
|
||||||
|
}) => {
|
||||||
const _className = classnames(theme.item, theme.checkboxItem, {
|
const _className = classnames(theme.item, theme.checkboxItem, {
|
||||||
[theme.disabled]: disabled
|
[theme.disabled]: disabled,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -41,13 +52,13 @@ const factory = (Checkbox, ListItemContent) => {
|
||||||
checkbox: PropTypes.string,
|
checkbox: PropTypes.string,
|
||||||
checkboxItem: PropTypes.string,
|
checkboxItem: PropTypes.string,
|
||||||
disabled: PropTypes.string,
|
disabled: PropTypes.string,
|
||||||
item: PropTypes.string
|
item: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
ListCheckbox.defaultProps = {
|
ListCheckbox.defaultProps = {
|
||||||
checked: false,
|
checked: false,
|
||||||
disabled: false
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return ListCheckbox;
|
return ListCheckbox;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LIST } from '../identifiers.js';
|
import { LIST } from '../identifiers';
|
||||||
|
|
||||||
const ListDivider = ({inset, theme}) => (
|
const ListDivider = ({ inset, theme }) => (
|
||||||
<hr className={inset ? `${theme.divider} ${theme.inset}` : theme.divider} />
|
<hr className={inset ? `${theme.divider} ${theme.inset}` : theme.divider} />
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -10,12 +10,12 @@ ListDivider.propTypes = {
|
||||||
inset: PropTypes.bool,
|
inset: PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
divider: PropTypes.string,
|
divider: PropTypes.string,
|
||||||
inset: PropTypes.string
|
inset: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
ListDivider.defaultProps = {
|
ListDivider.defaultProps = {
|
||||||
inset: false
|
inset: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themr(LIST)(ListDivider);
|
export default themr(LIST)(ListDivider);
|
||||||
|
|
|
@ -1,28 +1,31 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LIST } from '../identifiers.js';
|
import { LIST } from '../identifiers';
|
||||||
import InjectListItemContent from './ListItemContent.js';
|
import InjectListItemContent from './ListItemContent';
|
||||||
import InjectListItemLayout from './ListItemLayout.js';
|
import InjectListItemLayout from './ListItemLayout';
|
||||||
import rippleFactory from '../ripple/Ripple.js';
|
import rippleFactory from '../ripple/Ripple';
|
||||||
|
|
||||||
const factory = (ripple, ListItemLayout, ListItemContent) => {
|
const factory = (ripple, ListItemLayout, ListItemContent) => {
|
||||||
class ListItem extends Component {
|
class ListItem extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
|
hasRipple: PropTypes.bool,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
|
onMouseDown: PropTypes.func,
|
||||||
|
onTouchStart: PropTypes.func,
|
||||||
ripple: PropTypes.bool,
|
ripple: PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
listItem: PropTypes.string
|
listItem: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
to: PropTypes.string
|
to: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
className: '',
|
className: '',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
ripple: false
|
ripple: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = (event) => {
|
handleClick = (event) => {
|
||||||
|
@ -31,16 +34,16 @@ const factory = (ripple, ListItemLayout, ListItemContent) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
groupChildren () {
|
groupChildren() {
|
||||||
const children = {
|
const children = {
|
||||||
leftActions: [],
|
leftActions: [],
|
||||||
rightActions: [],
|
rightActions: [],
|
||||||
ignored: []
|
ignored: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
React.Children.forEach(this.props.children, (child, i) => {
|
React.Children.forEach(this.props.children, (child, i) => {
|
||||||
if (!React.isValidElement(child)) {
|
if (!React.isValidElement(child)) {
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { listItemIgnore, ...rest } = child.props;
|
const { listItemIgnore, ...rest } = child.props;
|
||||||
|
@ -48,23 +51,34 @@ const factory = (ripple, ListItemLayout, ListItemContent) => {
|
||||||
|
|
||||||
if (listItemIgnore) {
|
if (listItemIgnore) {
|
||||||
children.ignored.push(strippedChild);
|
children.ignored.push(strippedChild);
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (child.type === ListItemContent) {
|
if (child.type === ListItemContent) {
|
||||||
children.itemContent = strippedChild;
|
children.itemContent = strippedChild;
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
const bucket = children.itemContent ? 'rightActions' : 'leftActions';
|
const bucket = children.itemContent ? 'rightActions' : 'leftActions';
|
||||||
children[bucket].push({...strippedChild, key: i});
|
children[bucket].push({ ...strippedChild, key: i });
|
||||||
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {className, onMouseDown, onTouchStart, to, onClick, ripple: hasRipple, theme, ...other} = this.props; //eslint-disable-line no-unused-vars
|
const {
|
||||||
|
className,
|
||||||
|
hasRipple, // eslint-disable-line no-unused-vars
|
||||||
|
onClick, // eslint-disable-line no-unused-vars
|
||||||
|
onMouseDown, // eslint-disable-line no-unused-vars
|
||||||
|
onTouchStart, // eslint-disable-line no-unused-vars
|
||||||
|
ripple:
|
||||||
|
theme,
|
||||||
|
to,
|
||||||
|
...other
|
||||||
|
} = this.props;
|
||||||
const children = this.groupChildren();
|
const children = this.groupChildren();
|
||||||
const content = <ListItemLayout theme={theme} {...children} {...other}/>;
|
const content = <ListItemLayout theme={theme} {...children} {...other} />;
|
||||||
return (
|
return (
|
||||||
<li className={`${theme.listItem} ${className}`} onClick={this.handleClick} onMouseDown={onMouseDown} onTouchStart={onTouchStart}>
|
<li className={`${theme.listItem} ${className}`} onClick={this.handleClick} onMouseDown={onMouseDown} onTouchStart={onTouchStart}>
|
||||||
{to ? <a href={this.props.to}>{content}</a> : content}
|
{to ? <a href={this.props.to}>{content}</a> : content}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LIST } from '../identifiers.js';
|
import { LIST } from '../identifiers';
|
||||||
|
|
||||||
const ListItemAction = ({action, theme}) => {
|
const ListItemAction = ({ action, theme }) => {
|
||||||
const {onClick, onMouseDown} = action.props;
|
const { onClick, onMouseDown } = action.props;
|
||||||
const stopRipple = onClick && !onMouseDown;
|
const stopRipple = onClick && !onMouseDown;
|
||||||
const stop = e => e.stopPropagation();
|
const stop = e => e.stopPropagation();
|
||||||
return (
|
return (
|
||||||
|
@ -14,10 +14,10 @@ const ListItemAction = ({action, theme}) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
ListItemAction.propTypes = {
|
ListItemAction.propTypes = {
|
||||||
action: PropTypes.object,
|
action: PropTypes.node,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
itemAction: PropTypes.string
|
itemAction: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themr(LIST)(ListItemAction);
|
export default themr(LIST)(ListItemAction);
|
||||||
|
|
|
@ -1,28 +1,30 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LIST } from '../identifiers.js';
|
import { LIST } from '../identifiers';
|
||||||
import InjectListItemAction from './ListItemAction.js';
|
import InjectListItemAction from './ListItemAction';
|
||||||
|
|
||||||
const factory = (ListItemAction) => {
|
const factory = (ListItemAction) => {
|
||||||
const ListItemActions = ({type, children, theme}) => {
|
const ListItemActions = ({ type, children, theme }) => {
|
||||||
const validChildren = React.Children.toArray(children).filter(c => (
|
const validChildren = React.Children.toArray(children).filter(c => (
|
||||||
React.isValidElement(c)
|
React.isValidElement(c)
|
||||||
));
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={theme[type]}>
|
<span className={theme[type]}>
|
||||||
{validChildren.map((action, i) => <ListItemAction key={i} theme={theme} action={action} />)}
|
{validChildren.map((action, i) => (
|
||||||
|
<ListItemAction key={i} theme={theme} action={action} /> // eslint-disable-line
|
||||||
|
))}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ListItemActions.propTypes = {
|
ListItemActions.propTypes = {
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
left: PropTypes.string,
|
left: PropTypes.string,
|
||||||
right: PropTypes.string
|
right: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
type: PropTypes.oneOf(['left', 'right'])
|
type: PropTypes.oneOf(['left', 'right']),
|
||||||
};
|
};
|
||||||
|
|
||||||
return ListItemActions;
|
return ListItemActions;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LIST } from '../identifiers.js';
|
import { LIST } from '../identifiers';
|
||||||
import InjectListItemText from './ListItemText.js';
|
import InjectListItemText from './ListItemText';
|
||||||
|
|
||||||
const types = ['auto', 'normal', 'large'];
|
const types = ['auto', 'normal', 'large'];
|
||||||
|
|
||||||
|
@ -11,34 +11,34 @@ const factory = (ListItemText) => {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
caption: PropTypes.oneOfType([
|
caption: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.node
|
PropTypes.node,
|
||||||
]),
|
]),
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
legend: PropTypes.string,
|
legend: PropTypes.string,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
auto: PropTypes.string,
|
auto: PropTypes.string,
|
||||||
itemContentRoot: PropTypes.string,
|
itemContentRoot: PropTypes.string,
|
||||||
large: PropTypes.string,
|
large: PropTypes.string,
|
||||||
normal: PropTypes.string
|
normal: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
type: PropTypes.oneOf(types)
|
type: PropTypes.oneOf(types),
|
||||||
};
|
};
|
||||||
|
|
||||||
getType () {
|
getType() {
|
||||||
const {type, children, caption, legend} = this.props;
|
const { type, children, caption, legend } = this.props;
|
||||||
|
|
||||||
let count = React.Children.count(children);
|
let count = React.Children.count(children);
|
||||||
[caption, legend].forEach(s => { count += s ? 1 : 0; });
|
[caption, legend].forEach((s) => { count += s ? 1 : 0; });
|
||||||
const typeIndex = Math.min(count, types.length);
|
const typeIndex = Math.min(count, types.length);
|
||||||
|
|
||||||
return type || types[typeIndex];
|
return type || types[typeIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {children, caption, legend, theme} = this.props;
|
const { children, caption, legend, theme } = this.props;
|
||||||
const contentType = this.getType();
|
const contentType = this.getType();
|
||||||
const className = classnames(theme.itemContentRoot, {
|
const className = classnames(theme.itemContentRoot, {
|
||||||
[theme[contentType]]: theme[contentType]
|
[theme[contentType]]: theme[contentType],
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,36 +1,38 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LIST } from '../identifiers.js';
|
import { LIST } from '../identifiers';
|
||||||
import FontIcon from '../font_icon/FontIcon.js';
|
import { FontIcon } from '../font_icon/FontIcon';
|
||||||
import InjectAvatar from '../avatar/Avatar.js';
|
import InjectAvatar from '../avatar/Avatar';
|
||||||
import InjectListItemContent from './ListItemContent.js';
|
import InjectListItemContent from './ListItemContent';
|
||||||
import InjectListItemActions from './ListItemActions.js';
|
import InjectListItemActions from './ListItemActions';
|
||||||
|
|
||||||
const factory = (Avatar, ListItemContent, ListItemActions) => {
|
const factory = (Avatar, ListItemContent, ListItemActions) => {
|
||||||
const ListItemLayout = (props) => {
|
const ListItemLayout = (props) => {
|
||||||
const className = classnames(props.theme.item, {
|
const className = classnames(props.theme.item, {
|
||||||
[props.theme.disabled]: props.disabled,
|
[props.theme.disabled]: props.disabled,
|
||||||
[props.theme.selectable]: props.selectable
|
[props.theme.selectable]: props.selectable,
|
||||||
}, props.className);
|
}, props.className);
|
||||||
|
|
||||||
const leftActions = [
|
const leftActions = [
|
||||||
props.leftIcon && <FontIcon value={props.leftIcon} key='leftIcon'/>,
|
props.leftIcon && <FontIcon value={props.leftIcon} key="leftIcon" />,
|
||||||
props.avatar && <Avatar image={props.avatar} key='avatar'/>,
|
props.avatar && <Avatar image={props.avatar} key="avatar" />,
|
||||||
...props.leftActions
|
...props.leftActions,
|
||||||
];
|
];
|
||||||
const rightActions = [
|
const rightActions = [
|
||||||
props.rightIcon && <FontIcon value={props.rightIcon} key='rightIcon'/>,
|
props.rightIcon && <FontIcon value={props.rightIcon} key="rightIcon" />,
|
||||||
...props.rightActions
|
...props.rightActions,
|
||||||
];
|
];
|
||||||
const content = props.itemContent || <ListItemContent theme={props.theme} caption={props.caption} legend={props.legend} />;
|
const emptyActions = item => !item[0] && !item[1] && !item[2];
|
||||||
const emptyActions = (item) => !item[0] && !item[1] && !item[2];
|
const content = props.itemContent || (
|
||||||
|
<ListItemContent theme={props.theme} caption={props.caption} legend={props.legend} />
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={className}>
|
<span className={className}>
|
||||||
{!emptyActions(leftActions) > 0 && <ListItemActions type='left' theme={props.theme}>{leftActions}</ListItemActions>}
|
{!emptyActions(leftActions) > 0 && <ListItemActions type="left" theme={props.theme}>{leftActions}</ListItemActions>}
|
||||||
{content}
|
{content}
|
||||||
{!emptyActions(rightActions) > 0 && <ListItemActions type='right' theme={props.theme}>{rightActions}</ListItemActions>}
|
{!emptyActions(rightActions) > 0 && <ListItemActions type="right" theme={props.theme}>{rightActions}</ListItemActions>}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -38,36 +40,34 @@ const factory = (Avatar, ListItemContent, ListItemActions) => {
|
||||||
ListItemLayout.propTypes = {
|
ListItemLayout.propTypes = {
|
||||||
avatar: PropTypes.oneOfType([
|
avatar: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
caption: PropTypes.string,
|
caption: PropTypes.string,
|
||||||
children: PropTypes.any,
|
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
itemContent: PropTypes.element,
|
itemContent: PropTypes.element,
|
||||||
leftActions: PropTypes.array,
|
leftActions: PropTypes.arrayOf(PropTypes.node),
|
||||||
leftIcon: PropTypes.oneOfType([
|
leftIcon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
legend: PropTypes.string,
|
legend: PropTypes.string,
|
||||||
rightActions: PropTypes.array,
|
rightActions: PropTypes.arrayOf(PropTypes.node),
|
||||||
rightIcon: PropTypes.oneOfType([
|
rightIcon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
selectable: PropTypes.bool,
|
selectable: PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
disabled: PropTypes.string,
|
disabled: PropTypes.string,
|
||||||
item: PropTypes.string,
|
item: PropTypes.string,
|
||||||
selectable: PropTypes.string
|
selectable: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
to: PropTypes.string
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ListItemLayout.defaultProps = {
|
ListItemLayout.defaultProps = {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
selectable: false
|
selectable: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return ListItemLayout;
|
return ListItemLayout;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LIST } from '../identifiers.js';
|
import { LIST } from '../identifiers';
|
||||||
|
|
||||||
const ListItemText = ({className, primary, children, theme, ...other}) => {
|
const ListItemText = ({ className, primary, children, theme, ...other }) => {
|
||||||
const _className = classnames(theme.itemText, {[theme.primary]: primary}, className);
|
const _className = classnames(theme.itemText, { [theme.primary]: primary }, className);
|
||||||
return (
|
return (
|
||||||
<span data-react-toolbox="list-item-text" className={_className} {...other}>
|
<span data-react-toolbox="list-item-text" className={_className} {...other}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -13,17 +13,17 @@ const ListItemText = ({className, primary, children, theme, ...other}) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
ListItemText.propTypes = {
|
ListItemText.propTypes = {
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
primary: PropTypes.bool,
|
primary: PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
itemText: PropTypes.string,
|
itemText: PropTypes.string,
|
||||||
primary: PropTypes.string
|
primary: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
ListItemText.defaultProps = {
|
ListItemText.defaultProps = {
|
||||||
primary: false
|
primary: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themr(LIST)(ListItemText);
|
export default themr(LIST)(ListItemText);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LIST } from '../identifiers.js';
|
import { LIST } from '../identifiers';
|
||||||
|
|
||||||
const ListSubHeader = ({ caption, className, theme }) => (
|
const ListSubHeader = ({ caption, className, theme }) => (
|
||||||
<h5 className={classnames(theme.subheader, className)}>{caption}</h5>
|
<h5 className={classnames(theme.subheader, className)}>{caption}</h5>
|
||||||
|
@ -10,11 +10,11 @@ const ListSubHeader = ({ caption, className, theme }) => (
|
||||||
ListSubHeader.propTypes = {
|
ListSubHeader.propTypes = {
|
||||||
caption: PropTypes.string,
|
caption: PropTypes.string,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
theme: PropTypes.object
|
theme: PropTypes.object, // eslint-disable-line
|
||||||
};
|
};
|
||||||
|
|
||||||
ListSubHeader.defaultProps = {
|
ListSubHeader.defaultProps = {
|
||||||
className: ''
|
className: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themr(LIST)(ListSubHeader);
|
export default themr(LIST)(ListSubHeader);
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { LIST } from '../identifiers.js';
|
import { LIST } from '../identifiers';
|
||||||
import { Avatar } from '../avatar';
|
import { Avatar } from '../avatar';
|
||||||
import { Checkbox } from '../checkbox';
|
import { Checkbox } from '../checkbox';
|
||||||
import { ListItemText } from './ListItemText.js';
|
import { ListItemText } from './ListItemText';
|
||||||
import { ListItemAction } from './ListItemAction.js';
|
import { ListItemAction } from './ListItemAction';
|
||||||
import { ListSubHeader } from './ListSubHeader.js';
|
import { ListSubHeader } from './ListSubHeader';
|
||||||
import { ListDivider } from './ListDivider.js';
|
import { ListDivider } from './ListDivider';
|
||||||
import { listFactory } from './List.js';
|
import { listFactory } from './List';
|
||||||
import { listItemFactory } from './ListItem.js';
|
import { listItemFactory } from './ListItem';
|
||||||
import { listCheckboxFactory } from './ListCheckbox.js';
|
import { listCheckboxFactory } from './ListCheckbox';
|
||||||
import { listItemActionsFactory } from './ListItemActions.js';
|
import { listItemActionsFactory } from './ListItemActions';
|
||||||
import { listItemContentFactory } from './ListItemContent.js';
|
import { listItemContentFactory } from './ListItemContent';
|
||||||
import { listItemLayoutFactory } from './ListItemLayout.js';
|
import { listItemLayoutFactory } from './ListItemLayout';
|
||||||
import themedRippleFactory from '../ripple';
|
import themedRippleFactory from '../ripple';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const applyTheme = (Component) => themr(LIST, theme)(Component);
|
const applyTheme = Component => themr(LIST, theme)(Component);
|
||||||
const ripple = themedRippleFactory({ centered: false, listItemIgnore: true });
|
const ripple = themedRippleFactory({ centered: false, listItemIgnore: true });
|
||||||
const ThemedListItemAction = applyTheme(ListItemAction);
|
const ThemedListItemAction = applyTheme(ListItemAction);
|
||||||
const ThemedListSubHeader = applyTheme(ListSubHeader);
|
const ThemedListSubHeader = applyTheme(ListSubHeader);
|
||||||
|
@ -23,9 +23,13 @@ const ThemedListItemText = applyTheme(ListItemText);
|
||||||
const ThemedListDivider = applyTheme(ListDivider);
|
const ThemedListDivider = applyTheme(ListDivider);
|
||||||
const ThemedListItemContent = applyTheme(listItemContentFactory(ThemedListItemText));
|
const ThemedListItemContent = applyTheme(listItemContentFactory(ThemedListItemText));
|
||||||
const ThemedListItemActions = applyTheme(listItemActionsFactory(ThemedListItemAction));
|
const ThemedListItemActions = applyTheme(listItemActionsFactory(ThemedListItemAction));
|
||||||
const ThemedListItemLayout = applyTheme(listItemLayoutFactory(Avatar, ThemedListItemContent, ThemedListItemActions));
|
const ThemedListItemLayout = applyTheme(
|
||||||
|
listItemLayoutFactory(Avatar, ThemedListItemContent, ThemedListItemActions),
|
||||||
|
);
|
||||||
const ThemedListCheckbox = applyTheme(listCheckboxFactory(Checkbox, ThemedListItemContent));
|
const ThemedListCheckbox = applyTheme(listCheckboxFactory(Checkbox, ThemedListItemContent));
|
||||||
const ThemedListItem = applyTheme(listItemFactory(ripple, ThemedListItemLayout, ThemedListItemContent));
|
const ThemedListItem = applyTheme(
|
||||||
|
listItemFactory(ripple, ThemedListItemLayout, ThemedListItemContent),
|
||||||
|
);
|
||||||
const ThemedList = applyTheme(listFactory(ThemedListItem));
|
const ThemedList = applyTheme(listFactory(ThemedListItem));
|
||||||
|
|
||||||
export { ThemedListItemActions as ListItemActions };
|
export { ThemedListItemActions as ListItemActions };
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { MENU } from '../identifiers.js';
|
import { MENU } from '../identifiers';
|
||||||
import InjectIconButton from '../button/IconButton.js';
|
import InjectIconButton from '../button/IconButton';
|
||||||
import InjectMenu from './Menu.js';
|
import InjectMenu from './Menu';
|
||||||
|
|
||||||
const factory = (IconButton, Menu) => {
|
const factory = (IconButton, Menu) => {
|
||||||
class IconMenu extends Component {
|
class IconMenu extends Component {
|
||||||
|
@ -12,7 +12,7 @@ const factory = (IconButton, Menu) => {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
icon: PropTypes.oneOfType([
|
icon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
iconRipple: PropTypes.bool,
|
iconRipple: PropTypes.bool,
|
||||||
menuRipple: PropTypes.bool,
|
menuRipple: PropTypes.bool,
|
||||||
|
@ -22,11 +22,11 @@ const factory = (IconButton, Menu) => {
|
||||||
onShow: PropTypes.func,
|
onShow: PropTypes.func,
|
||||||
position: PropTypes.string,
|
position: PropTypes.string,
|
||||||
selectable: PropTypes.bool,
|
selectable: PropTypes.bool,
|
||||||
selected: PropTypes.any,
|
selected: PropTypes.node,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
icon: PropTypes.string,
|
icon: PropTypes.string,
|
||||||
iconMenu: PropTypes.string
|
iconMenu: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -35,11 +35,11 @@ const factory = (IconButton, Menu) => {
|
||||||
iconRipple: true,
|
iconRipple: true,
|
||||||
menuRipple: true,
|
menuRipple: true,
|
||||||
position: 'auto',
|
position: 'auto',
|
||||||
selectable: false
|
selectable: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
active: false
|
active: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
handleButtonClick = (event) => {
|
handleButtonClick = (event) => {
|
||||||
|
@ -52,7 +52,7 @@ const factory = (IconButton, Menu) => {
|
||||||
if (this.props.onHide) this.props.onHide();
|
if (this.props.onHide) this.props.onHide();
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {
|
const {
|
||||||
children, className, icon, iconRipple, inverse, menuRipple, onHide, // eslint-disable-line
|
children, className, icon, iconRipple, inverse, menuRipple, onHide, // eslint-disable-line
|
||||||
onSelect, onShow, position, selectable, selected, theme, ...other
|
onSelect, onShow, position, selectable, selected, theme, ...other
|
||||||
|
|
|
@ -2,10 +2,10 @@ import React, { Component, PropTypes } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { MENU } from '../identifiers.js';
|
import { MENU } from '../identifiers';
|
||||||
import { events } from '../utils';
|
import { events } from '../utils';
|
||||||
import { getViewport } from '../utils/utils';
|
import { getViewport } from '../utils/utils';
|
||||||
import InjectMenuItem from './MenuItem.js';
|
import InjectMenuItem from './MenuItem';
|
||||||
|
|
||||||
const POSITION = {
|
const POSITION = {
|
||||||
AUTO: 'auto',
|
AUTO: 'auto',
|
||||||
|
@ -13,7 +13,7 @@ const POSITION = {
|
||||||
TOP_LEFT: 'topLeft',
|
TOP_LEFT: 'topLeft',
|
||||||
TOP_RIGHT: 'topRight',
|
TOP_RIGHT: 'topRight',
|
||||||
BOTTOM_LEFT: 'bottomLeft',
|
BOTTOM_LEFT: 'bottomLeft',
|
||||||
BOTTOM_RIGHT: 'bottomRight'
|
BOTTOM_RIGHT: 'bottomRight',
|
||||||
};
|
};
|
||||||
|
|
||||||
const factory = (MenuItem) => {
|
const factory = (MenuItem) => {
|
||||||
|
@ -29,7 +29,7 @@ const factory = (MenuItem) => {
|
||||||
position: PropTypes.oneOf(Object.keys(POSITION).map(key => POSITION[key])),
|
position: PropTypes.oneOf(Object.keys(POSITION).map(key => POSITION[key])),
|
||||||
ripple: PropTypes.bool,
|
ripple: PropTypes.bool,
|
||||||
selectable: PropTypes.bool,
|
selectable: PropTypes.bool,
|
||||||
selected: PropTypes.any,
|
selected: PropTypes.node,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
active: PropTypes.string,
|
active: PropTypes.string,
|
||||||
bottomLeft: PropTypes.string,
|
bottomLeft: PropTypes.string,
|
||||||
|
@ -40,8 +40,8 @@ const factory = (MenuItem) => {
|
||||||
rippled: PropTypes.string,
|
rippled: PropTypes.string,
|
||||||
static: PropTypes.string,
|
static: PropTypes.string,
|
||||||
topLeft: PropTypes.string,
|
topLeft: PropTypes.string,
|
||||||
topRight: PropTypes.string
|
topRight: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -49,17 +49,17 @@ const factory = (MenuItem) => {
|
||||||
outline: true,
|
outline: true,
|
||||||
position: POSITION.STATIC,
|
position: POSITION.STATIC,
|
||||||
ripple: true,
|
ripple: true,
|
||||||
selectable: true
|
selectable: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
active: this.props.active,
|
active: this.props.active,
|
||||||
rippled: false
|
rippled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
this.positionTimeoutHandle = setTimeout(() => {
|
this.positionTimeoutHandle = setTimeout(() => {
|
||||||
const { width, height } = this.refs.menu.getBoundingClientRect();
|
const { width, height } = this.menuNode.getBoundingClientRect();
|
||||||
const position = this.props.position === POSITION.AUTO
|
const position = this.props.position === POSITION.AUTO
|
||||||
? this.calculatePosition()
|
? this.calculatePosition()
|
||||||
: this.props.position;
|
: this.props.position;
|
||||||
|
@ -67,7 +67,7 @@ const factory = (MenuItem) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (this.props.position !== nextProps.position) {
|
if (this.props.position !== nextProps.position) {
|
||||||
const position = nextProps.position === POSITION.AUTO
|
const position = nextProps.position === POSITION.AUTO
|
||||||
? this.calculatePosition()
|
? this.calculatePosition()
|
||||||
|
@ -104,64 +104,39 @@ const factory = (MenuItem) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUpdate (nextProps, nextState) {
|
componentWillUpdate(nextProps, nextState) {
|
||||||
if (!this.state.active && nextState.active) {
|
if (!this.state.active && nextState.active) {
|
||||||
events.addEventsToDocument({
|
events.addEventsToDocument({
|
||||||
click: this.handleDocumentClick,
|
click: this.handleDocumentClick,
|
||||||
touchstart: this.handleDocumentClick
|
touchstart: this.handleDocumentClick,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate (prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
if (prevState.active && !this.state.active) {
|
if (prevState.active && !this.state.active) {
|
||||||
if (this.props.onHide) this.props.onHide();
|
if (this.props.onHide) this.props.onHide();
|
||||||
events.removeEventsFromDocument({
|
events.removeEventsFromDocument({
|
||||||
click: this.handleDocumentClick,
|
click: this.handleDocumentClick,
|
||||||
touchstart: this.handleDocumentClick
|
touchstart: this.handleDocumentClick,
|
||||||
});
|
});
|
||||||
} else if (!prevState.active && this.state.active && this.props.onShow) {
|
} else if (!prevState.active && this.state.active && this.props.onShow) {
|
||||||
this.props.onShow();
|
this.props.onShow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
if (this.state.active) {
|
if (this.state.active) {
|
||||||
events.removeEventsFromDocument({
|
events.removeEventsFromDocument({
|
||||||
click: this.handleDocumentClick,
|
click: this.handleDocumentClick,
|
||||||
touchstart: this.handleDocumentClick
|
touchstart: this.handleDocumentClick,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
clearTimeout(this.positionTimeoutHandle);
|
clearTimeout(this.positionTimeoutHandle);
|
||||||
clearTimeout(this.activateTimeoutHandle);
|
clearTimeout(this.activateTimeoutHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDocumentClick = (event) => {
|
getMenuStyle() {
|
||||||
if (this.state.active && !events.targetIsDescendant(event, ReactDOM.findDOMNode(this))) {
|
|
||||||
this.setState({active: false, rippled: false});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleSelect = (item, event) => {
|
|
||||||
const { value, onClick } = item.props;
|
|
||||||
if (onClick) event.persist();
|
|
||||||
this.setState({ active: false, rippled: this.props.ripple }, () => {
|
|
||||||
if (onClick) onClick(event);
|
|
||||||
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} = 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;
|
const { width, height, position } = this.state;
|
||||||
if (position !== POSITION.STATIC) {
|
if (position !== POSITION.STATIC) {
|
||||||
if (this.state.active) {
|
if (this.state.active) {
|
||||||
|
@ -176,50 +151,82 @@ const factory = (MenuItem) => {
|
||||||
return { clip: 'rect(0 0 0 0)' };
|
return { clip: 'rect(0 0 0 0)' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRootStyle () {
|
getRootStyle() {
|
||||||
if (this.state.position !== POSITION.STATIC) {
|
return this.state.position !== POSITION.STATIC
|
||||||
return { width: this.state.width, height: this.state.height };
|
? { width: this.state.width, height: this.state.height }
|
||||||
}
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItems () {
|
calculatePosition() {
|
||||||
|
const parentNode = ReactDOM.findDOMNode(this).parentNode;
|
||||||
|
if (!parentNode) return undefined;
|
||||||
|
const { top, left, height, width } = parentNode.getBoundingClientRect();
|
||||||
|
const { height: wh, width: ww } = getViewport();
|
||||||
|
const toTop = top < ((wh / 2) - (height / 2));
|
||||||
|
const toLeft = left < ((ww / 2) - (width / 2));
|
||||||
|
return `${toTop ? 'top' : 'bottom'}${toLeft ? 'Left' : 'Right'}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDocumentClick = (event) => {
|
||||||
|
if (this.state.active && !events.targetIsDescendant(event, ReactDOM.findDOMNode(this))) {
|
||||||
|
this.setState({ active: false, rippled: false });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSelect = (item, event) => {
|
||||||
|
const { value, onClick } = item.props;
|
||||||
|
if (onClick) event.persist();
|
||||||
|
this.setState({ active: false, rippled: this.props.ripple }, () => {
|
||||||
|
if (onClick) onClick(event);
|
||||||
|
if (this.props.onSelect) this.props.onSelect(value);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
show() {
|
||||||
|
const { width, height } = this.menuNode.getBoundingClientRect();
|
||||||
|
this.setState({ active: true, width, height });
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.setState({ active: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
renderItems() {
|
||||||
return React.Children.map(this.props.children, (item) => {
|
return React.Children.map(this.props.children, (item) => {
|
||||||
if (!item) return item;
|
if (!item) return item;
|
||||||
if (item.type === MenuItem) {
|
if (item.type === MenuItem) {
|
||||||
return React.cloneElement(item, {
|
return React.cloneElement(item, {
|
||||||
ripple: item.props.ripple || this.props.ripple,
|
ripple: item.props.ripple || this.props.ripple,
|
||||||
selected: typeof item.props.value !== 'undefined' && this.props.selectable && item.props.value === this.props.selected,
|
selected: typeof item.props.value !== 'undefined'
|
||||||
onClick: this.handleSelect.bind(this, item)
|
&& this.props.selectable
|
||||||
|
&& item.props.value === this.props.selected,
|
||||||
|
onClick: this.handleSelect.bind(this, item),
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
return React.cloneElement(item);
|
return React.cloneElement(item);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
show () {
|
render() {
|
||||||
const { width, height } = this.refs.menu.getBoundingClientRect();
|
|
||||||
this.setState({active: true, width, height});
|
|
||||||
}
|
|
||||||
|
|
||||||
hide () {
|
|
||||||
this.setState({active: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
const outlineStyle = { width: this.state.width, height: this.state.height };
|
const outlineStyle = { width: this.state.width, height: this.state.height };
|
||||||
const className = classnames([theme.menu, theme[this.state.position]], {
|
const className = classnames([theme.menu, theme[this.state.position]], {
|
||||||
[theme.active]: this.state.active,
|
[theme.active]: this.state.active,
|
||||||
[theme.rippled]: this.state.rippled
|
[theme.rippled]: this.state.rippled,
|
||||||
}, this.props.className);
|
}, this.props.className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox='menu' className={className} style={this.getRootStyle()}>
|
<div data-react-toolbox="menu" className={className} style={this.getRootStyle()}>
|
||||||
{this.props.outline ? <div className={theme.outline} style={outlineStyle} /> : null}
|
{this.props.outline ? <div className={theme.outline} style={outlineStyle} /> : null}
|
||||||
<ul ref='menu' className={theme.menuInner} style={this.getMenuStyle()}>
|
<ul
|
||||||
|
ref={(node) => { this.menuNode = node; }}
|
||||||
|
className={theme.menuInner}
|
||||||
|
style={this.getMenuStyle()}
|
||||||
|
>
|
||||||
{this.renderItems()}
|
{this.renderItems()}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { MENU } from '../identifiers.js';
|
import { MENU } from '../identifiers';
|
||||||
|
|
||||||
const MenuDivider = ({ theme }) => (
|
const MenuDivider = ({ theme }) => (
|
||||||
<hr data-react-toolbox='menu-divider' className={theme.menuDivider}/>
|
<hr data-react-toolbox="menu-divider" className={theme.menuDivider} />
|
||||||
);
|
);
|
||||||
|
|
||||||
MenuDivider.propTypes = {
|
MenuDivider.propTypes = {
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
menuDivider: PropTypes.string
|
menuDivider: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default themr(MENU)(MenuDivider);
|
export default themr(MENU)(MenuDivider);
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { MENU } from '../identifiers.js';
|
import { MENU } from '../identifiers';
|
||||||
import FontIcon from '../font_icon/FontIcon.js';
|
import { FontIcon } from '../font_icon/FontIcon';
|
||||||
import rippleFactory from '../ripple/Ripple.js';
|
import rippleFactory from '../ripple/Ripple';
|
||||||
|
|
||||||
const factory = (ripple) => {
|
const factory = (ripple) => {
|
||||||
class MenuItem extends Component {
|
class MenuItem extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
caption: PropTypes.string,
|
caption: PropTypes.string,
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
icon: PropTypes.oneOfType([
|
icon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
selected: PropTypes.bool,
|
selected: PropTypes.bool,
|
||||||
|
@ -25,14 +25,14 @@ const factory = (ripple) => {
|
||||||
icon: PropTypes.string,
|
icon: PropTypes.string,
|
||||||
menuItem: PropTypes.string,
|
menuItem: PropTypes.string,
|
||||||
selected: PropTypes.string,
|
selected: PropTypes.string,
|
||||||
shortcut: PropTypes.string
|
shortcut: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
className: '',
|
className: '',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
selected: false
|
selected: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = (event) => {
|
handleClick = (event) => {
|
||||||
|
@ -41,16 +41,25 @@ const factory = (ripple) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {icon, caption, children, shortcut, selected, disabled, theme, ...others} = this.props;
|
const {
|
||||||
|
caption,
|
||||||
|
children,
|
||||||
|
disabled,
|
||||||
|
icon,
|
||||||
|
selected,
|
||||||
|
shortcut,
|
||||||
|
theme,
|
||||||
|
...others
|
||||||
|
} = this.props;
|
||||||
const className = classnames(theme.menuItem, {
|
const className = classnames(theme.menuItem, {
|
||||||
[theme.selected]: selected,
|
[theme.selected]: selected,
|
||||||
[theme.disabled]: disabled
|
[theme.disabled]: disabled,
|
||||||
}, this.props.className);
|
}, this.props.className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li {...others} data-react-toolbox='menu-item' className={className} onClick={this.handleClick}>
|
<li {...others} data-react-toolbox="menu-item" className={className} onClick={this.handleClick}>
|
||||||
{icon ? <FontIcon value={icon} className={theme.icon}/> : null}
|
{icon ? <FontIcon value={icon} className={theme.icon} /> : null}
|
||||||
<span className={theme.caption}>{caption}</span>
|
<span className={theme.caption}>{caption}</span>
|
||||||
{shortcut ? <small className={theme.shortcut}>{shortcut}</small> : null}
|
{shortcut ? <small className={theme.shortcut}>{shortcut}</small> : null}
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
|
/* eslint-disable */
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import ReactTestUtils from 'react-addons-test-utils';
|
import ReactTestUtils from 'react-addons-test-utils';
|
||||||
import Menu from '../Menu';
|
import Menu from '../Menu';
|
||||||
import MenuItem, {MenuItem as RawMenuItem} from '../MenuItem';
|
import MenuItem, { MenuItem as RawMenuItem } from '../MenuItem';
|
||||||
|
|
||||||
describe('MenuItem', function () {
|
describe('MenuItem', () => {
|
||||||
describe('#onClick', function () {
|
describe('#onClick', () => {
|
||||||
it('passes to listener the event', function () {
|
it('passes to listener the event', () => {
|
||||||
let listenerCalled = false;
|
let listenerCalled = false;
|
||||||
const handleClick = function (event) {
|
const handleClick = function (event) {
|
||||||
listenerCalled = true;
|
listenerCalled = true;
|
||||||
|
@ -17,7 +18,7 @@ describe('MenuItem', function () {
|
||||||
|
|
||||||
const tree = ReactTestUtils.renderIntoDocument(
|
const tree = ReactTestUtils.renderIntoDocument(
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuItem key="1" onClick={handleClick}/>
|
<MenuItem key="1" onClick={handleClick} />
|
||||||
</Menu>);
|
</Menu>);
|
||||||
|
|
||||||
const menuItem = ReactTestUtils.findRenderedComponentWithType(tree, RawMenuItem);
|
const menuItem = ReactTestUtils.findRenderedComponentWithType(tree, RawMenuItem);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { MENU } from '../identifiers.js';
|
import { MENU } from '../identifiers';
|
||||||
import { IconButton } from '../button';
|
import { IconButton } from '../button';
|
||||||
import { MenuDivider } from './MenuDivider.js';
|
import { MenuDivider } from './MenuDivider';
|
||||||
import { menuItemFactory } from './MenuItem.js';
|
import { menuItemFactory } from './MenuItem';
|
||||||
import { menuFactory } from './Menu.js';
|
import { menuFactory } from './Menu';
|
||||||
import { iconMenuFactory } from './IconMenu.js';
|
import { iconMenuFactory } from './IconMenu';
|
||||||
import themedRippleFactory from '../ripple';
|
import themedRippleFactory from '../ripple';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const applyTheme = (Component) => themr(MENU, theme)(Component);
|
const applyTheme = Component => themr(MENU, theme)(Component);
|
||||||
const ThemedMenuDivider = applyTheme(MenuDivider);
|
const ThemedMenuDivider = applyTheme(MenuDivider);
|
||||||
const ThemedMenuItem = applyTheme(menuItemFactory(themedRippleFactory({})));
|
const ThemedMenuItem = applyTheme(menuItemFactory(themedRippleFactory({})));
|
||||||
const ThemedMenu = applyTheme(menuFactory(ThemedMenuItem));
|
const ThemedMenu = applyTheme(menuFactory(ThemedMenuItem));
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { NAVIGATION } from '../identifiers.js';
|
import { NAVIGATION } from '../identifiers';
|
||||||
import InjectButton from '../button/Button.js';
|
import InjectButton from '../button/Button';
|
||||||
import InjectLink from '../link/Link.js';
|
import InjectLink from '../link/Link';
|
||||||
|
|
||||||
const factory = (Button, Link) => {
|
const factory = (Button, Link) => {
|
||||||
const Navigation = ({ actions, children, className, routes, theme, type }) => {
|
const Navigation = ({ actions, children, className, routes, theme, type }) => {
|
||||||
const _className = classnames(theme[type], className);
|
const _className = classnames(theme[type], className);
|
||||||
const buttons = actions.map((action, index) => {
|
const buttons = actions.map((action, index) => (
|
||||||
return <Button className={theme.button} key={index} {...action} />;
|
<Button className={theme.button} key={index} {...action} /> // eslint-disable-line
|
||||||
});
|
));
|
||||||
|
|
||||||
const links = routes.map((route, index) => {
|
const links = routes.map((route, index) => (
|
||||||
return <Link className={theme.link} key={index} {...route} />;
|
<Link className={theme.link} key={index} {...route} /> // eslint-disable-line
|
||||||
});
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav data-react-toolbox='navigation' className={_className}>
|
<nav data-react-toolbox="navigation" className={_className}>
|
||||||
{links}
|
{links}
|
||||||
{buttons}
|
{buttons}
|
||||||
{children}
|
{children}
|
||||||
|
@ -26,24 +26,24 @@ const factory = (Button, Link) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
Navigation.propTypes = {
|
Navigation.propTypes = {
|
||||||
actions: PropTypes.array,
|
actions: PropTypes.array, // eslint-disable-line
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
routes: PropTypes.array,
|
routes: PropTypes.array, // eslint-disable-line
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
button: PropTypes.string,
|
button: PropTypes.string,
|
||||||
horizontal: PropTypes.string,
|
horizontal: PropTypes.string,
|
||||||
link: PropTypes.string,
|
link: PropTypes.string,
|
||||||
vertical: PropTypes.string
|
vertical: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
type: PropTypes.oneOf(['vertical', 'horizontal'])
|
type: PropTypes.oneOf(['vertical', 'horizontal']),
|
||||||
};
|
};
|
||||||
|
|
||||||
Navigation.defaultProps = {
|
Navigation.defaultProps = {
|
||||||
actions: [],
|
actions: [],
|
||||||
className: '',
|
className: '',
|
||||||
type: 'horizontal',
|
type: 'horizontal',
|
||||||
routes: []
|
routes: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
return Navigation;
|
return Navigation;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { NAVIGATION } from '../identifiers.js';
|
import { NAVIGATION } from '../identifiers';
|
||||||
import { navigationFactory } from './Navigation.js';
|
import { navigationFactory } from './Navigation';
|
||||||
import { Button } from '../button';
|
import { Button } from '../button';
|
||||||
import { Link } from '../link';
|
import { Link } from '../link';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { OVERLAY } from '../identifiers.js';
|
import { OVERLAY } from '../identifiers';
|
||||||
|
|
||||||
class Overlay extends Component {
|
class Overlay extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -14,21 +14,21 @@ class Overlay extends Component {
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
active: PropTypes.string,
|
active: PropTypes.string,
|
||||||
backdrop: PropTypes.string,
|
backdrop: PropTypes.string,
|
||||||
overlay: PropTypes.string
|
overlay: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
lockScroll: true
|
lockScroll: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
const { active, lockScroll, onEscKeyDown } = this.props;
|
const { active, lockScroll, onEscKeyDown } = this.props;
|
||||||
if (onEscKeyDown) document.body.addEventListener('keydown', this.handleEscKey.bind(this));
|
if (onEscKeyDown) document.body.addEventListener('keydown', this.handleEscKey.bind(this));
|
||||||
if (active && lockScroll) document.body.style.overflow = 'hidden';
|
if (active && lockScroll) document.body.style.overflow = 'hidden';
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUpdate (nextProps) {
|
componentWillUpdate(nextProps) {
|
||||||
if (this.props.lockScroll) {
|
if (this.props.lockScroll) {
|
||||||
const becomingActive = nextProps.active && !this.props.active;
|
const becomingActive = nextProps.active && !this.props.active;
|
||||||
const becomingUnactive = !nextProps.active && this.props.active;
|
const becomingUnactive = !nextProps.active && this.props.active;
|
||||||
|
@ -43,13 +43,13 @@ class Overlay extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (this.props.active && !prevProps.active && this.props.onEscKeyDown) {
|
if (this.props.active && !prevProps.active && this.props.onEscKeyDown) {
|
||||||
document.body.addEventListener('keydown', this.handleEscKey.bind(this));
|
document.body.addEventListener('keydown', this.handleEscKey.bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
if (this.props.active && this.props.lockScroll) {
|
if (this.props.active && this.props.lockScroll) {
|
||||||
if (!document.querySelectorAll('[data-react-toolbox="overlay"]')[1]) {
|
if (!document.querySelectorAll('[data-react-toolbox="overlay"]')[1]) {
|
||||||
document.body.style.overflow = '';
|
document.body.style.overflow = '';
|
||||||
|
@ -67,7 +67,7 @@ class Overlay extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick = event => {
|
handleClick = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if (this.props.onClick) {
|
if (this.props.onClick) {
|
||||||
|
@ -75,14 +75,14 @@ class Overlay extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { active, className, lockScroll, theme, onEscKeyDown, ...other } = this.props; // eslint-disable-line
|
const { active, className, lockScroll, theme, onEscKeyDown, ...other } = this.props; // eslint-disable-line
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
{...other}
|
{...other}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
className={classnames(theme.overlay, {
|
className={classnames(theme.overlay, {
|
||||||
[theme.active]: active
|
[theme.active]: active,
|
||||||
}, className)}
|
}, className)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { OVERLAY } from '../identifiers.js';
|
import { OVERLAY } from '../identifiers';
|
||||||
import { Overlay } from './Overlay.js';
|
import { Overlay } from './Overlay';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const ThemedOverlay = themr(OVERLAY, theme)(Overlay);
|
const ThemedOverlay = themr(OVERLAY, theme)(Overlay);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { PROGRESS_BAR } from '../identifiers.js';
|
import { PROGRESS_BAR } from '../identifiers';
|
||||||
import prefixer from '../utils/prefixer.js';
|
import prefixer from '../utils/prefixer';
|
||||||
|
|
||||||
class ProgressBar extends Component {
|
class ProgressBar extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -21,10 +21,10 @@ class ProgressBar extends Component {
|
||||||
linear: PropTypes.string,
|
linear: PropTypes.string,
|
||||||
multicolor: PropTypes.string,
|
multicolor: PropTypes.string,
|
||||||
path: PropTypes.string,
|
path: PropTypes.string,
|
||||||
value: PropTypes.string
|
value: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
type: PropTypes.oneOf(['linear', 'circular']),
|
type: PropTypes.oneOf(['linear', 'circular']),
|
||||||
value: PropTypes.number
|
value: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -35,61 +35,60 @@ class ProgressBar extends Component {
|
||||||
mode: 'indeterminate',
|
mode: 'indeterminate',
|
||||||
multicolor: false,
|
multicolor: false,
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
value: 0
|
value: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
calculateRatio (value) {
|
calculateRatio(value) {
|
||||||
if (value < this.props.min) return 0;
|
if (value < this.props.min) return 0;
|
||||||
if (value > this.props.max) return 1;
|
if (value > this.props.max) return 1;
|
||||||
return (value - this.props.min) / (this.props.max - this.props.min);
|
return (value - this.props.min) / (this.props.max - this.props.min);
|
||||||
}
|
}
|
||||||
|
|
||||||
circularStyle () {
|
circularStyle() {
|
||||||
if (this.props.mode !== 'indeterminate') {
|
return this.props.mode !== 'indeterminate'
|
||||||
return {strokeDasharray: `${2 * Math.PI * 25 * this.calculateRatio(this.props.value)}, 400`};
|
? { strokeDasharray: `${2 * Math.PI * 25 * this.calculateRatio(this.props.value)}, 400` }
|
||||||
}
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
linearStyle () {
|
linearStyle() {
|
||||||
if (this.props.mode !== 'indeterminate') {
|
if (this.props.mode !== 'indeterminate') {
|
||||||
return {
|
return {
|
||||||
buffer: prefixer({transform: `scaleX(${this.calculateRatio(this.props.buffer)})`}),
|
buffer: prefixer({ transform: `scaleX(${this.calculateRatio(this.props.buffer)})` }),
|
||||||
value: prefixer({transform: `scaleX(${this.calculateRatio(this.props.value)})`})
|
value: prefixer({ transform: `scaleX(${this.calculateRatio(this.props.value)})` }),
|
||||||
};
|
};
|
||||||
} else {
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
renderCircular () {
|
renderCircular() {
|
||||||
return (
|
return (
|
||||||
<svg className={this.props.theme.circle} viewBox="0 0 60 60">
|
<svg className={this.props.theme.circle} viewBox="0 0 60 60">
|
||||||
<circle className={this.props.theme.path} style={this.circularStyle()} cx='30' cy='30' r='25' />
|
<circle className={this.props.theme.path} style={this.circularStyle()} cx="30" cy="30" r="25" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLinear () {
|
renderLinear() {
|
||||||
const {buffer, value} = this.linearStyle();
|
const { buffer, value } = this.linearStyle();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<span ref='buffer' data-ref='buffer' className={this.props.theme.buffer} style={buffer}/>
|
<span data-ref="buffer" className={this.props.theme.buffer} style={buffer} />
|
||||||
<span ref='value' data-ref='value' className={this.props.theme.value} style={value}/>
|
<span data-ref="value" className={this.props.theme.value} style={value} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { className, disabled, max, min, mode, multicolor, type, theme, value } = this.props;
|
const { className, disabled, max, min, mode, multicolor, type, theme, value } = this.props;
|
||||||
const _className = classnames(theme[type], {
|
const _className = classnames(theme[type], {
|
||||||
[theme[mode]]: mode,
|
[theme[mode]]: mode,
|
||||||
[theme.multicolor]: multicolor
|
[theme.multicolor]: multicolor,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
data-react-toolbox='progress-bar'
|
data-react-toolbox="progress-bar"
|
||||||
aria-valuenow={value}
|
aria-valuenow={value}
|
||||||
aria-valuemin={min}
|
aria-valuemin={min}
|
||||||
aria-valuemax={max}
|
aria-valuemax={max}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import TestUtils from 'react-addons-test-utils';
|
import TestUtils from 'react-addons-test-utils';
|
||||||
|
@ -5,61 +6,65 @@ import ProgressBar, { ProgressBar as RawProgressBar } from '../ProgressBar';
|
||||||
import theme from '../theme.css';
|
import theme from '../theme.css';
|
||||||
import utils from '../../utils/testing';
|
import utils from '../../utils/testing';
|
||||||
|
|
||||||
describe('ProgressBar', function () {
|
describe('ProgressBar', () => {
|
||||||
let progressBar;
|
let progressBar;
|
||||||
|
|
||||||
describe('#calculateRatio', function () {
|
describe('#calculateRatio', () => {
|
||||||
before(function () {
|
before(() => {
|
||||||
const tree = TestUtils.renderIntoDocument(<ProgressBar min={100} max={300} theme={theme} />);
|
const tree = TestUtils.renderIntoDocument(<ProgressBar min={100} max={300} theme={theme} />);
|
||||||
progressBar = TestUtils.findRenderedComponentWithType(tree, RawProgressBar);
|
progressBar = TestUtils.findRenderedComponentWithType(tree, RawProgressBar);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calculates the right ratio', function () {
|
it('calculates the right ratio', () => {
|
||||||
expect(progressBar.calculateRatio(150)).toEqual(0.25);
|
expect(progressBar.calculateRatio(150)).toEqual(0.25);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets 0 when value is less than min', function () {
|
it('gets 0 when value is less than min', () => {
|
||||||
expect(progressBar.calculateRatio(10)).toEqual(0);
|
expect(progressBar.calculateRatio(10)).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets 1 when value is more than max', function () {
|
it('gets 1 when value is more than max', () => {
|
||||||
expect(progressBar.calculateRatio(400)).toEqual(1);
|
expect(progressBar.calculateRatio(400)).toEqual(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#render', function () {
|
describe('#render', () => {
|
||||||
let buffer, value, wrapper, circle, strokeLength;
|
let buffer,
|
||||||
|
value,
|
||||||
|
wrapper,
|
||||||
|
circle,
|
||||||
|
strokeLength;
|
||||||
|
|
||||||
it('renders the value and buffer bars when it is linear', function () {
|
it('renders the value and buffer bars when it is linear', () => {
|
||||||
wrapper = utils.shallowRenderComponent(RawProgressBar, {theme}).props.children;
|
wrapper = utils.shallowRenderComponent(RawProgressBar, { theme }).props.children;
|
||||||
expect(wrapper.props.children.length).toEqual(2);
|
expect(wrapper.props.children.length).toEqual(2);
|
||||||
expect(wrapper.props.children[0].ref).toEqual('buffer');
|
expect(wrapper.props.children[0].ref).toEqual('buffer');
|
||||||
expect(wrapper.props.children[1].ref).toEqual('value');
|
expect(wrapper.props.children[1].ref).toEqual('value');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the value and buffer bars when it is linear', function () {
|
it('renders the value and buffer bars when it is linear', () => {
|
||||||
progressBar = utils.shallowRenderComponent(RawProgressBar, {mode: 'determinate', value: 30, buffer: 60, theme});
|
progressBar = utils.shallowRenderComponent(RawProgressBar, { mode: 'determinate', value: 30, buffer: 60, theme });
|
||||||
buffer = (progressBar.props.children.props.children[0]);
|
buffer = (progressBar.props.children.props.children[0]);
|
||||||
value = (progressBar.props.children.props.children[1]);
|
value = (progressBar.props.children.props.children[1]);
|
||||||
expect(buffer.props.style.transform).toEqual(`scaleX(${0.6})`);
|
expect(buffer.props.style.transform).toEqual(`scaleX(${0.6})`);
|
||||||
expect(value.props.style.transform).toEqual(`scaleX(${0.3})`);
|
expect(value.props.style.transform).toEqual(`scaleX(${0.3})`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the svg circle when it is circular', function () {
|
it('renders the svg circle when it is circular', () => {
|
||||||
progressBar = utils.shallowRenderComponent(RawProgressBar, {type: 'circular', theme});
|
progressBar = utils.shallowRenderComponent(RawProgressBar, { type: 'circular', theme });
|
||||||
expect(progressBar.props.children.type).toEqual('svg');
|
expect(progressBar.props.children.type).toEqual('svg');
|
||||||
expect(progressBar.props.children.props.children.type).toEqual('circle');
|
expect(progressBar.props.children.props.children.type).toEqual('circle');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the proper circle length style when it is circular and determinate', function () {
|
it('renders the proper circle length style when it is circular and determinate', () => {
|
||||||
progressBar = utils.shallowRenderComponent(RawProgressBar, {type: 'circular', mode: 'determinate', value: 30, theme});
|
progressBar = utils.shallowRenderComponent(RawProgressBar, { type: 'circular', mode: 'determinate', value: 30, theme });
|
||||||
circle = progressBar.props.children.props.children;
|
circle = progressBar.props.children.props.children;
|
||||||
strokeLength = 2 * Math.PI * circle.props.r * 0.3;
|
strokeLength = 2 * Math.PI * circle.props.r * 0.3;
|
||||||
expect(circle.props.style.strokeDasharray).toEqual(`${strokeLength}, 400`);
|
expect(circle.props.style.strokeDasharray).toEqual(`${strokeLength}, 400`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains mode and className in its className', function () {
|
it('contains mode and className in its className', () => {
|
||||||
progressBar = utils.shallowRenderComponent(RawProgressBar, {mode: 'determinate', className: 'tight', theme});
|
progressBar = utils.shallowRenderComponent(RawProgressBar, { mode: 'determinate', className: 'tight', theme });
|
||||||
expect(progressBar.props.className).toContain(theme.determinate);
|
expect(progressBar.props.className).toContain(theme.determinate);
|
||||||
expect(progressBar.props.className).toContain(theme.tight);
|
expect(progressBar.props.className).toContain(theme.tight);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { PROGRESS_BAR } from '../identifiers.js';
|
import { PROGRESS_BAR } from '../identifiers';
|
||||||
import { ProgressBar } from './ProgressBar.js';
|
import { ProgressBar } from './ProgressBar';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const ThemedProgressBar = themr(PROGRESS_BAR, theme)(ProgressBar);
|
const ThemedProgressBar = themr(PROGRESS_BAR, theme)(ProgressBar);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
|
|
||||||
const factory = (ripple) => {
|
const factory = (ripple) => {
|
||||||
const Radio = ({checked, onMouseDown, theme, ...other}) => (
|
const Radio = ({ checked, onMouseDown, theme, ...other }) => (
|
||||||
<div
|
<div
|
||||||
data-react-toolbox='radio'
|
data-react-toolbox="radio"
|
||||||
className={theme[checked ? 'radioChecked' : 'radio']}
|
className={theme[checked ? 'radioChecked' : 'radio']}
|
||||||
onMouseDown={onMouseDown}
|
onMouseDown={onMouseDown}
|
||||||
{...other}
|
{...other}
|
||||||
|
@ -12,13 +12,13 @@ const factory = (ripple) => {
|
||||||
|
|
||||||
Radio.propTypes = {
|
Radio.propTypes = {
|
||||||
checked: PropTypes.bool,
|
checked: PropTypes.bool,
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
onMouseDown: PropTypes.func,
|
onMouseDown: PropTypes.func,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
radio: PropTypes.string,
|
radio: PropTypes.string,
|
||||||
radioChecked: PropTypes.string,
|
radioChecked: PropTypes.string,
|
||||||
ripple: PropTypes.string
|
ripple: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
return ripple(Radio);
|
return ripple(Radio);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { RADIO } from '../identifiers.js';
|
import { RADIO } from '../identifiers';
|
||||||
import rippleFactory from '../ripple/Ripple.js';
|
import rippleFactory from '../ripple/Ripple';
|
||||||
import radioFactory from './Radio.js';
|
import radioFactory from './Radio';
|
||||||
|
|
||||||
const factory = (Radio) => {
|
const factory = (Radio) => {
|
||||||
class RadioButton extends Component {
|
class RadioButton extends Component {
|
||||||
|
@ -14,7 +14,7 @@ const factory = (Radio) => {
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
label: PropTypes.oneOfType([
|
label: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.node
|
PropTypes.node,
|
||||||
]),
|
]),
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
onBlur: PropTypes.func,
|
onBlur: PropTypes.func,
|
||||||
|
@ -26,38 +26,54 @@ const factory = (Radio) => {
|
||||||
disabled: PropTypes.string,
|
disabled: PropTypes.string,
|
||||||
field: PropTypes.string,
|
field: PropTypes.string,
|
||||||
input: PropTypes.string,
|
input: PropTypes.string,
|
||||||
text: PropTypes.string
|
text: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
value: PropTypes.any
|
value: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
checked: false,
|
checked: false,
|
||||||
className: '',
|
className: '',
|
||||||
disabled: false
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = (event) => {
|
handleClick = (event) => {
|
||||||
const {checked, disabled, onChange} = this.props;
|
const { checked, disabled, onChange } = this.props;
|
||||||
if (event.pageX !== 0 && event.pageY !== 0) this.blur();
|
if (event.pageX !== 0 && event.pageY !== 0) this.blur();
|
||||||
if (!disabled && !checked && onChange) onChange(event, this);
|
if (!disabled && !checked && onChange) onChange(event, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
blur () {
|
blur() {
|
||||||
this.inputNode && this.inputNode.blur();
|
if (this.inputNode) {
|
||||||
|
this.inputNode.blur();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
focus () {
|
focus() {
|
||||||
this.inputNode && this.inputNode.focus();
|
if (this.inputNode) {
|
||||||
|
this.inputNode.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { checked, children, className, disabled, label, name, onChange, // eslint-disable-line
|
const {
|
||||||
onMouseEnter, onMouseLeave, theme, ...others } = this.props;
|
checked,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
disabled,
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
onChange, // eslint-disable-line
|
||||||
|
onMouseEnter,
|
||||||
|
onMouseLeave,
|
||||||
|
theme,
|
||||||
|
...others
|
||||||
|
} = this.props;
|
||||||
const _className = classnames(theme[this.props.disabled ? 'disabled' : 'field'], className);
|
const _className = classnames(theme[this.props.disabled ? 'disabled' : 'field'], className);
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
data-react-toolbox='radio-button'
|
htmlFor={name}
|
||||||
|
data-react-toolbox="radio-button"
|
||||||
className={_className}
|
className={_className}
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
|
@ -70,8 +86,8 @@ const factory = (Radio) => {
|
||||||
name={name}
|
name={name}
|
||||||
onChange={() => {}}
|
onChange={() => {}}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
ref={node => { this.inputNode = node; }}
|
ref={(node) => { this.inputNode = node; }}
|
||||||
type='radio'
|
type="radio"
|
||||||
/>
|
/>
|
||||||
<Radio checked={checked} disabled={disabled} theme={theme} />
|
<Radio checked={checked} disabled={disabled} theme={theme} />
|
||||||
{label ? <span className={theme.text}>{label}</span> : null}
|
{label ? <span className={theme.text}>{label}</span> : null}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { RADIO } from '../identifiers.js';
|
import { RADIO } from '../identifiers';
|
||||||
import InjectRadioButton from './RadioButton.js';
|
import InjectRadioButton from './RadioButton';
|
||||||
import { isComponentOfType } from '../utils/react.js';
|
import { isComponentOfType } from '../utils/react';
|
||||||
|
|
||||||
const factory = (RadioButton) => {
|
const factory = (RadioButton) => {
|
||||||
class RadioGroup extends Component {
|
class RadioGroup extends Component {
|
||||||
|
@ -10,35 +10,34 @@ const factory = (RadioButton) => {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
name: PropTypes.string,
|
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
value: PropTypes.any
|
value: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
className: '',
|
className: '',
|
||||||
disabled: false
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleChange = (value) => {
|
handleChange = (value) => {
|
||||||
if (this.props.onChange) this.props.onChange(value);
|
if (this.props.onChange) this.props.onChange(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderRadioButtons () {
|
renderRadioButtons() {
|
||||||
return React.Children.map(this.props.children, child => (
|
return React.Children.map(this.props.children, child => (
|
||||||
!isComponentOfType(RadioButton, child)
|
!isComponentOfType(RadioButton, child)
|
||||||
? child
|
? child
|
||||||
: React.cloneElement(child, {
|
: React.cloneElement(child, {
|
||||||
checked: child.props.value === this.props.value,
|
checked: child.props.value === this.props.value,
|
||||||
disabled: this.props.disabled || child.props.disabled,
|
disabled: this.props.disabled || child.props.disabled,
|
||||||
onChange: this.handleChange.bind(this, child.props.value)
|
onChange: this.handleChange.bind(this, child.props.value),
|
||||||
})
|
})
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div data-react-toolbox='radio-group' className={this.props.className}>
|
<div data-react-toolbox="radio-group" className={this.props.className}>
|
||||||
{this.renderRadioButtons()}
|
{this.renderRadioButtons()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { RADIO } from '../identifiers.js';
|
import { RADIO } from '../identifiers';
|
||||||
import themedRippleFactory from '../ripple';
|
import themedRippleFactory from '../ripple';
|
||||||
import radioFactory from './Radio.js';
|
import radioFactory from './Radio';
|
||||||
import { radioButtonFactory } from './RadioButton.js';
|
import { radioButtonFactory } from './RadioButton';
|
||||||
import { radioGroupFactory } from './RadioGroup.js';
|
import { radioGroupFactory } from './RadioGroup';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const ThemedRadio = radioFactory(themedRippleFactory({ centered: true, spread: 2.6}));
|
const ThemedRadio = radioFactory(themedRippleFactory({ centered: true, spread: 2.6 }));
|
||||||
const ThemedRadioButton = themr(RADIO, theme)(radioButtonFactory(ThemedRadio));
|
const ThemedRadioButton = themr(RADIO, theme)(radioButtonFactory(ThemedRadio));
|
||||||
const ThemedRadioGroup = themr(RADIO, theme)(radioGroupFactory(ThemedRadioButton));
|
const ThemedRadioGroup = themr(RADIO, theme)(radioGroupFactory(ThemedRadioButton));
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import ReactDOM from 'react-dom';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import dissoc from 'ramda/src/dissoc';
|
import dissoc from 'ramda/src/dissoc';
|
||||||
import { RIPPLE } from '../identifiers.js';
|
import { RIPPLE } from '../identifiers';
|
||||||
import events from '../utils/events';
|
import events from '../utils/events';
|
||||||
import prefixer from '../utils/prefixer';
|
import prefixer from '../utils/prefixer';
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ const defaults = {
|
||||||
multiple: true,
|
multiple: true,
|
||||||
passthrough: true,
|
passthrough: true,
|
||||||
spread: 2,
|
spread: 2,
|
||||||
theme: {}
|
theme: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const rippleFactory = (options = {}) => {
|
const rippleFactory = (options = {}) => {
|
||||||
|
@ -25,14 +25,16 @@ const rippleFactory = (options = {}) => {
|
||||||
spread: defaultSpread,
|
spread: defaultSpread,
|
||||||
theme: defaultTheme,
|
theme: defaultTheme,
|
||||||
...props
|
...props
|
||||||
} = {...defaults, ...options};
|
} = { ...defaults, ...options };
|
||||||
|
|
||||||
return ComposedComponent => {
|
return (ComposedComponent) => {
|
||||||
class RippledComponent extends Component {
|
class RippledComponent extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
|
onMouseDown: PropTypes.func,
|
||||||
onRippleEnded: PropTypes.func,
|
onRippleEnded: PropTypes.func,
|
||||||
|
onTouchStart: PropTypes.func,
|
||||||
ripple: PropTypes.bool,
|
ripple: PropTypes.bool,
|
||||||
rippleCentered: PropTypes.bool,
|
rippleCentered: PropTypes.bool,
|
||||||
rippleClassName: PropTypes.string,
|
rippleClassName: PropTypes.string,
|
||||||
|
@ -42,8 +44,8 @@ const rippleFactory = (options = {}) => {
|
||||||
ripple: PropTypes.string,
|
ripple: PropTypes.string,
|
||||||
rippleActive: PropTypes.string,
|
rippleActive: PropTypes.string,
|
||||||
rippleRestarting: PropTypes.string,
|
rippleRestarting: PropTypes.string,
|
||||||
rippleWrapper: PropTypes.string
|
rippleWrapper: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -52,43 +54,83 @@ const rippleFactory = (options = {}) => {
|
||||||
rippleCentered: defaultCentered,
|
rippleCentered: defaultCentered,
|
||||||
rippleClassName: defaultClassName,
|
rippleClassName: defaultClassName,
|
||||||
rippleMultiple: defaultMultiple,
|
rippleMultiple: defaultMultiple,
|
||||||
rippleSpread: defaultSpread
|
rippleSpread: defaultSpread,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
ripples: {}
|
ripples: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidUpdate (prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
// If a new ripple was just added, add a remove event listener to its animation
|
// If a new ripple was just added, add a remove event listener to its animation
|
||||||
if (Object.keys(prevState.ripples).length < Object.keys(this.state.ripples).length) {
|
if (Object.keys(prevState.ripples).length < Object.keys(this.state.ripples).length) {
|
||||||
this.addRippleRemoveEventListener(this.getLastKey());
|
this.addRippleRemoveEventListener(this.getLastKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
// Remove document event listeners for ripple if they still exists
|
// Remove document event listeners for ripple if they still exists
|
||||||
Object.keys(this.state.ripples).forEach(key => {
|
Object.keys(this.state.ripples).forEach((key) => {
|
||||||
this.state.ripples[key].endRipple();
|
this.state.ripples[key].endRipple();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an event listener to the reference with given key so when the animation transition
|
* Find out a descriptor object for the ripple element being created depending on
|
||||||
* ends we can be sure that it finished and it can be safely removed from the state.
|
* the position where the it was triggered and the component's dimensions.
|
||||||
* This function is called whenever a new ripple is added to the component.
|
|
||||||
*
|
*
|
||||||
* @param {String} rippleKey Is the key of the ripple to add the event.
|
* @param {Number} x Coordinate x in the viewport where ripple was triggered
|
||||||
|
* @param {Number} y Coordinate y in the viewport where ripple was triggered
|
||||||
|
* @return {Object} Descriptor element including position and size of the element
|
||||||
*/
|
*/
|
||||||
addRippleRemoveEventListener (rippleKey) {
|
getDescriptor(x, y) {
|
||||||
const self = this;
|
const { left, top, height, width } = ReactDOM.findDOMNode(this).getBoundingClientRect();
|
||||||
events.addEventListenerOnTransitionEnded(this.refs[rippleKey], function onOpacityEnd (e) {
|
const { rippleCentered: centered, rippleSpread: spread } = this.props;
|
||||||
if (e.propertyName === 'opacity') {
|
return {
|
||||||
if (self.props.onRippleEnded) self.props.onRippleEnded(e);
|
left: centered ? 0 : x - left - (width / 2),
|
||||||
events.removeEventListenerOnTransitionEnded(self.refs[rippleKey], onOpacityEnd);
|
top: centered ? 0 : y - top - (height / 2),
|
||||||
self.setState({ ripples: dissoc(rippleKey, self.state.ripples) });
|
width: width * spread,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
/**
|
||||||
|
* Increments and internal counter and returns the next value as a string. It
|
||||||
|
* is used to assign key references to new ripple elements.
|
||||||
|
*
|
||||||
|
* @return {String} Key to be assigned to a ripple.
|
||||||
|
*/
|
||||||
|
getNextKey() {
|
||||||
|
this.currentCount = this.currentCount ? this.currentCount + 1 : 1;
|
||||||
|
return `ripple${this.currentCount}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the last generated key for a ripple element. When there is only one ripple
|
||||||
|
* and to get the reference when a ripple was just created.
|
||||||
|
*
|
||||||
|
* @return {String} The last generated ripple key.
|
||||||
|
*/
|
||||||
|
getLastKey() {
|
||||||
|
return `ripple${this.currentCount}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variable to store the ripple references
|
||||||
|
*/
|
||||||
|
rippleNodes = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a ripple should start depending if its a touch event. For mobile both
|
||||||
|
* touchStart and mouseDown are launched so in case is touch we should always trigger
|
||||||
|
* but if its not we should check if a touch was already triggered to decide.
|
||||||
|
*
|
||||||
|
* @param {Boolean} isTouch True in case a touch event triggered the ripple false otherwise.
|
||||||
|
* @return {Boolean} True in case the ripple should trigger or false if it shouldn't.
|
||||||
|
*/
|
||||||
|
rippleShouldTrigger(isTouch) {
|
||||||
|
const shouldStart = isTouch ? true : !this.touchCache;
|
||||||
|
this.touchCache = isTouch;
|
||||||
|
return shouldStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,76 +144,46 @@ const rippleFactory = (options = {}) => {
|
||||||
* @param {Number} y Coordinate Y on the screen where animation should start
|
* @param {Number} y Coordinate Y on the screen where animation should start
|
||||||
* @param {Boolean} isTouch Use events from touch or mouse.
|
* @param {Boolean} isTouch Use events from touch or mouse.
|
||||||
*/
|
*/
|
||||||
animateRipple (x, y, isTouch) {
|
animateRipple(x, y, isTouch) {
|
||||||
if (this.rippleShouldTrigger(isTouch)) {
|
if (this.rippleShouldTrigger(isTouch)) {
|
||||||
const { top, left, width } = this.getDescriptor(x, y);
|
const { top, left, width } = this.getDescriptor(x, y);
|
||||||
const noRipplesActive = Object.keys(this.state.ripples).length === 0;
|
const noRipplesActive = Object.keys(this.state.ripples).length === 0;
|
||||||
const key = this.props.rippleMultiple || noRipplesActive ? this.getNextKey() : this.getLastKey();
|
const key = (this.props.rippleMultiple || noRipplesActive)
|
||||||
|
? this.getNextKey()
|
||||||
|
: this.getLastKey();
|
||||||
const endRipple = this.addRippleDeactivateEventListener(isTouch, key);
|
const endRipple = this.addRippleDeactivateEventListener(isTouch, key);
|
||||||
const initialState = { active: false, restarting: true, top, left, width, endRipple };
|
const initialState = { active: false, restarting: true, top, left, width, endRipple };
|
||||||
const runningState = { active: true, restarting: false };
|
const runningState = { active: true, restarting: false };
|
||||||
const ripples = {...this.state.ripples, [key]: initialState };
|
const ripples = { ...this.state.ripples, [key]: initialState };
|
||||||
this.setState({ ripples }, () => {
|
this.setState({ ripples }, () => {
|
||||||
if (this.refs[key]) this.refs[key].offsetWidth; //eslint-disable-line no-unused-expressions
|
if (this.rippleNodes[key]) this.rippleNodes[key].offsetWidth; // eslint-disable-line
|
||||||
this.setState({ ripples: {
|
this.setState({ ripples: {
|
||||||
...this.state.ripples,
|
...this.state.ripples,
|
||||||
[key]: Object.assign({}, this.state.ripples[key], runningState)
|
[key]: Object.assign({}, this.state.ripples[key], runningState),
|
||||||
} });
|
} });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if a ripple should start depending if its a touch event. For mobile both
|
* Add an event listener to the reference with given key so when the animation transition
|
||||||
* touchStart and mouseDown are launched so in case is touch we should always trigger
|
* ends we can be sure that it finished and it can be safely removed from the state.
|
||||||
* but if its not we should check if a touch was already triggered to decide.
|
* This function is called whenever a new ripple is added to the component.
|
||||||
*
|
*
|
||||||
* @param {Boolean} isTouch True in case a touch event triggered the ripple false otherwise.
|
* @param {String} rippleKey Is the key of the ripple to add the event.
|
||||||
* @return {Boolean} True in case the ripple should trigger or false if it shouldn't.
|
|
||||||
*/
|
*/
|
||||||
rippleShouldTrigger (isTouch) {
|
addRippleRemoveEventListener(rippleKey) {
|
||||||
const shouldStart = isTouch ? true : !this.touchCache;
|
const self = this;
|
||||||
this.touchCache = isTouch;
|
const rippleNode = this.rippleNodes[rippleKey];
|
||||||
return shouldStart;
|
events.addEventListenerOnTransitionEnded(rippleNode, function onOpacityEnd(e) {
|
||||||
|
if (e.propertyName === 'opacity') {
|
||||||
|
if (self.props.onRippleEnded) self.props.onRippleEnded(e);
|
||||||
|
events.removeEventListenerOnTransitionEnded(self.rippleNodes[rippleKey], onOpacityEnd);
|
||||||
|
// self.rippleNodes = dissoc(rippleKey, self.rippleNodes);
|
||||||
|
delete self.rippleNodes[rippleKey];
|
||||||
|
self.setState({ ripples: dissoc(rippleKey, self.state.ripples) });
|
||||||
}
|
}
|
||||||
|
});
|
||||||
/**
|
|
||||||
* Find out a descriptor object for the ripple element being created depending on
|
|
||||||
* the position where the it was triggered and the component's dimensions.
|
|
||||||
*
|
|
||||||
* @param {Number} x Coordinate x in the viewport where ripple was triggered
|
|
||||||
* @param {Number} y Coordinate y in the viewport where ripple was triggered
|
|
||||||
* @return {Object} Descriptor element including position and size of the element
|
|
||||||
*/
|
|
||||||
getDescriptor (x, y) {
|
|
||||||
const { left, top, height, width } = ReactDOM.findDOMNode(this).getBoundingClientRect();
|
|
||||||
const { rippleCentered: centered, rippleSpread: spread } = this.props;
|
|
||||||
return {
|
|
||||||
left: centered ? 0 : x - left - width / 2,
|
|
||||||
top: centered ? 0 : y - top - height / 2,
|
|
||||||
width: width * spread
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increments and internal counter and returns the next value as a string. It
|
|
||||||
* is used to assign key references to new ripple elements.
|
|
||||||
*
|
|
||||||
* @return {String} Key to be assigned to a ripple.
|
|
||||||
*/
|
|
||||||
getNextKey () {
|
|
||||||
this.currentCount = this.currentCount ? this.currentCount + 1 : 1;
|
|
||||||
return `ripple${this.currentCount}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the last generated key for a ripple element. When there is only one ripple
|
|
||||||
* and to get the reference when a ripple was just created.
|
|
||||||
*
|
|
||||||
* @return {String} The last generated ripple key.
|
|
||||||
*/
|
|
||||||
getLastKey () {
|
|
||||||
return `ripple${this.currentCount}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,9 +193,9 @@ const rippleFactory = (options = {}) => {
|
||||||
*
|
*
|
||||||
* @param {Boolean} isTouch True in case the trigger was a touch event false otherwise.
|
* @param {Boolean} isTouch True in case the trigger was a touch event false otherwise.
|
||||||
* @param {String} rippleKey It's a key to identify the ripple that should be deactivated.
|
* @param {String} rippleKey It's a key to identify the ripple that should be deactivated.
|
||||||
* @return {Function} Callback function that deactivates the ripple and removes the event listener
|
* @return {Function} Callback function that deactivates the ripple and removes the listener
|
||||||
*/
|
*/
|
||||||
addRippleDeactivateEventListener (isTouch, rippleKey) {
|
addRippleDeactivateEventListener(isTouch, rippleKey) {
|
||||||
const eventType = isTouch ? 'touchend' : 'mouseup';
|
const eventType = isTouch ? 'touchend' : 'mouseup';
|
||||||
const endRipple = this.createRippleDeactivateCallback(eventType, rippleKey);
|
const endRipple = this.createRippleDeactivateCallback(eventType, rippleKey);
|
||||||
document.addEventListener(eventType, endRipple);
|
document.addEventListener(eventType, endRipple);
|
||||||
|
@ -191,7 +203,7 @@ const rippleFactory = (options = {}) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a function that can be called to deactivate a given ripple and remove its finishing
|
* Generates a function that can be called to deactivate a ripple and remove its finishing
|
||||||
* event listener. If is generated because we need to store it to be called on unmount in case
|
* event listener. If is generated because we need to store it to be called on unmount in case
|
||||||
* the ripple is still running.
|
* the ripple is still running.
|
||||||
*
|
*
|
||||||
|
@ -199,13 +211,13 @@ const rippleFactory = (options = {}) => {
|
||||||
* @param {String} rippleKey Is the key representing the ripple
|
* @param {String} rippleKey Is the key representing the ripple
|
||||||
* @return {Function} Callback function that deactivates the ripple and removes the listener
|
* @return {Function} Callback function that deactivates the ripple and removes the listener
|
||||||
*/
|
*/
|
||||||
createRippleDeactivateCallback (eventType, rippleKey) {
|
createRippleDeactivateCallback(eventType, rippleKey) {
|
||||||
const self = this;
|
const self = this;
|
||||||
return function endRipple () {
|
return function endRipple() {
|
||||||
document.removeEventListener(eventType, endRipple);
|
document.removeEventListener(eventType, endRipple);
|
||||||
self.setState({ ripples: {
|
self.setState({ ripples: {
|
||||||
...self.state.ripples,
|
...self.state.ripples,
|
||||||
[rippleKey]: Object.assign({}, self.state.ripples[rippleKey], { active: false })
|
[rippleKey]: Object.assign({}, self.state.ripples[rippleKey], { active: false }),
|
||||||
} });
|
} });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -226,26 +238,25 @@ const rippleFactory = (options = {}) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
renderRipple (key, className, { active, left, restarting, top, width }) {
|
renderRipple(key, className, { active, left, restarting, top, width }) {
|
||||||
const scale = restarting ? 0 : 1;
|
const scale = restarting ? 0 : 1;
|
||||||
const transform = `translate3d(${-width / 2 + left}px, ${-width / 2 + top}px, 0) scale(${scale})`;
|
const transform = `translate3d(${(-width / 2) + left}px, ${(-width / 2) + top}px, 0) scale(${scale})`;
|
||||||
const _className = classnames(this.props.theme.ripple, {
|
const _className = classnames(this.props.theme.ripple, {
|
||||||
[this.props.theme.rippleActive]: active,
|
[this.props.theme.rippleActive]: active,
|
||||||
[this.props.theme.rippleRestarting]: restarting
|
[this.props.theme.rippleRestarting]: restarting,
|
||||||
}, className);
|
}, className);
|
||||||
return (
|
return (
|
||||||
<span key={key} data-react-toolbox='ripple' className={this.props.theme.rippleWrapper} {...props}>
|
<span key={key} data-react-toolbox="ripple" className={this.props.theme.rippleWrapper} {...props}>
|
||||||
<span
|
<span
|
||||||
role='ripple'
|
|
||||||
ref={key}
|
|
||||||
className={_className}
|
className={_className}
|
||||||
style={prefixer({ transform }, {width, height: width})}
|
ref={(node) => { if (node) this.rippleNodes[key] = node; }}
|
||||||
|
style={prefixer({ transform }, { width, height: width })}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {
|
const {
|
||||||
children,
|
children,
|
||||||
disabled,
|
disabled,
|
||||||
|
@ -259,9 +270,17 @@ const rippleFactory = (options = {}) => {
|
||||||
...other
|
...other
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { ripples } = this.state;
|
const { ripples } = this.state;
|
||||||
const childRipples = Object.keys(ripples).map(key => this.renderRipple(key, rippleClassName, ripples[key]));
|
const childRipples = Object.keys(ripples).map(key =>
|
||||||
const childProps = { onMouseDown: this.handleMouseDown, onTouchStart: this.handleTouchStart, ...other };
|
this.renderRipple(key, rippleClassName, ripples[key]),
|
||||||
const finalProps = defaultPassthrough ? { ...childProps, theme, disabled } : childProps;
|
);
|
||||||
|
const childProps = {
|
||||||
|
onMouseDown: this.handleMouseDown,
|
||||||
|
onTouchStart: this.handleTouchStart,
|
||||||
|
...other,
|
||||||
|
};
|
||||||
|
const finalProps = defaultPassthrough
|
||||||
|
? { ...childProps, theme, disabled }
|
||||||
|
: childProps;
|
||||||
|
|
||||||
return !ripple
|
return !ripple
|
||||||
? React.createElement(ComposedComponent, finalProps, children)
|
? React.createElement(ComposedComponent, finalProps, children)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import rippleFactory from './Ripple.js';
|
import rippleFactory from './Ripple';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
export default (options) => rippleFactory({ ...options, theme });
|
export default options => rippleFactory({ ...options, theme });
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import styleShape from 'react-style-proptype';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { round, range } from '../utils/utils';
|
import { round, range } from '../utils/utils';
|
||||||
import { SLIDER } from '../identifiers.js';
|
import { SLIDER } from '../identifiers';
|
||||||
import events from '../utils/events.js';
|
import events from '../utils/events';
|
||||||
import InjectProgressBar from '../progress_bar/ProgressBar.js';
|
import InjectProgressBar from '../progress_bar/ProgressBar';
|
||||||
import InjectInput from '../input/Input.js';
|
import InjectInput from '../input/Input';
|
||||||
|
|
||||||
const factory = (ProgressBar, Input) => {
|
const factory = (ProgressBar, Input) => {
|
||||||
class Slider extends Component {
|
class Slider extends Component {
|
||||||
|
@ -20,7 +21,7 @@ const factory = (ProgressBar, Input) => {
|
||||||
pinned: PropTypes.bool,
|
pinned: PropTypes.bool,
|
||||||
snaps: PropTypes.bool,
|
snaps: PropTypes.bool,
|
||||||
step: PropTypes.number,
|
step: PropTypes.number,
|
||||||
style: PropTypes.object,
|
style: styleShape,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
container: PropTypes.string,
|
container: PropTypes.string,
|
||||||
editable: PropTypes.string,
|
editable: PropTypes.string,
|
||||||
|
@ -34,9 +35,9 @@ const factory = (ProgressBar, Input) => {
|
||||||
ring: PropTypes.string,
|
ring: PropTypes.string,
|
||||||
slider: PropTypes.string,
|
slider: PropTypes.string,
|
||||||
snap: PropTypes.string,
|
snap: PropTypes.string,
|
||||||
snaps: PropTypes.string
|
snaps: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
value: PropTypes.number
|
value: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -47,52 +48,84 @@ const factory = (ProgressBar, Input) => {
|
||||||
pinned: false,
|
pinned: false,
|
||||||
snaps: false,
|
snaps: false,
|
||||||
step: 0.01,
|
step: 0.01,
|
||||||
value: 0
|
value: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
inputFocused: false,
|
inputFocused: false,
|
||||||
inputValue: null,
|
inputValue: null,
|
||||||
sliderLength: 0,
|
sliderLength: 0,
|
||||||
sliderStart: 0
|
sliderStart: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
window.addEventListener('resize', this.handleResize);
|
window.addEventListener('resize', this.handleResize);
|
||||||
this.handleResize();
|
this.handleResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (this.state.inputFocused && this.props.value !== nextProps.value) {
|
if (this.state.inputFocused && this.props.value !== nextProps.value) {
|
||||||
this.setState({inputValue: this.valueForInput(nextProps.value)});
|
this.setState({ inputValue: this.valueForInput(nextProps.value) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate (nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
return this.state.inputFocused || !nextState.inputFocused;
|
return this.state.inputFocused || !nextState.inputFocused;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('resize', this.handleResize);
|
window.removeEventListener('resize', this.handleResize);
|
||||||
events.removeEventsFromDocument(this.getMouseEventMap());
|
events.removeEventsFromDocument(this.getMouseEventMap());
|
||||||
events.removeEventsFromDocument(this.getTouchEventMap());
|
events.removeEventsFromDocument(this.getTouchEventMap());
|
||||||
events.removeEventsFromDocument(this.getKeyboardEvents());
|
events.removeEventsFromDocument(this.getKeyboardEvents());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getInput() {
|
||||||
|
return this.inputNode && this.inputNode.getWrappedInstance
|
||||||
|
? this.inputNode.getWrappedInstance()
|
||||||
|
: this.inputNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
getKeyboardEvents() {
|
||||||
|
return {
|
||||||
|
keydown: this.handleKeyDown,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getMouseEventMap() {
|
||||||
|
return {
|
||||||
|
mousemove: this.handleMouseMove,
|
||||||
|
mouseup: this.handleMouseUp,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getTouchEventMap() {
|
||||||
|
return {
|
||||||
|
touchmove: this.handleTouchMove,
|
||||||
|
touchend: this.handleTouchEnd,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
handleInputFocus = () => {
|
handleInputFocus = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
inputFocused: true,
|
inputFocused: true,
|
||||||
inputValue: this.valueForInput(this.props.value)
|
inputValue: this.valueForInput(this.props.value),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleInputChange = (value) => {
|
handleInputChange = (value) => {
|
||||||
this.setState({inputValue: value});
|
this.setState({ inputValue: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleInputBlur = (event) => {
|
handleInputBlur = (event) => {
|
||||||
const value = this.state.inputValue || 0;
|
const value = this.state.inputValue || 0;
|
||||||
this.setState({inputFocused: false, inputValue: null}, () => {
|
this.setState({ inputFocused: false, inputValue: null }, () => {
|
||||||
this.props.onChange(this.trimValue(value), event);
|
this.props.onChange(this.trimValue(value), event);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -120,9 +153,9 @@ const factory = (ProgressBar, Input) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
handleResize = (event, callback) => {
|
handleResize = (event, callback) => {
|
||||||
const {left, right} = ReactDOM.findDOMNode(this.refs.progressbar).getBoundingClientRect();
|
const { left, right } = ReactDOM.findDOMNode(this.progressbarNode).getBoundingClientRect();
|
||||||
const cb = (callback) || (() => {});
|
const cb = (callback) || (() => {});
|
||||||
this.setState({sliderStart: left, sliderLength: right - left}, cb);
|
this.setState({ sliderStart: left, sliderLength: right - left }, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSliderBlur = () => {
|
handleSliderBlur = () => {
|
||||||
|
@ -148,157 +181,124 @@ const factory = (ProgressBar, Input) => {
|
||||||
events.pauseEvent(event);
|
events.pauseEvent(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
addToValue (increment) {
|
end(revents) {
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
getInput () {
|
|
||||||
return this.refs.input && this.refs.input.getWrappedInstance
|
|
||||||
? this.refs.input.getWrappedInstance()
|
|
||||||
: this.refs.input;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
events.removeEventsFromDocument(revents);
|
||||||
this.setState({ pressed: false });
|
this.setState({ pressed: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
knobOffset () {
|
knobOffset() {
|
||||||
const { max, min } = this.props;
|
const { max, min } = this.props;
|
||||||
const translated = this.state.sliderLength * (this.props.value - min) / (max - min);
|
const translated = this.state.sliderLength * ((this.props.value - min) / (max - min));
|
||||||
return translated * 100 / this.state.sliderLength;
|
return (translated * 100) / this.state.sliderLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
move (position) {
|
move(position) {
|
||||||
const newValue = this.positionToValue(position);
|
const newValue = this.positionToValue(position);
|
||||||
if (newValue !== this.props.value) this.props.onChange(newValue);
|
if (newValue !== this.props.value) this.props.onChange(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
positionToValue (position) {
|
positionToValue(position) {
|
||||||
const { sliderStart: start, sliderLength: length } = this.state;
|
const { sliderStart: start, sliderLength: length } = this.state;
|
||||||
const { max, min, step } = this.props;
|
const { max, min, step } = this.props;
|
||||||
const pos = (position.x - start) / length * (max - min);
|
const pos = ((position.x - start) / length) * (max - min);
|
||||||
return this.trimValue(Math.round(pos / step) * step + min);
|
return this.trimValue((Math.round(pos / step) * step) + min);
|
||||||
}
|
}
|
||||||
|
|
||||||
start (position) {
|
start(position) {
|
||||||
this.handleResize(null, () => {
|
this.handleResize(null, () => {
|
||||||
this.setState({pressed: true});
|
this.setState({ pressed: true });
|
||||||
this.props.onChange(this.positionToValue(position));
|
this.props.onChange(this.positionToValue(position));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
stepDecimals () {
|
stepDecimals() {
|
||||||
return (this.props.step.toString().split('.')[1] || []).length;
|
return (this.props.step.toString().split('.')[1] || []).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
trimValue (value) {
|
trimValue(value) {
|
||||||
if (value < this.props.min) return this.props.min;
|
if (value < this.props.min) return this.props.min;
|
||||||
if (value > this.props.max) return this.props.max;
|
if (value > this.props.max) return this.props.max;
|
||||||
return round(value, this.stepDecimals());
|
return round(value, this.stepDecimals());
|
||||||
}
|
}
|
||||||
|
|
||||||
valueForInput (value) {
|
valueForInput(value) {
|
||||||
const decimals = this.stepDecimals();
|
const decimals = this.stepDecimals();
|
||||||
return decimals > 0 ? value.toFixed(decimals) : value.toString();
|
return decimals > 0 ? value.toFixed(decimals) : value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSnaps () {
|
renderSnaps() {
|
||||||
if (this.props.snaps) {
|
if (!this.props.snaps) return undefined;
|
||||||
return (
|
return (
|
||||||
<div ref='snaps' className={this.props.theme.snaps}>
|
<div className={this.props.theme.snaps}>
|
||||||
{range(0, (this.props.max - this.props.min) / this.props.step).map(i => {
|
{range(0, (this.props.max - this.props.min) / this.props.step).map(i =>
|
||||||
return <div key={`span-${i}`} className={this.props.theme.snap} />;
|
<div key={`span-${i}`} className={this.props.theme.snap} />,
|
||||||
})}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
renderInput () {
|
renderInput() {
|
||||||
if (this.props.editable) {
|
if (!this.props.editable) return undefined;
|
||||||
const value = this.state.inputFocused ? this.state.inputValue : this.valueForInput(this.props.value);
|
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
ref='input'
|
ref={(node) => { this.inputNode = node; }}
|
||||||
className={this.props.theme.input}
|
className={this.props.theme.input}
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
onFocus={this.handleInputFocus}
|
onFocus={this.handleInputFocus}
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
onBlur={this.handleInputBlur}
|
onBlur={this.handleInputBlur}
|
||||||
value={value}
|
value={this.state.inputFocused
|
||||||
|
? this.state.inputValue
|
||||||
|
: this.valueForInput(this.props.value)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
const knobStyles = {left: `${this.knobOffset()}%`};
|
const knobStyles = { left: `${this.knobOffset()}%` };
|
||||||
const className = classnames(theme.slider, {
|
const className = classnames(theme.slider, {
|
||||||
[theme.editable]: this.props.editable,
|
[theme.editable]: this.props.editable,
|
||||||
[theme.disabled]: this.props.disabled,
|
[theme.disabled]: this.props.disabled,
|
||||||
[theme.pinned]: this.props.pinned,
|
[theme.pinned]: this.props.pinned,
|
||||||
[theme.pressed]: this.state.pressed,
|
[theme.pressed]: this.state.pressed,
|
||||||
[theme.ring]: this.props.value === this.props.min
|
[theme.ring]: this.props.value === this.props.min,
|
||||||
}, this.props.className);
|
}, this.props.className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={className}
|
className={className}
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
data-react-toolbox='slider'
|
data-react-toolbox="slider"
|
||||||
onBlur={this.handleSliderBlur}
|
onBlur={this.handleSliderBlur}
|
||||||
onFocus={this.handleSliderFocus}
|
onFocus={this.handleSliderFocus}
|
||||||
style={this.props.style}
|
style={this.props.style}
|
||||||
tabIndex='0'
|
tabIndex="0"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref='slider'
|
ref={(node) => { this.sliderNode = node; }}
|
||||||
className={theme.container}
|
className={theme.container}
|
||||||
onMouseDown={this.handleMouseDown}
|
onMouseDown={this.handleMouseDown}
|
||||||
onTouchStart={this.handleTouchStart}
|
onTouchStart={this.handleTouchStart}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref='knob'
|
ref={(node) => { this.knobNode = node; }}
|
||||||
className={theme.knob}
|
className={theme.knob}
|
||||||
onMouseDown={this.handleMouseDown}
|
onMouseDown={this.handleMouseDown}
|
||||||
onTouchStart={this.handleTouchStart}
|
onTouchStart={this.handleTouchStart}
|
||||||
style={knobStyles}
|
style={knobStyles}
|
||||||
>
|
>
|
||||||
<div className={theme.innerknob} data-value={parseInt(this.props.value)}/>
|
<div className={theme.innerknob} data-value={parseInt(this.props.value, 10)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={theme.progress}>
|
<div className={theme.progress}>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
ref='progressbar'
|
ref={(node) => { this.progressbarNode = node; }}
|
||||||
className={theme.innerprogress}
|
className={theme.innerprogress}
|
||||||
max={this.props.max}
|
max={this.props.max}
|
||||||
min={this.props.min}
|
min={this.props.min}
|
||||||
mode='determinate'
|
mode="determinate"
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
/>
|
/>
|
||||||
{this.renderSnaps()}
|
{this.renderSnaps()}
|
||||||
|
|
|
@ -1,73 +1,77 @@
|
||||||
|
/* eslint-disable */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import TestUtils from 'react-addons-test-utils';
|
import TestUtils from 'react-addons-test-utils';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import { ProgressBar } from '../../progress_bar/ProgressBar.js';
|
import { ProgressBar } from '../../progress_bar/ProgressBar';
|
||||||
import Input, { Input as RawInput } from '../../input/Input.js';
|
import Input, { Input as RawInput } from '../../input/Input';
|
||||||
import Slider, { Slider as RawSlider } from '../Slider.js';
|
import Slider, { Slider as RawSlider } from '../Slider';
|
||||||
import utils from '../../utils/testing';
|
import utils from '../../utils/testing';
|
||||||
import theme from '../theme.css';
|
import theme from '../theme.css';
|
||||||
|
|
||||||
describe('Slider', function () {
|
describe('Slider', () => {
|
||||||
let slider, progress, input, onChange;
|
let slider,
|
||||||
|
progress,
|
||||||
|
input,
|
||||||
|
onChange;
|
||||||
|
|
||||||
describe('#positionToValue', function () {
|
describe('#positionToValue', () => {
|
||||||
before(function () {
|
before(() => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Slider min={-500} max={500} />);
|
const tree = TestUtils.renderIntoDocument(<Slider min={-500} max={500} />);
|
||||||
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
||||||
slider.setState({ sliderStart: 500, sliderLength: 100 });
|
slider.setState({ sliderStart: 500, sliderLength: 100 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns min when position is less than origin', function () {
|
it('returns min when position is less than origin', () => {
|
||||||
expect(slider.positionToValue({x: 400})).toEqual(-500);
|
expect(slider.positionToValue({ x: 400 })).toEqual(-500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns max when position is more and origin plus length', function () {
|
it('returns max when position is more and origin plus length', () => {
|
||||||
expect(slider.positionToValue({x: 900})).toEqual(500);
|
expect(slider.positionToValue({ x: 900 })).toEqual(500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the proper position when the position is inside slider', function () {
|
it('returns the proper position when the position is inside slider', () => {
|
||||||
expect(slider.positionToValue({x: 520})).toEqual(-300);
|
expect(slider.positionToValue({ x: 520 })).toEqual(-300);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#trimValue', function () {
|
describe('#trimValue', () => {
|
||||||
before(function () {
|
before(() => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Slider min={0} max={100} step={0.1} />);
|
const tree = TestUtils.renderIntoDocument(<Slider min={0} max={100} step={0.1} />);
|
||||||
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rounds to the proper number', function () {
|
it('rounds to the proper number', () => {
|
||||||
expect(slider.trimValue(57.16)).toEqual(57.2);
|
expect(slider.trimValue(57.16)).toEqual(57.2);
|
||||||
expect(slider.trimValue(57.12)).toEqual(57.10);
|
expect(slider.trimValue(57.12)).toEqual(57.10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns min if number is less than min', function () {
|
it('returns min if number is less than min', () => {
|
||||||
expect(slider.trimValue(-57.16)).toEqual(0);
|
expect(slider.trimValue(-57.16)).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns max if number is more than max', function () {
|
it('returns max if number is more than max', () => {
|
||||||
expect(slider.trimValue(257.16)).toEqual(100);
|
expect(slider.trimValue(257.16)).toEqual(100);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#valueForInput', function () {
|
describe('#valueForInput', () => {
|
||||||
before(function () {
|
before(() => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Slider min={0} max={100} step={0.01} />);
|
const tree = TestUtils.renderIntoDocument(<Slider min={0} max={100} step={0.01} />);
|
||||||
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a fixed number when an integer is given', function () {
|
it('returns a fixed number when an integer is given', () => {
|
||||||
expect(slider.valueForInput(4)).toEqual('4.00');
|
expect(slider.valueForInput(4)).toEqual('4.00');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a fixed number when a float is given', function () {
|
it('returns a fixed number when a float is given', () => {
|
||||||
expect(slider.valueForInput(4.06)).toEqual('4.06');
|
expect(slider.valueForInput(4.06)).toEqual('4.06');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#knobOffset', function () {
|
describe('#knobOffset', () => {
|
||||||
it('returns the corresponding offset for a given value and slider length/start', function () {
|
it('returns the corresponding offset for a given value and slider length/start', () => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Slider min={-500} max={500} value={-250} />);
|
const tree = TestUtils.renderIntoDocument(<Slider min={-500} max={500} value={-250} />);
|
||||||
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
||||||
slider.setState({ sliderStart: 500, sliderLength: 100 });
|
slider.setState({ sliderStart: 500, sliderLength: 100 });
|
||||||
|
@ -75,8 +79,8 @@ describe('Slider', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#render', function () {
|
describe('#render', () => {
|
||||||
it('contains a linear progress bar with proper properties', function () {
|
it('contains a linear progress bar with proper properties', () => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Slider min={100} max={1000} value={140} />);
|
const tree = TestUtils.renderIntoDocument(<Slider min={100} max={1000} value={140} />);
|
||||||
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
||||||
progress = TestUtils.findRenderedComponentWithType(slider, ProgressBar);
|
progress = TestUtils.findRenderedComponentWithType(slider, ProgressBar);
|
||||||
|
@ -87,24 +91,24 @@ describe('Slider', function () {
|
||||||
expect(progress.props.max).toEqual(1000);
|
expect(progress.props.max).toEqual(1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains an input component if its editable', function () {
|
it('contains an input component if its editable', () => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Slider editable value={130} />);
|
const tree = TestUtils.renderIntoDocument(<Slider editable value={130} />);
|
||||||
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
||||||
input = TestUtils.findRenderedComponentWithType(slider, Input);
|
input = TestUtils.findRenderedComponentWithType(slider, Input);
|
||||||
expect(parseInt(input.props.value)).toEqual(slider.props.value);
|
expect(parseInt(input.props.value)).toEqual(slider.props.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains the proper number of snaps when snapped', function () {
|
it('contains the proper number of snaps when snapped', () => {
|
||||||
slider = utils.shallowRenderComponent(RawSlider, {editable: true, pinned: true, theme});
|
slider = utils.shallowRenderComponent(RawSlider, { editable: true, pinned: true, theme });
|
||||||
expect(slider.props.className).toContain(theme.ring);
|
expect(slider.props.className).toContain(theme.ring);
|
||||||
expect(slider.props.className).toContain(theme.pinned);
|
expect(slider.props.className).toContain(theme.pinned);
|
||||||
slider = utils.shallowRenderComponent(RawSlider, {editable: true, value: 50, theme});
|
slider = utils.shallowRenderComponent(RawSlider, { editable: true, value: 50, theme });
|
||||||
expect(slider.props.className).toNotContain(theme.ring);
|
expect(slider.props.className).toNotContain(theme.ring);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#events', function () {
|
describe('#events', () => {
|
||||||
beforeEach(function () {
|
beforeEach(() => {
|
||||||
onChange = sinon.spy();
|
onChange = sinon.spy();
|
||||||
const tree = TestUtils.renderIntoDocument(<Slider min={-500} max={500} onChange={onChange} />);
|
const tree = TestUtils.renderIntoDocument(<Slider min={-500} max={500} onChange={onChange} />);
|
||||||
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
||||||
|
@ -112,32 +116,32 @@ describe('Slider', function () {
|
||||||
slider.handleResize = (event, callback) => { callback(); };
|
slider.handleResize = (event, callback) => { callback(); };
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets pressed state when knob is clicked', function () {
|
it('sets pressed state when knob is clicked', () => {
|
||||||
TestUtils.Simulate.mouseDown(slider.refs.knob);
|
TestUtils.Simulate.mouseDown(slider.refs.knob);
|
||||||
expect(slider.state.pressed).toEqual(true);
|
expect(slider.state.pressed).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets pressed state when knob is touched', function () {
|
it('sets pressed state when knob is touched', () => {
|
||||||
TestUtils.Simulate.touchStart(slider.refs.knob, {touches: [{pageX: 200}]});
|
TestUtils.Simulate.touchStart(slider.refs.knob, { touches: [{ pageX: 200 }] });
|
||||||
expect(slider.state.pressed).toEqual(true);
|
expect(slider.state.pressed).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets a proper value when the slider is clicked', function () {
|
it('sets a proper value when the slider is clicked', () => {
|
||||||
TestUtils.Simulate.mouseDown(slider.refs.slider, { pageX: 200 });
|
TestUtils.Simulate.mouseDown(slider.refs.slider, { pageX: 200 });
|
||||||
expect(onChange.called).toEqual(true);
|
expect(onChange.called).toEqual(true);
|
||||||
expect(onChange.getCall(0).args[0]).toEqual(-300);
|
expect(onChange.getCall(0).args[0]).toEqual(-300);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets a proper value when the slider is touched', function () {
|
it('sets a proper value when the slider is touched', () => {
|
||||||
TestUtils.Simulate.touchStart(slider.refs.slider, {touches: [{pageX: 200, pageY: 0}]});
|
TestUtils.Simulate.touchStart(slider.refs.slider, { touches: [{ pageX: 200, pageY: 0 }] });
|
||||||
expect(onChange.called).toEqual(true);
|
expect(onChange.called).toEqual(true);
|
||||||
expect(onChange.getCall(0).args[0]).toEqual(-300);
|
expect(onChange.getCall(0).args[0]).toEqual(-300);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes input value when slider changes', function () {
|
it('changes input value when slider changes', () => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Slider editable onChange={onChange} />);
|
const tree = TestUtils.renderIntoDocument(<Slider editable onChange={onChange} />);
|
||||||
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
||||||
slider.setState({sliderStart: 0, sliderLength: 1000});
|
slider.setState({ sliderStart: 0, sliderLength: 1000 });
|
||||||
slider.handleResize = (event, callback) => { callback(); };
|
slider.handleResize = (event, callback) => { callback(); };
|
||||||
input = TestUtils.findRenderedComponentWithType(slider, Input);
|
input = TestUtils.findRenderedComponentWithType(slider, Input);
|
||||||
TestUtils.Simulate.mouseDown(slider.refs.slider, { pageX: 900 });
|
TestUtils.Simulate.mouseDown(slider.refs.slider, { pageX: 900 });
|
||||||
|
@ -145,21 +149,21 @@ describe('Slider', function () {
|
||||||
expect(onChange.getCall(0).args[0]).toEqual(90);
|
expect(onChange.getCall(0).args[0]).toEqual(90);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes its value when input is blurred', function () {
|
it('changes its value when input is blurred', () => {
|
||||||
const tree = TestUtils.renderIntoDocument(<Slider editable value={50} onChange={onChange} />);
|
const tree = TestUtils.renderIntoDocument(<Slider editable value={50} onChange={onChange} />);
|
||||||
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
||||||
input = TestUtils.findRenderedComponentWithType(slider, RawInput);
|
input = TestUtils.findRenderedComponentWithType(slider, RawInput);
|
||||||
TestUtils.Simulate.change(input.refs.input, {target: {value: '80'}});
|
TestUtils.Simulate.change(input.refs.input, { target: { value: '80' } });
|
||||||
TestUtils.Simulate.blur(input.refs.input);
|
TestUtils.Simulate.blur(input.refs.input);
|
||||||
expect(onChange.called).toEqual(true);
|
expect(onChange.called).toEqual(true);
|
||||||
expect(onChange.getCall(0).args[0]).toEqual(80);
|
expect(onChange.getCall(0).args[0]).toEqual(80);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls onChange callback when the value is changed', function () {
|
it('calls onChange callback when the value is changed', () => {
|
||||||
const onChangeSpy = sinon.spy();
|
const onChangeSpy = sinon.spy();
|
||||||
const tree = TestUtils.renderIntoDocument(<Slider onChange={onChangeSpy} />);
|
const tree = TestUtils.renderIntoDocument(<Slider onChange={onChangeSpy} />);
|
||||||
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
|
||||||
slider.setState({sliderStart: 0, sliderLength: 1000});
|
slider.setState({ sliderStart: 0, sliderLength: 1000 });
|
||||||
TestUtils.Simulate.mouseDown(slider.refs.slider, { pageX: 900 });
|
TestUtils.Simulate.mouseDown(slider.refs.slider, { pageX: 900 });
|
||||||
expect(onChangeSpy.called).toEqual(true);
|
expect(onChangeSpy.called).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { SLIDER } from '../identifiers.js';
|
import { SLIDER } from '../identifiers';
|
||||||
import { ProgressBar } from '../progress_bar';
|
import { ProgressBar } from '../progress_bar';
|
||||||
import { Input} from '../input';
|
import { Input } from '../input';
|
||||||
import { sliderFactory } from './Slider.js';
|
import { sliderFactory } from './Slider';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const ThemedSlider = themr(SLIDER, theme)(sliderFactory(ProgressBar, Input));
|
const ThemedSlider = themr(SLIDER, theme)(sliderFactory(ProgressBar, Input));
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { SNACKBAR } from '../identifiers.js';
|
import { SNACKBAR } from '../identifiers';
|
||||||
import ActivableRenderer from '../hoc/ActivableRenderer.js';
|
import ActivableRenderer from '../hoc/ActivableRenderer';
|
||||||
import InjectButton from '../button/Button.js';
|
import InjectButton from '../button/Button';
|
||||||
import Portal from '../hoc/Portal.js';
|
import Portal from '../hoc/Portal';
|
||||||
|
|
||||||
const factory = (Button) => {
|
const factory = (Button) => {
|
||||||
class Snackbar extends Component {
|
class Snackbar extends Component {
|
||||||
|
@ -15,7 +15,7 @@ const factory = (Button) => {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
label: PropTypes.oneOfType([
|
label: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element
|
PropTypes.element,
|
||||||
]),
|
]),
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
onTimeout: PropTypes.func,
|
onTimeout: PropTypes.func,
|
||||||
|
@ -26,29 +26,29 @@ const factory = (Button) => {
|
||||||
cancel: PropTypes.string,
|
cancel: PropTypes.string,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
snackbar: PropTypes.string,
|
snackbar: PropTypes.string,
|
||||||
warning: PropTypes.string
|
warning: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
timeout: PropTypes.number,
|
timeout: PropTypes.number,
|
||||||
type: PropTypes.oneOf([ 'accept', 'cancel', 'warning' ])
|
type: PropTypes.oneOf(['accept', 'cancel', 'warning']),
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
if (this.props.active && this.props.timeout) {
|
if (this.props.active && this.props.timeout) {
|
||||||
this.scheduleTimeout(this.props);
|
this.scheduleTimeout(this.props);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.active && nextProps.timeout) {
|
if (nextProps.active && nextProps.timeout) {
|
||||||
this.scheduleTimeout(nextProps);
|
this.scheduleTimeout(nextProps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
clearTimeout(this.curTimeout);
|
clearTimeout(this.curTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleTimeout = props => {
|
scheduleTimeout = (props) => {
|
||||||
const { onTimeout, timeout } = props;
|
const { onTimeout, timeout } = props;
|
||||||
if (this.curTimeout) clearTimeout(this.curTimeout);
|
if (this.curTimeout) clearTimeout(this.curTimeout);
|
||||||
this.curTimeout = setTimeout(() => {
|
this.curTimeout = setTimeout(() => {
|
||||||
|
@ -57,20 +57,20 @@ const factory = (Button) => {
|
||||||
}, timeout);
|
}, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {action, active, children, label, onClick, theme, type } = this.props;
|
const { action, active, children, label, onClick, theme, type } = this.props;
|
||||||
const className = classnames([theme.snackbar, theme[type]], {
|
const className = classnames([theme.snackbar, theme[type]], {
|
||||||
[theme.active]: active
|
[theme.active]: active,
|
||||||
}, this.props.className);
|
}, this.props.className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Portal className={theme.portal}>
|
<Portal className={theme.portal}>
|
||||||
<div data-react-toolbox='snackbar' className={className}>
|
<div data-react-toolbox="snackbar" className={className}>
|
||||||
<span className={theme.label}>
|
<span className={theme.label}>
|
||||||
{label}
|
{label}
|
||||||
{children}
|
{children}
|
||||||
</span>
|
</span>
|
||||||
{action ? <Button className={theme.button} label={action} onClick={onClick}/> : null}
|
{action ? <Button className={theme.button} label={action} onClick={onClick} /> : null}
|
||||||
</div>
|
</div>
|
||||||
</Portal>
|
</Portal>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { SNACKBAR } from '../identifiers.js';
|
import { SNACKBAR } from '../identifiers';
|
||||||
import { snackbarFactory } from './Snackbar.js';
|
import { snackbarFactory } from './Snackbar';
|
||||||
import { Button } from '../button';
|
import { Button } from '../button';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { SWITCH } from '../identifiers.js';
|
import { SWITCH } from '../identifiers';
|
||||||
import rippleFactory from '../ripple/Ripple.js';
|
import rippleFactory from '../ripple/Ripple';
|
||||||
import thumbFactory from './Thumb.js';
|
import thumbFactory from './Thumb';
|
||||||
|
|
||||||
const factory = (Thumb) => {
|
const factory = (Thumb) => {
|
||||||
class Switch extends Component {
|
class Switch extends Component {
|
||||||
|
@ -25,14 +25,14 @@ const factory = (Thumb) => {
|
||||||
on: PropTypes.string,
|
on: PropTypes.string,
|
||||||
ripple: PropTypes.string,
|
ripple: PropTypes.string,
|
||||||
text: PropTypes.string,
|
text: PropTypes.string,
|
||||||
thumb: PropTypes.string
|
thumb: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
checked: false,
|
checked: false,
|
||||||
className: '',
|
className: '',
|
||||||
disabled: false
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleToggle = (event) => {
|
handleToggle = (event) => {
|
||||||
|
@ -42,27 +42,35 @@ const factory = (Thumb) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
blur () {
|
blur() {
|
||||||
this.refs.input.blur();
|
this.inputNode.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
focus () {
|
focus() {
|
||||||
this.refs.input.focus();
|
this.inputNode.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { className, checked, ripple, disabled, onChange, theme, ...others } = this.props; //eslint-disable-line no-unused-vars
|
const {
|
||||||
|
checked,
|
||||||
|
className,
|
||||||
|
disabled,
|
||||||
|
onChange, // eslint-disable-line no-unused-vars
|
||||||
|
ripple,
|
||||||
|
theme,
|
||||||
|
...others
|
||||||
|
} = this.props;
|
||||||
const _className = classnames(theme[disabled ? 'disabled' : 'field'], className);
|
const _className = classnames(theme[disabled ? 'disabled' : 'field'], className);
|
||||||
return (
|
return (
|
||||||
<label data-react-toolbox='switch' className={_className}>
|
<label data-react-toolbox="switch" className={_className} htmlFor={this.props.name}>
|
||||||
<input
|
<input
|
||||||
{...others}
|
{...others}
|
||||||
checked={this.props.checked}
|
checked={this.props.checked}
|
||||||
className={theme.input}
|
className={theme.input}
|
||||||
onClick={this.handleToggle}
|
onClick={this.handleToggle}
|
||||||
readOnly
|
readOnly
|
||||||
ref='input'
|
ref={(node) => { this.inputNode = node; }}
|
||||||
type='checkbox'
|
type="checkbox"
|
||||||
/>
|
/>
|
||||||
<span className={theme[checked ? 'on' : 'off']}>
|
<span className={theme[checked ? 'on' : 'off']}>
|
||||||
<Thumb disabled={this.props.disabled} theme={theme} ripple={ripple} />
|
<Thumb disabled={this.props.disabled} theme={theme} ripple={ripple} />
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
|
|
||||||
const factory = (ripple) => {
|
const factory = (ripple) => {
|
||||||
const Thumb = ({onMouseDown, theme, ...other}) => (
|
const Thumb = ({ onMouseDown, theme, ...other }) => (
|
||||||
<span role='thumb' className={theme.thumb} onMouseDown={onMouseDown} {...other} />
|
<span className={theme.thumb} onMouseDown={onMouseDown} {...other} />
|
||||||
);
|
);
|
||||||
|
|
||||||
Thumb.propTypes = {
|
Thumb.propTypes = {
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
|
onMouseDown: PropTypes.func,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
ripple: PropTypes.string,
|
ripple: PropTypes.string,
|
||||||
thumb: PropTypes.string
|
thumb: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
return ripple(Thumb);
|
return ripple(Thumb);
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { switchFactory } from './Switch.js';
|
import { switchFactory } from './Switch';
|
||||||
import { SWITCH } from '../identifiers.js';
|
import { SWITCH } from '../identifiers';
|
||||||
import thumbFactory from './Thumb.js';
|
import thumbFactory from './Thumb';
|
||||||
import themedRippleFactory from '../ripple';
|
import themedRippleFactory from '../ripple';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const applyTheme = (Component) => themr(SWITCH, theme)(Component);
|
const applyTheme = Component => themr(SWITCH, theme)(Component);
|
||||||
const ripple = themedRippleFactory({ centered: true, spread: 2.6 });
|
const ripple = themedRippleFactory({ centered: true, spread: 2.6 });
|
||||||
const ThemedThumb = applyTheme(thumbFactory(ripple));
|
const ThemedThumb = applyTheme(thumbFactory(ripple));
|
||||||
const ThemedSwitch = applyTheme(switchFactory(ThemedThumb));
|
const ThemedSwitch = applyTheme(switchFactory(ThemedThumb));
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import React, { PropTypes, Component, cloneElement } from 'react';
|
import React, { PropTypes, Component, cloneElement } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import filterReactChildren from '../utils/filter-react-children.js';
|
import filterReactChildren from '../utils/filter-react-children';
|
||||||
import isComponentOfType from '../utils/is-component-of-type.js';
|
import isComponentOfType from '../utils/is-component-of-type';
|
||||||
import { TABLE } from '../identifiers.js';
|
import { TABLE } from '../identifiers';
|
||||||
import InjectTableHead from './TableHead.js';
|
import InjectTableHead from './TableHead';
|
||||||
import InjectTableRow from './TableRow.js';
|
import InjectTableRow from './TableRow';
|
||||||
|
|
||||||
const factory = (TableHead, TableRow) => {
|
const factory = (TableHead, TableRow) => {
|
||||||
const isTableHead = child => isComponentOfType(TableHead, child);
|
const isTableHead = child => isComponentOfType(TableHead, child);
|
||||||
|
@ -20,21 +20,21 @@ const factory = (TableHead, TableRow) => {
|
||||||
selectable: PropTypes.bool,
|
selectable: PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
head: PropTypes.string,
|
head: PropTypes.string,
|
||||||
table: PropTypes.string
|
table: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
className: '',
|
className: '',
|
||||||
multiSelectable: false,
|
multiSelectable: false,
|
||||||
selectable: true
|
selectable: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
getRowTuples = () => React.Children
|
getRowTuples = () => React.Children
|
||||||
.toArray(filterReactChildren(this.props.children, isTableRow))
|
.toArray(filterReactChildren(this.props.children, isTableRow))
|
||||||
.map((child, index) => [index, Boolean(child.props.selected)]);
|
.map((child, index) => [index, Boolean(child.props.selected)]);
|
||||||
|
|
||||||
handleHeadSelect = value => {
|
handleHeadSelect = (value) => {
|
||||||
if (this.props.onRowSelect) {
|
if (this.props.onRowSelect) {
|
||||||
this.props.onRowSelect(value
|
this.props.onRowSelect(value
|
||||||
? this.getRowTuples().map(item => item[0])
|
? this.getRowTuples().map(item => item[0])
|
||||||
|
@ -42,7 +42,7 @@ const factory = (TableHead, TableRow) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleRowSelect = idx => {
|
handleRowSelect = (idx) => {
|
||||||
if (this.props.onRowSelect) {
|
if (this.props.onRowSelect) {
|
||||||
if (this.props.multiSelectable) {
|
if (this.props.multiSelectable) {
|
||||||
const current = this.getRowTuples().filter(item => item[1]).map(item => item[0]);
|
const current = this.getRowTuples().filter(item => item[1]).map(item => item[0]);
|
||||||
|
@ -66,8 +66,8 @@ const factory = (TableHead, TableRow) => {
|
||||||
selected,
|
selected,
|
||||||
multiSelectable: this.props.multiSelectable,
|
multiSelectable: this.props.multiSelectable,
|
||||||
onSelect: this.handleHeadSelect,
|
onSelect: this.handleHeadSelect,
|
||||||
selectable: this.props.selectable
|
selectable: this.props.selectable,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -76,11 +76,11 @@ const factory = (TableHead, TableRow) => {
|
||||||
(child, idx) => cloneElement(child, {
|
(child, idx) => cloneElement(child, {
|
||||||
idx,
|
idx,
|
||||||
onSelect: this.handleRowSelect,
|
onSelect: this.handleRowSelect,
|
||||||
selectable: this.props.selectable
|
selectable: this.props.selectable,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
multiSelectable, // eslint-disable-line
|
multiSelectable, // eslint-disable-line
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { TABLE } from '../identifiers.js';
|
import { TABLE } from '../identifiers';
|
||||||
import InjectFontIcon from '../font_icon/FontIcon.js';
|
import InjectFontIcon from '../font_icon/FontIcon';
|
||||||
|
|
||||||
const ASC = 'asc';
|
const ASC = 'asc';
|
||||||
const DESC = 'desc';
|
const DESC = 'desc';
|
||||||
|
@ -10,7 +10,7 @@ const DESC = 'desc';
|
||||||
const factory = (FontIcon) => {
|
const factory = (FontIcon) => {
|
||||||
class TableCell extends Component {
|
class TableCell extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.any,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
column: PropTypes.number,
|
column: PropTypes.number,
|
||||||
numeric: PropTypes.bool,
|
numeric: PropTypes.bool,
|
||||||
|
@ -25,23 +25,23 @@ const factory = (FontIcon) => {
|
||||||
rowCell: PropTypes.string,
|
rowCell: PropTypes.string,
|
||||||
sorted: PropTypes.string,
|
sorted: PropTypes.string,
|
||||||
sortIcon: PropTypes.string,
|
sortIcon: PropTypes.string,
|
||||||
tableCell: PropTypes.string
|
tableCell: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
className: '',
|
className: '',
|
||||||
numeric: false,
|
numeric: false,
|
||||||
tagName: 'td'
|
tagName: 'td',
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = event => {
|
handleClick = (event) => {
|
||||||
const { onClick, row, column } = this.props;
|
const { onClick, row, column } = this.props;
|
||||||
if (onClick) onClick(event, column, row);
|
if (onClick) onClick(event, column, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {
|
const {
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
|
@ -58,23 +58,23 @@ const factory = (FontIcon) => {
|
||||||
[theme.headCell]: tagName === 'th',
|
[theme.headCell]: tagName === 'th',
|
||||||
[theme.rowCell]: tagName === 'td',
|
[theme.rowCell]: tagName === 'td',
|
||||||
[theme.sorted]: sorted,
|
[theme.sorted]: sorted,
|
||||||
[theme.numeric]: numeric
|
[theme.numeric]: numeric,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
...other,
|
...other,
|
||||||
className: _className,
|
className: _className,
|
||||||
onClick: this.handleClick
|
onClick: this.handleClick,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
React.createElement(tagName, props, [
|
React.createElement(tagName, props, [
|
||||||
sorted && <FontIcon
|
sorted && <FontIcon
|
||||||
className={classnames(theme.sortIcon, { [theme.asc]: sorted === ASC })}
|
className={classnames(theme.sortIcon, { [theme.asc]: sorted === ASC })}
|
||||||
key='icon'
|
key="icon"
|
||||||
value="arrow_downward"
|
value="arrow_downward"
|
||||||
/>,
|
/>,
|
||||||
children
|
children,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { Component, PropTypes, cloneElement } from 'react';
|
import React, { Component, PropTypes, cloneElement } from 'react';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { TABLE } from '../identifiers.js';
|
import { TABLE } from '../identifiers';
|
||||||
import InjectCheckbox from '../checkbox/Checkbox.js';
|
import InjectCheckbox from '../checkbox/Checkbox';
|
||||||
import InjectTableCell from './TableCell.js';
|
import InjectTableCell from './TableCell';
|
||||||
|
|
||||||
const factory = (Checkbox, TableCell) => {
|
const factory = (Checkbox, TableCell) => {
|
||||||
class TableHead extends Component {
|
class TableHead extends Component {
|
||||||
|
@ -15,19 +15,19 @@ const factory = (Checkbox, TableCell) => {
|
||||||
selectable: PropTypes.bool,
|
selectable: PropTypes.bool,
|
||||||
selected: PropTypes.bool,
|
selected: PropTypes.bool,
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
checkboxCell: PropTypes.string
|
checkboxCell: PropTypes.string,
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
displaySelect: true
|
displaySelect: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelect = (value, event) => {
|
handleSelect = (value, event) => {
|
||||||
this.props.onSelect(value, event);
|
this.props.onSelect(value, event);
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {
|
const {
|
||||||
children,
|
children,
|
||||||
displaySelect,
|
displaySelect,
|
||||||
|
@ -49,7 +49,7 @@ const factory = (Checkbox, TableCell) => {
|
||||||
</TableCell>}
|
</TableCell>}
|
||||||
{React.Children.map(children, (child, index) => cloneElement(child, {
|
{React.Children.map(children, (child, index) => cloneElement(child, {
|
||||||
column: index,
|
column: index,
|
||||||
tagName: 'th'
|
tagName: 'th',
|
||||||
}))}
|
}))}
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { cloneElement, Component, PropTypes } from 'react';
|
import React, { cloneElement, Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { TABLE } from '../identifiers.js';
|
import { TABLE } from '../identifiers';
|
||||||
import InjectCheckbox from '../checkbox/Checkbox.js';
|
import InjectCheckbox from '../checkbox/Checkbox';
|
||||||
import InjectTableCell from './TableCell.js';
|
import InjectTableCell from './TableCell';
|
||||||
|
|
||||||
const factory = (Checkbox, TableCell) => {
|
const factory = (Checkbox, TableCell) => {
|
||||||
class TableRow extends Component {
|
class TableRow extends Component {
|
||||||
|
@ -17,19 +17,19 @@ const factory = (Checkbox, TableCell) => {
|
||||||
theme: PropTypes.shape({
|
theme: PropTypes.shape({
|
||||||
checkboxCell: PropTypes.string,
|
checkboxCell: PropTypes.string,
|
||||||
row: PropTypes.string,
|
row: PropTypes.string,
|
||||||
selected: PropTypes.string
|
selected: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSelect = value => {
|
handleSelect = (value) => {
|
||||||
const { idx, onSelect } = this.props;
|
const { idx, onSelect } = this.props;
|
||||||
if (onSelect) onSelect(idx, value);
|
if (onSelect) onSelect(idx, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { children, className, selectable, idx, selected, theme, ...other } = this.props; // eslint-disable-line
|
const { children, className, selectable, idx, selected, theme, ...other } = this.props; // eslint-disable-line
|
||||||
const _className = classnames(theme.row, {
|
const _className = classnames(theme.row, {
|
||||||
[theme.selected]: selectable && selected
|
[theme.selected]: selectable && selected,
|
||||||
}, className);
|
}, className);
|
||||||
return (
|
return (
|
||||||
<tr {...other} className={_className}>
|
<tr {...other} className={_className}>
|
||||||
|
@ -38,7 +38,7 @@ const factory = (Checkbox, TableCell) => {
|
||||||
</TableCell>}
|
</TableCell>}
|
||||||
{React.Children.map(children, (child, index) => cloneElement(child, {
|
{React.Children.map(children, (child, index) => cloneElement(child, {
|
||||||
column: index,
|
column: index,
|
||||||
tagName: 'td'
|
tagName: 'td',
|
||||||
}))}
|
}))}
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { TABLE } from '../identifiers.js';
|
import { TABLE } from '../identifiers';
|
||||||
import { Checkbox } from '../checkbox';
|
import { Checkbox } from '../checkbox';
|
||||||
import { FontIcon } from '../font_icon';
|
import { FontIcon } from '../font_icon';
|
||||||
|
|
||||||
import { tableFactory } from './Table.js';
|
import { tableFactory } from './Table';
|
||||||
import { tableHeadFactory } from './TableHead.js';
|
import { tableHeadFactory } from './TableHead';
|
||||||
import { tableRowFactory } from './TableRow.js';
|
import { tableRowFactory } from './TableRow';
|
||||||
import { tableCellFactory } from './TableCell.js';
|
import { tableCellFactory } from './TableCell';
|
||||||
import theme from './theme.css';
|
import theme from './theme.css';
|
||||||
|
|
||||||
const applyTheme = (Component) => themr(TABLE, theme)(Component);
|
const applyTheme = Component => themr(TABLE, theme)(Component);
|
||||||
const ThemedTableCell = applyTheme(tableCellFactory(FontIcon));
|
const ThemedTableCell = applyTheme(tableCellFactory(FontIcon));
|
||||||
const ThemedTableHead = applyTheme(tableHeadFactory(Checkbox, ThemedTableCell));
|
const ThemedTableHead = applyTheme(tableHeadFactory(Checkbox, ThemedTableCell));
|
||||||
const ThemedTableRow = applyTheme(tableRowFactory(Checkbox, ThemedTableCell));
|
const ThemedTableRow = applyTheme(tableRowFactory(Checkbox, ThemedTableCell));
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { FontIcon } from '../font_icon';
|
|
||||||
import { themr } from 'react-css-themr';
|
import { themr } from 'react-css-themr';
|
||||||
import { TABS } from '../identifiers.js';
|
import { FontIcon } from '../font_icon';
|
||||||
import rippleFactory from '../ripple/Ripple.js';
|
import { TABS } from '../identifiers';
|
||||||
|
import rippleFactory from '../ripple/Ripple';
|
||||||
|
|
||||||
const factory = (ripple) => {
|
const factory = (ripple) => {
|
||||||
class Tab extends Component {
|
class Tab extends Component {
|
||||||
|
@ -26,18 +26,18 @@ const factory = (ripple) => {
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
rippleWrapper: PropTypes.string,
|
rippleWrapper: PropTypes.string,
|
||||||
withIcon: PropTypes.string,
|
withIcon: PropTypes.string,
|
||||||
withText: PropTypes.string
|
withText: PropTypes.string,
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
active: false,
|
active: false,
|
||||||
className: '',
|
className: '',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
hidden: false
|
hidden: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (!prevProps.active && this.props.active && this.props.onActive) {
|
if (!prevProps.active && this.props.active && this.props.onActive) {
|
||||||
this.props.onActive();
|
this.props.onActive();
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ const factory = (ripple) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const {
|
const {
|
||||||
index, onActive, // eslint-disable-line
|
index, onActive, // eslint-disable-line
|
||||||
active, activeClassName, children, className, disabled, hidden, label, icon, theme, ...other
|
active, activeClassName, children, className, disabled, hidden, label, icon, theme, ...other
|
||||||
|
@ -60,15 +60,15 @@ const factory = (ripple) => {
|
||||||
[theme.withText]: label,
|
[theme.withText]: label,
|
||||||
[theme.withIcon]: icon,
|
[theme.withIcon]: icon,
|
||||||
[theme.disabled]: disabled,
|
[theme.disabled]: disabled,
|
||||||
[activeClassName]: active
|
[activeClassName]: active,
|
||||||
}, className);
|
}, className);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label {...other} data-react-toolbox='tab' className={_className} onClick={this.handleClick}>
|
<div {...other} data-react-toolbox="tab" className={_className} onClick={this.handleClick}>
|
||||||
{icon && <FontIcon className={theme.icon} value={icon}/>}
|
{icon && <FontIcon className={theme.icon} value={icon} />}
|
||||||
{label}
|
{label}
|
||||||
{children}
|
{children}
|
||||||
</label>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue