Rename most components index to actual components

old
Javi Velasco 2015-11-22 21:41:28 +01:00
parent 1611b491fa
commit cfebdb55f4
62 changed files with 1108 additions and 1080 deletions

View File

@ -1,6 +1,6 @@
{
"stage": 1,
"optional": ["es7.classProperties"],
"stage": 2,
"optional": ["es7.classProperties", "es7.exportExtensions"],
"env": {
"development": {
"plugins": ["react-transform"],

View File

@ -227,7 +227,7 @@
"react/no-danger": 0,
"react/no-did-mount-set-state": 0,
"react/no-did-update-set-state": 1,
"react/no-multi-comp": 1,
"react/no-multi-comp": 0,
"react/no-unknown-property": 1,
"react/prop-types": [2, {"ignore": ["onMouseDown", "onTouchStart"]}],
"react/react-in-jsx-scope": 1,

View File

@ -1,4 +1,2 @@
import SlideLeft from './slide-left';
import SlideRight from './slide-right';
export default {SlideLeft, SlideRight};
export SlideLeft from './slide-left';
export SlideRight from './slide-right';

View File

@ -1,6 +1,6 @@
import React from 'react';
import Calendar from './calendar';
import Dialog from '../Dialog';
import Dialog from '../dialog';
import style from './style';
import time from '../utils/time';

View File

@ -25,7 +25,7 @@ class DropdownTest extends React.Component {
render () {
return (
<Dropdown
auto={true}
auto
onChange={this.handleChange}
source={countries}
value={this.state.value}

View File

@ -34,7 +34,7 @@ class List extends React.Component {
if (this.props.className) className += ` ${this.props.className}`;
return (
<ul className={className}>
{ this.renderItems() }
{this.renderItems()}
</ul>
);
}

View File

@ -0,0 +1,42 @@
import React from 'react';
import style from './style';
import Button from '../button';
import Link from '../link';
const Navigation = props => {
let className = `${style[props.type]}`;
if (props.className) className += ` ${props.className}`;
const buttons = props.actions.map((action, index) => {
return <Button className={style.button} key={index} {...action} />;
});
const links = props.routes.map((route, index) => {
return <Link className={style.link} key={index} {...route} />;
});
return (
<nav data-react-toolbox='navigation' className={className}>
{links}
{buttons}
{props.children}
</nav>
);
};
Navigation.propTypes = {
actions: React.PropTypes.array,
children: React.PropTypes.node,
className: React.PropTypes.string,
routes: React.PropTypes.array,
type: React.PropTypes.oneOf(['vertical', 'horizontal'])
};
Navigation.defaultProps = {
actions: [],
className: '',
type: 'horizontal',
routes: []
};
export default Navigation;

View File

@ -1,41 +1 @@
import React from 'react';
import style from './style';
import Button from '../button';
import Link from '../link';
const Navigation = props => {
let className = `${style[props.type]}`;
if (props.className) className += ` ${props.className}`;
const buttons = props.actions.map((action, index) => {
return <Button className={style.button} key={index} {...action} />;
});
const links = props.routes.map((route, index) => {
return <Link className={style.link} key={index} {...route} />;
});
return (
<nav data-react-toolbox='navigation' className={className}>
{ links }
{ buttons }
{ props.children }
</nav>
);
};
Navigation.propTypes = {
actions: React.PropTypes.array,
className: React.PropTypes.string,
routes: React.PropTypes.array,
type: React.PropTypes.oneOf(['vertical', 'horizontal'])
};
Navigation.defaultProps = {
actions: [],
className: '',
type: 'horizontal',
routes: []
};
export default Navigation;
export default from './Navigation.jsx';

View File

@ -0,0 +1,62 @@
import React from 'react';
import ReactDOM from 'react-dom';
import style from './style';
class Overlay extends React.Component {
static propTypes = {
active: React.PropTypes.bool,
children: React.PropTypes.node,
className: React.PropTypes.string,
onClick: React.PropTypes.func,
opacity: React.PropTypes.number
};
static defaultProps = {
opacity: 0.5
};
componentDidMount () {
this.app = document.querySelector('[data-react-toolbox="app"]') || document.body;
this.node = document.createElement('div');
this.node.setAttribute('data-react-toolbox', 'overlay');
this.app.appendChild(this.node);
this.handleRender();
}
componentDidUpdate () {
this.handleRender();
}
componentWillUnmount () {
ReactDOM.unmountComponentAtNode(this.node);
this.app.removeChild(this.node);
}
handleRender () {
let className = style.root;
const overlayStyle = {};
if (this.props.active) {
className += ` ${style.active}`;
overlayStyle.opacity = this.props.opacity;
}
if (this.props.className) className += ` ${className}`;
ReactDOM.render(
<div className={className}>
<div
className={style.overlay}
onClick={this.props.onClick}
style={overlayStyle}
/>
{this.props.children}
</div>
, this.node);
}
render () {
return React.DOM.noscript();
}
}
export default Overlay;

View File

@ -1,61 +1 @@
import React from 'react';
import ReactDOM from 'react-dom';
import style from './style';
class Overlay extends React.Component {
static propTypes = {
active: React.PropTypes.bool,
className: React.PropTypes.string,
onClick: React.PropTypes.func,
opacity: React.PropTypes.number
};
static defaultProps = {
opacity: 0.5
};
componentDidMount () {
this.app = document.querySelector('[data-react-toolbox="app"]') || document.body;
this.node = document.createElement('div');
this.node.setAttribute('data-react-toolbox', 'overlay');
this.app.appendChild(this.node);
this.handleRender();
}
componentDidUpdate () {
this.handleRender();
}
componentWillUnmount () {
ReactDOM.unmountComponentAtNode(this.node);
this.app.removeChild(this.node);
}
handleRender () {
let className = style.root;
const overlayStyle = {};
if (this.props.active) {
className += ` ${style.active}`;
overlayStyle.opacity = this.props.opacity;
}
if (this.props.className) className += ` ${className}`;
ReactDOM.render(
<div className={className}>
<div
className={style.overlay}
onClick={this.props.onClick}
style={overlayStyle}
/>
{this.props.children}
</div>
, this.node);
}
render () {
return React.DOM.noscript();
}
}
export default Overlay;
export default from './Overlay.jsx';

View File

@ -0,0 +1,89 @@
import React from 'react';
import style from './style';
import prefixer from '../utils/prefixer';
class ProgressBar extends React.Component {
static propTypes = {
buffer: React.PropTypes.number,
className: React.PropTypes.string,
max: React.PropTypes.number,
min: React.PropTypes.number,
mode: React.PropTypes.string,
multicolor: React.PropTypes.bool,
type: React.PropTypes.string,
value: React.PropTypes.number
};
static defaultProps = {
buffer: 0,
className: '',
max: 100,
min: 0,
mode: 'indeterminate',
multicolor: false,
type: 'linear',
value: 0
};
calculateRatio (value) {
if (value < this.props.min) return 0;
if (value > this.props.max) return 1;
return (value - this.props.min) / (this.props.max - this.props.min);
}
circularStyle () {
if (this.props.mode !== 'indeterminate') {
return {strokeDasharray: `${2 * Math.PI * 25 * this.calculateRatio(this.props.value)}, 400`};
}
}
linearStyle () {
if (this.props.mode !== 'indeterminate') {
return {
buffer: prefixer({transform: `scaleX(${this.calculateRatio(this.props.buffer)})`}),
value: prefixer({transform: `scaleX(${this.calculateRatio(this.props.value)})`})
};
} else {
return {};
}
}
renderCircular () {
return (
<svg className={style.circle}>
<circle className={style.path} style={this.circularStyle()} cx='30' cy='30' r='25' />
</svg>
);
}
renderLinear () {
const {buffer, value} = this.linearStyle();
return (
<div>
<span ref='buffer' data-ref='buffer' className={style.buffer} style={buffer}></span>
<span ref='value' data-ref='value' className={style.value} style={value}></span>
</div>
);
}
render () {
let className = this.props.type === 'linear' ? style.linear : style.circular;
if (this.props.mode) className += ` ${style[this.props.mode]}`;
if (this.props.multicolor) className += ` ${style.multicolor}`;
if (this.props.className) className += ` ${this.props.className}`;
return (
<div
data-react-toolbox='progress-bar'
aria-valuenow={this.props.value}
aria-valuemin={this.props.min}
aria-valuemax={this.props.max}
className={className}
>
{this.props.type === 'circular' ? this.renderCircular() : this.renderLinear()}
</div>
);
}
}
export default ProgressBar;

View File

@ -1,89 +1 @@
import React from 'react';
import style from './style';
import prefixer from '../utils/prefixer';
class ProgressBar extends React.Component {
static propTypes = {
buffer: React.PropTypes.number,
className: React.PropTypes.string,
max: React.PropTypes.number,
min: React.PropTypes.number,
mode: React.PropTypes.string,
multicolor: React.PropTypes.bool,
type: React.PropTypes.string,
value: React.PropTypes.number
};
static defaultProps = {
buffer: 0,
className: '',
max: 100,
min: 0,
mode: 'indeterminate',
multicolor: false,
type: 'linear',
value: 0
};
calculateRatio (value) {
if (value < this.props.min) return 0;
if (value > this.props.max) return 1;
return (value - this.props.min) / (this.props.max - this.props.min);
}
circularStyle () {
if (this.props.mode !== 'indeterminate') {
return {strokeDasharray: `${2 * Math.PI * 25 * this.calculateRatio(this.props.value)}, 400`};
}
}
linearStyle () {
if (this.props.mode !== 'indeterminate') {
return {
buffer: prefixer({transform: `scaleX(${this.calculateRatio(this.props.buffer)})`}),
value: prefixer({transform: `scaleX(${this.calculateRatio(this.props.value)})`})
};
} else {
return {};
}
}
renderCircular () {
return (
<svg className={style.circle}>
<circle className={style.path} style={this.circularStyle()} cx='30' cy='30' r='25' />
</svg>
);
}
renderLinear () {
const {buffer, value} = this.linearStyle();
return (
<div>
<span ref='buffer' data-ref='buffer' className={style.buffer} style={buffer}></span>
<span ref='value' data-ref='value' className={style.value} style={value}></span>
</div>
);
}
render () {
let className = this.props.type === 'linear' ? style.linear : style.circular;
if (this.props.mode) className += ` ${style[this.props.mode]}`;
if (this.props.multicolor) className += ` ${style.multicolor}`;
if (this.props.className) className += ` ${this.props.className}`;
return (
<div
data-react-toolbox='progress-bar'
aria-valuenow={this.props.value}
aria-valuemin={this.props.min}
aria-valuemax={this.props.max}
className={className}
>
{ this.props.type === 'circular' ? this.renderCircular() : this.renderLinear() }
</div>
);
}
}
export default ProgressBar;
export default from './ProgressBar.jsx';

View File

@ -41,6 +41,14 @@ class RadioButton extends React.Component {
if (!this.props.disabled) this.refs.ripple.start(event);
};
blur () {
this.refs.input.blur();
}
focus () {
this.refs.input.focus();
}
render () {
let labelClassName = style[this.props.disabled ? 'disabled' : 'field'];
const radioClassName = style[this.props.checked ? 'radio-checked' : 'radio'];
@ -59,18 +67,10 @@ class RadioButton extends React.Component {
<span role='radio' className={radioClassName} onMouseDown={this.handleMouseDown}>
<Ripple ref='ripple' role='ripple' className={style.ripple} spread={3} centered />
</span>
{ this.props.label ? <span className={style.text}>{this.props.label}</span> : null }
{this.props.label ? <span className={style.text}>{this.props.label}</span> : null}
</label>
);
}
blur () {
this.refs.input.blur();
}
focus () {
this.refs.input.focus();
}
}
export default RadioButton;

View File

@ -1,8 +1,9 @@
import React from 'react';
import RadioButton from './radio_button';
import RadioButton from './RadioButton';
class RadioGroup extends React.Component {
static propTypes = {
children: React.PropTypes.node,
className: React.PropTypes.string,
disabled: React.PropTypes.bool,
name: React.PropTypes.string,

View File

@ -1,4 +1,2 @@
import RadioButton from './radio_button';
import RadioGroup from './radio_group';
export default {RadioButton, RadioGroup};
export RadioButton from './RadioButton';
export RadioGroup from './RadioGroup';

View File

@ -0,0 +1,77 @@
import React from 'react';
import ReactDOM from 'react-dom';
import prefixer from '../utils/prefixer';
import style from './style';
class Ripple extends React.Component {
static propTypes = {
centered: React.PropTypes.bool,
className: React.PropTypes.string,
loading: React.PropTypes.bool,
spread: React.PropTypes.number
};
static defaultProps = {
centered: false,
className: '',
loading: false,
spread: 2
};
state = {
active: false,
left: null,
restarting: false,
top: null,
width: null
};
handleEnd = () => {
document.removeEventListener(this.touch ? 'touchend' : 'mouseup', this.handleEnd);
this.setState({active: false});
};
start = ({pageX, pageY}, touch = false) => {
this.touch = touch;
document.addEventListener(this.touch ? 'touchend' : 'mouseup', this.handleEnd);
const {top, left, width} = this._getDescriptor(pageX, pageY);
this.setState({active: false, restarting: true, top, left, width}, () => {
this.refs.ripple.offsetWidth; //eslint-disable-line no-unused-expressions
this.setState({active: true, restarting: false});
});
};
_getDescriptor (pageX, pageY) {
const {left, top, height, width} = ReactDOM.findDOMNode(this).getBoundingClientRect();
return {
left: this.props.centered ? 0 : pageX - left - width / 2 + window.scrollX,
top: this.props.centered ? 0 : pageY - top - height / 2 + window.scrollY,
width: width * this.props.spread
};
}
render () {
const { left, top, width } = this.state;
const scale = this.state.restarting ? 0 : 1;
let rippleStyle = {width, height: width};
if (!this.props.loading) {
rippleStyle = prefixer({
transform: `translate3d(${-width / 2 + left}px, ${-width / 2 + top}px, 0) scale(${scale})`
}, rippleStyle);
}
let className = style[this.props.loading ? 'loading' : 'normal'];
if (this.state.active) className += ` ${style.active}`;
if (this.state.restarting) className += ` ${style.restarting}`;
if (this.props.className) className += ` ${this.props.className}`;
return (
<span data-react-toolbox='ripple' className={style.wrapper}>
<span ref='ripple' role='ripple' className={className} style={rippleStyle} />
</span>
);
}
}
export default Ripple;

View File

@ -1,77 +1 @@
import React from 'react';
import ReactDOM from 'react-dom';
import prefixer from '../utils/prefixer';
import style from './style';
class Ripple extends React.Component {
static propTypes = {
centered: React.PropTypes.bool,
className: React.PropTypes.string,
loading: React.PropTypes.bool,
spread: React.PropTypes.number
};
static defaultProps = {
centered: false,
className: '',
loading: false,
spread: 2
};
state = {
active: false,
left: null,
restarting: false,
top: null,
width: null
};
handleEnd = () => {
document.removeEventListener(this.touch ? 'touchend' : 'mouseup', this.handleEnd);
this.setState({active: false});
};
start = ({pageX, pageY}, touch = false) => {
this.touch = touch;
document.addEventListener(this.touch ? 'touchend' : 'mouseup', this.handleEnd);
const {top, left, width} = this._getDescriptor(pageX, pageY);
this.setState({active: false, restarting: true, top, left, width}, () => {
this.refs.ripple.offsetWidth; //eslint-disable-line no-unused-expressions
this.setState({active: true, restarting: false});
});
};
_getDescriptor (pageX, pageY) {
const {left, top, height, width} = ReactDOM.findDOMNode(this).getBoundingClientRect();
return {
left: this.props.centered ? 0 : pageX - left - width / 2 + window.scrollX,
top: this.props.centered ? 0 : pageY - top - height / 2 + window.scrollY,
width: width * this.props.spread
};
}
render () {
const { left, top, width } = this.state;
const scale = this.state.restarting ? 0 : 1;
let rippleStyle = {width, height: width};
if (!this.props.loading) {
rippleStyle = prefixer({
transform: `translate3d(${-width / 2 + left}px, ${-width / 2 + top}px, 0) scale(${scale})`
}, rippleStyle);
}
let className = style[this.props.loading ? 'loading' : 'normal'];
if (this.state.active) className += ` ${style.active}`;
if (this.state.restarting) className += ` ${style.restarting}`;
if (this.props.className) className += ` ${this.props.className}`;
return (
<span data-react-toolbox='ripple' className={style.wrapper}>
<span ref='ripple' role='ripple' className={className} style={rippleStyle} />
</span>
);
}
}
export default Ripple;
export default from './Ripple';

View File

@ -0,0 +1,279 @@
import React from 'react';
import ReactDOM from 'react-dom';
import style from './style';
import events from '../utils/events';
import prefixer from '../utils/prefixer';
import utils from '../utils/utils';
import ProgressBar from '../progress_bar';
import Input from '../input';
class Slider extends React.Component {
static propTypes = {
className: React.PropTypes.string,
editable: React.PropTypes.bool,
max: React.PropTypes.number,
min: React.PropTypes.number,
onChange: React.PropTypes.func,
pinned: React.PropTypes.bool,
snaps: React.PropTypes.bool,
step: React.PropTypes.number,
value: React.PropTypes.number
};
static defaultProps = {
className: '',
editable: false,
max: 100,
min: 0,
pinned: false,
snaps: false,
step: 0.01,
value: 0
};
state = {
inputFocused: false,
inputValue: null,
sliderLength: 0,
sliderStart: 0
};
componentDidMount () {
window.addEventListener('resize', this.handleResize);
this.handleResize();
}
shouldComponentUpdate (nextProps, nextState) {
if (!this.state.inputFocused && nextState.inputFocused) return false;
if (this.state.inputFocused && this.props.value !== nextProps.value) {
this.setState({inputValue: this.valueForInput(nextProps.value)});
return false;
}
return true;
}
componentWillUnmount () {
window.removeEventListener('resize', this.handleResize);
}
handleInputFocus = () => {
this.setState({
inputFocused: true,
inputValue: this.valueForInput(this.props.value)
});
};
handleInputChange = (event) => {
this.setState({inputValue: event.target.value});
};
handleInputBlur = () => {
const value = this.state.inputValue || 0;
this.setState({inputFocused: false, inputValue: null}, () => {
this.props.onChange(this.trimValue(value));
});
};
handleKeyDown = (event) => {
if ([13, 27].indexOf(event.keyCode) !== -1) {
this.refs.input.blur();
ReactDOM.findDOMNode(this).blur();
}
if (event.keyCode === 38) this.addToValue(this.props.step);
if (event.keyCode === 40) this.addToValue(-this.props.step);
};
handleMouseDown = (event) => {
if (this.state.inputFocused) this.refs.input.blur();
events.addEventsToDocument(this.getMouseEventMap());
this.start(events.getMousePosition(event));
events.pauseEvent(event);
};
handleMouseMove = (event) => {
events.pauseEvent(event);
this.move(events.getMousePosition(event));
};
handleMouseUp = () => {
this.end(this.getMouseEventMap());
};
handleResize = (event, callback) => {
const {left, right} = ReactDOM.findDOMNode(this.refs.progressbar).getBoundingClientRect();
const cb = callback || () => {};
this.setState({sliderStart: left, sliderLength: right - left}, cb);
};
handleSliderBlur = () => {
events.removeEventsFromDocument(this.getKeyboardEvents());
};
handleSliderFocus = () => {
events.addEventsToDocument(this.getKeyboardEvents());
};
handleTouchEnd = () => {
this.end(this.getTouchEventMap());
};
handleTouchMove = (event) => {
this.move(events.getTouchPosition(event));
};
handleTouchStart = (event) => {
if (this.state.inputFocused) this.refs.input.blur();
this.start(events.getTouchPosition(event));
events.addEventsToDocument(this.getTouchEventMap());
events.pauseEvent(event);
};
addToValue (increment) {
let value = this.state.inputFocused ? parseFloat(this.state.inputValue) : this.props.value;
value = this.trimValue(value + increment);
if (value !== this.props.value) this.props.onChange(value);
}
getKeyboardEvents () {
return {
keydown: this.handleKeyDown
};
}
getMouseEventMap () {
return {
mousemove: this.handleMouseMove,
mouseup: this.handleMouseUp
};
}
getTouchEventMap () {
return {
touchmove: this.handleTouchMove,
touchend: this.handleTouchEnd
};
}
end (revents) {
events.removeEventsFromDocument(revents);
this.setState({ pressed: false });
}
knobOffset () {
const { max, min } = this.props;
return this.state.sliderLength * (this.props.value - min) / (max - min);
}
move (position) {
const newValue = this.positionToValue(position);
if (newValue !== this.props.value) this.props.onChange(newValue);
}
positionToValue (position) {
const { sliderStart: start, sliderLength: length } = this.state;
const { max, min } = this.props;
return this.trimValue((position.x - start) / length * (max - min) + min);
}
start (position) {
this.handleResize(null, () => {
this.setState({pressed: true});
this.props.onChange(this.positionToValue(position));
});
}
stepDecimals () {
return (this.props.step.toString().split('.')[1] || []).length;
}
trimValue (value) {
if (value < this.props.min) return this.props.min;
if (value > this.props.max) return this.props.max;
return utils.round(value, this.stepDecimals());
}
valueForInput (value) {
const decimals = this.stepDecimals();
return decimals > 0 ? value.toFixed(decimals) : value.toString();
}
renderSnaps () {
if (this.props.snaps) {
return (
<div ref='snaps' className={style.snaps}>
{utils.range(0, (this.props.max - this.props.min) / this.props.step).map(i => {
return <div key={`span-${i}`} className={style.snap}></div>;
})}
</div>
);
}
}
renderInput () {
if (this.props.editable) {
const value = this.state.inputFocused ? this.state.inputValue : this.valueForInput(this.props.value);
return (
<Input
ref='input'
className={style.input}
onFocus={this.handleInputFocus}
onChange={this.handleInputChange}
onBlur={this.handleInputBlur}
value={value}
/>
);
}
}
render () {
const knobStyles = prefixer({transform: `translateX(${this.knobOffset()}px)`});
let className = this.props.className;
if (this.props.editable) className += ` ${style.editable}`;
if (this.props.pinned) className += ` ${style.pinned}`;
if (this.state.pressed) className += ` ${style.pressed}`;
if (this.props.value === this.props.min) className += ` ${style.ring}`;
return (
<div
className={style.root + className}
data-react-toolbox='slider'
onBlur={this.handleSliderBlur}
onFocus={this.handleSliderFocus}
tabIndex='0'
>
<div
ref='slider'
className={style.container}
onMouseDown={this.handleMouseDown}
onTouchStart={this.handleTouchStart}
>
<div
ref='knob'
className={style.knob}
onMouseDown={this.handleMouseDown}
onTouchStart={this.handleTouchStart}
style={knobStyles}
>
<div className={style.innerknob} data-value={parseInt(this.props.value)}></div>
</div>
<div className={style.progress}>
<ProgressBar
ref='progressbar'
className={style.innerprogress}
max={this.props.max}
min={this.props.min}
mode='determinate'
value={this.props.value}
/>
{this.renderSnaps()}
</div>
</div>
{this.renderInput()}
</div>
);
}
}
export default Slider;

View File

@ -1,279 +1 @@
import React from 'react';
import ReactDOM from 'react-dom';
import style from './style';
import events from '../utils/events';
import prefixer from '../utils/prefixer';
import utils from '../utils/utils';
import ProgressBar from '../progress_bar';
import Input from '../input';
class Slider extends React.Component {
static propTypes = {
className: React.PropTypes.string,
editable: React.PropTypes.bool,
max: React.PropTypes.number,
min: React.PropTypes.number,
onChange: React.PropTypes.func,
pinned: React.PropTypes.bool,
snaps: React.PropTypes.bool,
step: React.PropTypes.number,
value: React.PropTypes.number
};
static defaultProps = {
className: '',
editable: false,
max: 100,
min: 0,
pinned: false,
snaps: false,
step: 0.01,
value: 0
};
state = {
inputFocused: false,
inputValue: null,
sliderLength: 0,
sliderStart: 0
};
shouldComponentUpdate (nextProps, nextState) {
if (!this.state.inputFocused && nextState.inputFocused) return false;
if (this.state.inputFocused && this.props.value !== nextProps.value) {
this.setState({inputValue: this.valueForInput(nextProps.value)});
return false;
}
return true;
}
componentDidMount () {
window.addEventListener('resize', this.handleResize);
this.handleResize();
}
componentWillUnmount () {
window.removeEventListener('resize', this.handleResize);
}
handleInputFocus = () => {
this.setState({
inputFocused: true,
inputValue: this.valueForInput(this.props.value)
});
};
handleInputChange = (event) => {
this.setState({inputValue: event.target.value});
};
handleInputBlur = () => {
const value = this.state.inputValue || 0;
this.setState({inputFocused: false, inputValue: null}, () => {
this.props.onChange(this.trimValue(value));
});
};
handleKeyDown = (event) => {
if ([13, 27].indexOf(event.keyCode) !== -1) {
this.refs.input.blur();
ReactDOM.findDOMNode(this).blur();
}
if (event.keyCode === 38) this.addToValue(this.props.step);
if (event.keyCode === 40) this.addToValue(-this.props.step);
};
handleMouseDown = (event) => {
if (this.state.inputFocused) this.refs.input.blur();
events.addEventsToDocument(this.getMouseEventMap());
this.start(events.getMousePosition(event));
events.pauseEvent(event);
};
handleMouseMove = (event) => {
events.pauseEvent(event);
this.move(events.getMousePosition(event));
};
handleMouseUp = () => {
this.end(this.getMouseEventMap());
};
handleResize = (event, callback) => {
const {left, right} = ReactDOM.findDOMNode(this.refs.progressbar).getBoundingClientRect();
const cb = callback || () => {};
this.setState({sliderStart: left, sliderLength: right - left}, cb);
};
handleSliderBlur = () => {
events.removeEventsFromDocument(this.getKeyboardEvents());
};
handleSliderFocus = () => {
events.addEventsToDocument(this.getKeyboardEvents());
};
handleTouchEnd = () => {
this.end(this.getTouchEventMap());
};
handleTouchMove = (event) => {
this.move(events.getTouchPosition(event));
};
handleTouchStart = (event) => {
if (this.state.inputFocused) this.refs.input.blur();
this.start(events.getTouchPosition(event));
events.addEventsToDocument(this.getTouchEventMap());
events.pauseEvent(event);
};
addToValue (increment) {
let value = this.state.inputFocused ? parseFloat(this.state.inputValue) : this.props.value;
value = this.trimValue(value + increment);
if (value !== this.props.value) this.props.onChange(value);
}
getKeyboardEvents () {
return {
keydown: this.handleKeyDown
};
}
getMouseEventMap () {
return {
mousemove: this.handleMouseMove,
mouseup: this.handleMouseUp
};
}
getTouchEventMap () {
return {
touchmove: this.handleTouchMove,
touchend: this.handleTouchEnd
};
}
end (revents) {
events.removeEventsFromDocument(revents);
this.setState({ pressed: false });
}
knobOffset () {
const { max, min } = this.props;
return this.state.sliderLength * (this.props.value - min) / (max - min);
}
move (position) {
const newValue = this.positionToValue(position);
if (newValue !== this.props.value) this.props.onChange(newValue);
}
positionToValue (position) {
const { sliderStart: start, sliderLength: length } = this.state;
const { max, min } = this.props;
return this.trimValue((position.x - start) / length * (max - min) + min);
}
start (position) {
this.handleResize(null, () => {
this.setState({pressed: true});
this.props.onChange(this.positionToValue(position));
});
}
stepDecimals () {
return (this.props.step.toString().split('.')[1] || []).length;
}
trimValue (value) {
if (value < this.props.min) return this.props.min;
if (value > this.props.max) return this.props.max;
return utils.round(value, this.stepDecimals());
}
valueForInput (value) {
const decimals = this.stepDecimals();
return decimals > 0 ? value.toFixed(decimals) : value.toString();
}
renderSnaps () {
if (this.props.snaps) {
return (
<div ref='snaps' className={style.snaps}>
{ utils.range(0, (this.props.max - this.props.min) / this.props.step).map(i => {
return <div key={`span-${i}`} className={style.snap}></div>;
}) }
</div>
);
}
}
renderInput () {
if (this.props.editable) {
const value = this.state.inputFocused ? this.state.inputValue : this.valueForInput(this.props.value);
return (
<Input
ref='input'
className={style.input}
onFocus={this.handleInputFocus}
onChange={this.handleInputChange}
onBlur={this.handleInputBlur}
value={value}
/>
);
}
}
render () {
const knobStyles = prefixer({transform: `translateX(${this.knobOffset()}px)`});
let className = this.props.className;
if (this.props.editable) className += ` ${style.editable}`;
if (this.props.pinned) className += ` ${style.pinned}`;
if (this.state.pressed) className += ` ${style.pressed}`;
if (this.props.value === this.props.min) className += ` ${style.ring}`;
return (
<div
className={style.root + className}
data-react-toolbox='slider'
onBlur={this.handleSliderBlur}
onFocus={this.handleSliderFocus}
tabIndex='0'
>
<div
ref='slider'
className={style.container}
onMouseDown={this.handleMouseDown}
onTouchStart={this.handleTouchStart}
>
<div
ref='knob'
className={style.knob}
onMouseDown={this.handleMouseDown}
onTouchStart={this.handleTouchStart}
style={knobStyles}
>
<div className={style.innerknob} data-value={parseInt(this.props.value)}></div>
</div>
<div className={style.progress}>
<ProgressBar
ref='progressbar'
className={style.innerprogress}
max={this.props.max}
min={this.props.min}
mode='determinate'
value={this.props.value}
/>
{ this.renderSnaps() }
</div>
</div>
{ this.renderInput() }
</div>
);
}
}
export default Slider;
export default from './Slider';

View File

@ -0,0 +1,57 @@
import React from 'react';
import Button from '../button';
import FontIcon from '../font_icon';
import Overlay from '../overlay';
import style from './style';
class Snackbar extends React.Component {
static propTypes = {
action: React.PropTypes.string,
active: React.PropTypes.bool,
className: React.PropTypes.string,
icon: React.PropTypes.string,
label: React.PropTypes.string.isRequired,
onClick: React.PropTypes.func,
onTimeout: React.PropTypes.func,
timeout: React.PropTypes.number,
type: React.PropTypes.string
};
componentDidUpdate () {
if (this.props.active && this.props.timeout) {
setTimeout(() => {
this.props.onTimeout();
}, this.props.timeout);
}
}
renderButton () {
if (this.props.action) {
return (
<Button
className={style.button}
label={this.props.action}
onClick={this.props.onClick}
/>
);
}
}
render () {
let className = `${style.root} ${style[this.props.type]}`;
if (this.props.active) className += ` ${style.active}`;
if (this.props.className) className += ` ${this.props.className}`;
return (
<Overlay active={this.props.active} opacity={0}>
<div data-react-toolbox='snackbar' className={className}>
{this.props.icon ? <FontIcon value={this.props.icon} className={style.icon} /> : null}
<span className={style.label}>{this.props.label}</span>
{this.renderButton()}
</div>
</Overlay>
);
}
}
export default Snackbar;

View File

@ -1,57 +1 @@
import React from 'react';
import Button from '../button';
import FontIcon from '../font_icon';
import Overlay from '../overlay';
import style from './style';
class Snackbar extends React.Component {
static propTypes = {
action: React.PropTypes.string,
active: React.PropTypes.bool,
className: React.PropTypes.string,
icon: React.PropTypes.string,
label: React.PropTypes.string.isRequired,
onClick: React.PropTypes.func,
onTimeout: React.PropTypes.func,
timeout: React.PropTypes.number,
type: React.PropTypes.string
};
componentDidUpdate () {
if (this.props.active && this.props.timeout) {
setTimeout(() => {
this.props.onTimeout();
}, this.props.timeout);
}
}
renderButton () {
if (this.props.action) {
return (
<Button
className={style.button}
label={this.props.action}
onClick={this.props.onClick}
/>
);
}
}
render () {
let className = `${style.root} ${style[this.props.type]}`;
if (this.props.active) className += ` ${style.active}`;
if (this.props.className) className += ` ${this.props.className}`;
return (
<Overlay active={this.props.active} opacity={0}>
<div data-react-toolbox='snackbar' className={className}>
{ this.props.icon ? <FontIcon value={this.props.icon} className={style.icon} /> : null }
<span className={style.label}>{this.props.label}</span>
{ this.renderButton() }
</div>
</Overlay>
);
}
}
export default Snackbar;
export default from './Snackbar.jsx';

View File

@ -0,0 +1,76 @@
import React from 'react';
import Ripple from '../ripple';
import style from './style';
import events from '../utils/events';
class Switch extends React.Component {
static propTypes = {
checked: React.PropTypes.bool,
className: React.PropTypes.string,
disabled: React.PropTypes.bool,
label: React.PropTypes.string,
name: React.PropTypes.string,
onBlur: React.PropTypes.func,
onChange: React.PropTypes.func,
onFocus: React.PropTypes.func
};
static defaultProps = {
checked: false,
className: '',
disabled: false
};
handleChange = (event) => {
events.pauseEvent(event);
if (this.props.onChange && !this.props.disabled) this.props.onChange(event);
};
handleInputClick = (event) => {
events.pauseEvent(event);
};
handleMouseDown = (event) => {
if (!this.props.disabled) this.refs.ripple.start(event);
};
blur () {
this.refs.input.blur();
}
focus () {
this.refs.input.focus();
}
render () {
let labelClassName = style[this.props.disabled ? 'disabled' : 'field'];
const switchClassName = style[this.props.checked ? 'on' : 'off'];
if (this.props.className) labelClassName += ` ${this.props.className}`;
return (
<label
data-react-toolbox='checkbox'
className={labelClassName}
onClick={this.handleChange}
>
<input
{...this.props}
ref='input'
checked={this.props.checked}
className={style.input}
onChange={this.handleChange}
onClick={this.handleInputClick}
type='checkbox'
/>
<span role='switch' className={switchClassName}>
<span role='thumb' className={style.thumb} onMouseDown={this.handleMouseDown}>
<Ripple ref='ripple' role='ripple' className={style.ripple} spread={2.4} centered />
</span>
</span>
{this.props.label ? <span className={style.text}>{this.props.label}</span> : null}
</label>
);
}
}
export default Switch;

View File

@ -1,76 +1 @@
import React from 'react';
import Ripple from '../ripple';
import style from './style';
import events from '../utils/events';
class Switch extends React.Component {
static propTypes = {
checked: React.PropTypes.bool,
className: React.PropTypes.string,
disabled: React.PropTypes.bool,
label: React.PropTypes.string,
name: React.PropTypes.string,
onBlur: React.PropTypes.func,
onChange: React.PropTypes.func,
onFocus: React.PropTypes.func
};
static defaultProps = {
checked: false,
className: '',
disabled: false
};
handleChange = (event) => {
events.pauseEvent(event);
if (this.props.onChange && !this.props.disabled) this.props.onChange(event);
};
handleInputClick = (event) => {
events.pauseEvent(event);
};
handleMouseDown = (event) => {
if (!this.props.disabled) this.refs.ripple.start(event);
};
render () {
let labelClassName = style[this.props.disabled ? 'disabled' : 'field'];
const switchClassName = style[this.props.checked ? 'on' : 'off'];
if (this.props.className) labelClassName += ` ${this.props.className}`;
return (
<label
data-react-toolbox='checkbox'
className={labelClassName}
onClick={this.handleChange}
>
<input
{...this.props}
ref='input'
checked={this.props.checked}
className={style.input}
onChange={this.handleChange}
onClick={this.handleInputClick}
type='checkbox'
/>
<span role='switch' className={switchClassName}>
<span role='thumb' className={style.thumb} onMouseDown={this.handleMouseDown}>
<Ripple ref='ripple' role='ripple' className={style.ripple} spread={2.4} centered />
</span>
</span>
{ this.props.label ? <span className={style.text}>{this.props.label}</span> : null }
</label>
);
}
blur () {
this.refs.input.blur();
}
focus () {
this.refs.input.focus();
}
}
export default Switch;
export default from './Switch.jsx';

View File

@ -0,0 +1,95 @@
import React from 'react';
import TableHead from './TableHead';
import TableRow from './TableRow';
import style from './style';
class Table extends React.Component {
static propTypes = {
className: React.PropTypes.string,
heading: React.PropTypes.bool,
model: React.PropTypes.object,
onChange: React.PropTypes.func,
onSelect: React.PropTypes.func,
selectable: React.PropTypes.bool,
selected: React.PropTypes.array,
source: React.PropTypes.array
};
static defaultProps = {
className: '',
heading: true,
selectable: true,
selected: [],
source: []
};
handleFullSelect = () => {
if (this.props.onSelect) {
const {source, selected} = this.props;
const newSelected = source.length === selected.length ? [] : source.map((i, idx) => idx);
this.props.onSelect(newSelected);
}
};
handleRowSelect = (index) => {
if (this.props.onSelect) {
const position = this.props.selected.indexOf(index);
const newSelected = [...this.props.selected];
if (position !== -1) newSelected.splice(position, 1); else newSelected.push(index);
this.props.onSelect(newSelected);
}
};
handleRowChange = (index, key, value) => {
if (this.props.onChange) {
this.props.onChange(index, key, value);
}
};
renderHead () {
if (this.props.heading) {
const {model, selected, source, selectable} = this.props;
const isSelected = selected.length === source.length;
return (
<TableHead
model={model}
onSelect={this.handleFullSelect}
selectable={selectable}
selected={isSelected}
/>
);
}
}
renderBody () {
const rows = this.props.source.map((data, idx) => {
return (
<TableRow
data={data}
index={idx}
key={idx}
model={this.props.model}
onChange={this.handleRowChange.bind(this, idx)}
onSelect={this.handleRowSelect.bind(this, idx)}
selectable={this.props.selectable}
selected={this.props.selected.indexOf(idx) !== -1}
/>
);
});
return <tbody>{rows}</tbody>;
}
render () {
let className = style.root;
if (this.props.className) className += ` ${this.props.className}`;
return (
<table data-react-toolbox='table' className={className}>
{this.renderHead()}
{this.renderBody()}
</table>
);
}
}
export default Table;

View File

@ -6,6 +6,7 @@ import style from './style';
class TableRow extends React.Component {
static propTypes = {
data: React.PropTypes.object,
model: React.PropTypes.object,
onChange: React.PropTypes.func,
onSelect: React.PropTypes.func,
selectable: React.PropTypes.bool,
@ -63,8 +64,8 @@ class TableRow extends React.Component {
return (
<tr data-react-toolbox-table='row' className={className}>
{ this.renderSelectCell() }
{ this.renderCells() }
{this.renderSelectCell()}
{this.renderCells()}
</tr>
);
}

View File

@ -1,95 +1 @@
import React from 'react';
import TableHead from './head';
import TableRow from './row';
import style from './style';
class Table extends React.Component {
static propTypes = {
className: React.PropTypes.string,
heading: React.PropTypes.bool,
model: React.PropTypes.object,
onChange: React.PropTypes.func,
onSelect: React.PropTypes.func,
selectable: React.PropTypes.bool,
selected: React.PropTypes.array,
source: React.PropTypes.array
};
static defaultProps = {
className: '',
heading: true,
selectable: true,
selected: [],
source: []
};
handleFullSelect = () => {
if (this.props.onSelect) {
const {source, selected} = this.props;
const newSelected = source.length === selected.length ? [] : source.map((i, idx) => idx);
this.props.onSelect(newSelected);
}
};
handleRowSelect = (index) => {
if (this.props.onSelect) {
const position = this.props.selected.indexOf(index);
const newSelected = [...this.props.selected];
if (position !== -1) newSelected.splice(position, 1); else newSelected.push(index);
this.props.onSelect(newSelected);
}
};
handleRowChange = (index, key, value) => {
if (this.props.onChange) {
this.props.onChange(index, key, value);
}
};
renderHead () {
if (this.props.heading) {
const {model, selected, source, selectable} = this.props;
const isSelected = selected.length === source.length;
return (
<TableHead
model={model}
onSelect={this.handleFullSelect}
selectable={selectable}
selected={isSelected}
/>
);
}
}
renderBody () {
const rows = this.props.source.map((data, idx) => {
return (
<TableRow
data={data}
index={idx}
key={idx}
model={this.props.model}
onChange={this.handleRowChange.bind(this, idx)}
onSelect={this.handleRowSelect.bind(this, idx)}
selectable={this.props.selectable}
selected={this.props.selected.indexOf(idx) !== -1}
/>
);
});
return <tbody>{rows}</tbody>;
}
render () {
let className = style.root;
if (this.props.className) className += ` ${this.props.className}`;
return (
<table data-react-toolbox='table' className={className}>
{ this.renderHead() }
{ this.renderBody() }
</table>
);
}
}
export default Table;
export default from './Table.jsx';

View File

@ -39,7 +39,7 @@ class TableTest extends React.Component {
model={UserModel}
onChange={this.handleChange}
onSelect={this.handleSelect}
selectable={true}
selectable
selected={this.state.selected}
source={this.state.source}
/>

View File

@ -4,6 +4,7 @@ import style from './style';
class TabContent extends React.Component {
static propTypes = {
active: React.PropTypes.bool,
children: React.PropTypes.node,
className: React.PropTypes.string,
tabIndex: React.PropTypes.number
};

View File

@ -1,4 +1,2 @@
import Tabs from './tabs';
import Tab from './tab';
export default { Tabs, Tab };
export Tabs from './Tabs.jsx';
export Tab from './Tab.jsx';

View File

@ -1,12 +1,13 @@
import React from 'react';
import Tab from './tab';
import Content from './content';
import Tab from './Tab';
import TabContent from './TabContent';
import style from './style';
class Tabs extends React.Component {
static propTypes = {
index: React.PropTypes.number,
children: React.PropTypes.node,
className: React.PropTypes.string,
index: React.PropTypes.number,
onChange: React.PropTypes.func
};
@ -18,16 +19,16 @@ class Tabs extends React.Component {
pointer: {}
};
componentWillReceiveProps (nextProps) {
this.updatePointer(nextProps.index);
}
componentDidMount () {
setTimeout(() => {
this.updatePointer(this.props.index);
}, 100);
}
componentWillReceiveProps (nextProps) {
this.updatePointer(nextProps.index);
}
handleHeaderClick = (idx) => {
if (this.props.onChange) this.props.onChange(idx);
};
@ -40,9 +41,9 @@ class Tabs extends React.Component {
if (item.type === Tab) {
headers.push(item);
if (item.props.children) {
contents.push(<Content children={item.props.children}/>);
contents.push(<TabContent children={item.props.children}/>);
}
} else if (item.type === Content) {
} else if (item.type === TabContent) {
contents.push(item);
}
});

View File

@ -0,0 +1,65 @@
import React from 'react';
import events from '../utils/events';
import time from '../utils/time';
import style from './style';
import Input from '../input';
import TimePickerDialog from './TimePickerDialog';
class TimePicker extends React.Component {
static propTypes = {
className: React.PropTypes.string,
format: React.PropTypes.oneOf(['24hr', 'ampm']),
label: React.PropTypes.string,
onChange: React.PropTypes.func,
value: React.PropTypes.object
};
static defaultProps = {
className: '',
format: '24hr'
};
state = {
active: false
};
handleDismiss = () => {
this.setState({active: false});
};
handleInputMouseDown = (event) => {
events.pauseEvent(event);
this.setState({active: true});
};
handleSelect = (value) => {
if (this.props.onChange) this.props.onChange(value);
this.setState({active: false});
};
render () {
const { value, format } = this.props;
const formattedTime = value ? time.formatTime(value, format) : null;
return (
<div data-react-toolbox='time-picker'>
<Input
className={style.input}
label={this.props.label}
onMouseDown={this.handleInputMouseDown}
readOnly
type='text'
value={formattedTime}
/>
<TimePickerDialog
active={this.state.active}
format={format}
onDismiss={this.handleDismiss}
onSelect={this.handleSelect}
value={this.props.value}
/>
</div>
);
}
}
export default TimePicker;

View File

@ -78,13 +78,13 @@ class TimePickerDialog extends React.Component {
<Dialog active={this.props.active} className={className} actions={this.actions}>
<header className={style.header}>
<span className={style.hours} onClick={this.switchDisplay.bind(this, 'hours')}>
{ ('0' + this.formatHours()).slice(-2) }
{('0' + this.formatHours()).slice(-2)}
</span>
<span className={style.separator}>:</span>
<span className={style.minutes} onClick={this.switchDisplay.bind(this, 'minutes')}>
{ ('0' + this.state.displayTime.getMinutes()).slice(-2) }
{('0' + this.state.displayTime.getMinutes()).slice(-2)}
</span>
{ this.renderAMPMLabels() }
{this.renderAMPMLabels()}
</header>
<Clock
ref='clock'

View File

@ -0,0 +1,106 @@
import React from 'react';
import style from './style';
import time from '../../utils/time';
import Hours from './Hours';
import Minutes from './Minutes';
class Clock extends React.Component {
static propTypes = {
className: React.PropTypes.string,
display: React.PropTypes.oneOf(['hours', 'minutes']),
format: React.PropTypes.oneOf(['24hr', 'ampm']),
onChange: React.PropTypes.func,
time: React.PropTypes.object
};
static defaultProps = {
className: '',
display: 'hours',
format: '24hr',
time: new Date()
};
state = {
center: {x: null, y: null},
radius: 0
};
componentDidMount () {
window.addEventListener('resize', this.handleCalculateShape);
this.handleCalculateShape();
}
componentWillUnmount () {
window.removeEventListener('resize', this.handleCalculateShape);
}
handleHourChange = (hours) => {
if (this.props.time.getHours() !== hours) {
this.props.onChange(time.setHours(this.props.time, this.adaptHourToFormat(hours)));
}
};
handleMinuteChange = (minutes) => {
if (this.props.time.getMinutes() !== minutes) {
this.props.onChange(time.setMinutes(this.props.time, minutes));
}
};
handleCalculateShape = () => {
const { top, left, width } = this.refs.wrapper.getBoundingClientRect();
this.setState({
center: { x: left + width / 2, y: top + width / 2 },
radius: width / 2
});
};
adaptHourToFormat (hour) {
if (this.props.format === 'ampm') {
if (time.getTimeMode(this.props.time) === 'pm') {
return hour < 12 ? hour + 12 : hour;
} else {
return hour === 12 ? 0 : hour;
}
} else {
return hour;
}
}
renderHours () {
return (
<Hours
center={this.state.center}
format={this.props.format}
onChange={this.handleHourChange}
radius={this.state.radius}
selected={this.props.time.getHours()}
spacing={this.state.radius * 0.18}
/>
);
}
renderMinutes () {
return (
<Minutes
center={this.state.center}
onChange={this.handleMinuteChange}
radius={this.state.radius}
selected={this.props.time.getMinutes()}
spacing={this.state.radius * 0.18}
/>
);
}
render () {
return (
<div data-react-toolbox='clock' className={style.root}>
<div ref='wrapper' className={style.wrapper} style={{height: this.state.radius * 2}}>
{this.props.display === 'hours' ? this.renderHours() : null}
{this.props.display === 'minutes' ? this.renderMinutes() : null}
</div>
</div>
);
}
}
export default Clock;

View File

@ -2,6 +2,14 @@ import React from 'react';
import style from './style';
class Face extends React.Component {
static propTypes = {
active: React.PropTypes.number,
numbers: React.PropTypes.array,
radius: React.PropTypes.number,
spacing: React.PropTypes.number,
twoDigits: React.PropTypes.bool
};
static defaultProps = {
active: null,
numbers: [],
@ -33,7 +41,7 @@ class Face extends React.Component {
style={this.numberStyle(this.props.radius - this.props.spacing, idx + 1)}
key={number}
>
{ this.props.twoDigits ? ('0' + number).slice(-2) : number }
{this.props.twoDigits ? ('0' + number).slice(-2) : number}
</span>
);
}
@ -47,7 +55,7 @@ class Face extends React.Component {
onMouseDown={this.props.onMouseDown}
style={this.faceStyle()}
>
{ this.props.numbers.map(this.renderNumber.bind(this))}
{this.props.numbers.map(this.renderNumber.bind(this))}
</div>
);
}

View File

@ -6,10 +6,13 @@ import utils from '../../utils/utils';
class Hand extends React.Component {
static propTypes = {
className: React.PropTypes.string,
angle: React.PropTypes.number,
className: React.PropTypes.string,
length: React.PropTypes.number,
onMove: React.PropTypes.func,
onMoved: React.PropTypes.func
onMoved: React.PropTypes.func,
origin: React.PropTypes.object,
step: React.PropTypes.number
};
static defaultProps = {

View File

@ -1,7 +1,7 @@
import React from 'react';
import utils from '../../utils/utils';
import Face from './face';
import Hand from './hand';
import Face from './Face';
import Hand from './Hand';
const outerNumbers = [0, ...utils.range(13, 24)];
const innerNumbers = [12, ...utils.range(1, 12)];
@ -10,10 +10,13 @@ const step = 360 / 12;
class Hours extends React.Component {
static propTypes = {
center: React.PropTypes.object,
format: React.PropTypes.oneOf(['24hr', 'ampm']),
onChange: React.PropTypes.func,
onHandMoved: React.PropTypes.func,
selected: React.PropTypes.number
radius: React.PropTypes.number,
selected: React.PropTypes.number,
spacing: React.PropTypes.number
};
state = {
@ -77,7 +80,7 @@ class Hours extends React.Component {
twoDigits={is24hr}
active={is24hr ? selected : (selected % 12 || 12)}
/>
{ this.renderInnerFace(radius - spacing * innerSpacing) }
{this.renderInnerFace(radius - spacing * innerSpacing)}
<Hand ref='hand'
angle={selected * step}
length={(this.state.inner ? radius - spacing * innerSpacing : radius) - spacing}

View File

@ -1,106 +1 @@
import React from 'react';
import style from './style';
import time from '../../utils/time';
import Hours from './hours';
import Minutes from './minutes';
class Clock extends React.Component {
static propTypes = {
className: React.PropTypes.string,
display: React.PropTypes.oneOf(['hours', 'minutes']),
format: React.PropTypes.oneOf(['24hr', 'ampm']),
time: React.PropTypes.object,
onChange: React.PropTypes.func
};
static defaultProps = {
className: '',
display: 'hours',
format: '24hr',
time: new Date()
};
state = {
center: {x: null, y: null},
radius: 0
};
componentDidMount () {
window.addEventListener('resize', this.handleCalculateShape);
this.handleCalculateShape();
}
componentWillUnmount () {
window.removeEventListener('resize', this.handleCalculateShape);
}
handleHourChange = (hours) => {
if (this.props.time.getHours() !== hours) {
this.props.onChange(time.setHours(this.props.time, this.adaptHourToFormat(hours)));
}
};
handleMinuteChange = (minutes) => {
if (this.props.time.getMinutes() !== minutes) {
this.props.onChange(time.setMinutes(this.props.time, minutes));
}
};
handleCalculateShape = () => {
const { top, left, width } = this.refs.wrapper.getBoundingClientRect();
this.setState({
center: { x: left + width / 2, y: top + width / 2 },
radius: width / 2
});
};
adaptHourToFormat (hour) {
if (this.props.format === 'ampm') {
if (time.getTimeMode(this.props.time) === 'pm') {
return hour < 12 ? hour + 12 : hour;
} else {
return hour === 12 ? 0 : hour;
}
} else {
return hour;
}
}
renderHours () {
return (
<Hours
center={this.state.center}
format={this.props.format}
onChange={this.handleHourChange}
radius={this.state.radius}
selected={this.props.time.getHours()}
spacing={this.state.radius * 0.18}
/>
);
}
renderMinutes () {
return (
<Minutes
center={this.state.center}
onChange={this.handleMinuteChange}
radius={this.state.radius}
selected={this.props.time.getMinutes()}
spacing={this.state.radius * 0.18}
/>
);
}
render () {
return (
<div data-react-toolbox='clock' className={style.root}>
<div ref='wrapper' className={style.wrapper} style={{height: this.state.radius * 2}}>
{ this.props.display === 'hours' ? this.renderHours() : null }
{ this.props.display === 'minutes' ? this.renderMinutes() : null }
</div>
</div>
);
}
}
export default Clock;
export default from './Clock.jsx';

View File

@ -1,16 +1,19 @@
import React from 'react';
import utils from '../../utils/utils';
import style from './style';
import Face from './face';
import Hand from './hand';
import Face from './Face';
import Hand from './Hand';
const minutes = utils.range(0, 60, 5);
const step = 360 / 60;
class Minutes extends React.Component {
static propTypes = {
center: React.PropTypes.object,
onChange: React.PropTypes.func,
radius: React.PropTypes.number,
selected: React.PropTypes.number,
onChange: React.PropTypes.func
spacing: React.PropTypes.number
};
static defaultProps = {
@ -39,7 +42,7 @@ class Minutes extends React.Component {
numbers={minutes}
spacing={this.props.spacing}
radius={this.props.radius}
twoDigits={true}
twoDigits
active={this.props.selected}
/>
<Hand ref='hand'

View File

@ -1,65 +1 @@
import React from 'react';
import events from '../utils/events';
import time from '../utils/time';
import style from './style';
import Input from '../input';
import TimeDialog from './dialog';
class TimePicker extends React.Component {
static propTypes = {
className: React.PropTypes.string,
format: React.PropTypes.oneOf(['24hr', 'ampm']),
label: React.PropTypes.string,
onChange: React.PropTypes.func,
value: React.PropTypes.object
};
static defaultProps = {
className: '',
format: '24hr'
};
state = {
active: false
};
handleDismiss = () => {
this.setState({active: false});
};
handleInputMouseDown = (event) => {
events.pauseEvent(event);
this.setState({active: true});
};
handleSelect = (value) => {
if (this.props.onChange) this.props.onChange(value);
this.setState({active: false});
};
render () {
const { value, format } = this.props;
const formattedTime = value ? time.formatTime(value, format) : null;
return (
<div data-react-toolbox='time-picker'>
<Input
className={style.input}
label={this.props.label}
onMouseDown={this.handleInputMouseDown}
readOnly={true}
type='text'
value={formattedTime}
/>
<TimeDialog
active={this.state.active}
format={format}
onDismiss={this.handleDismiss}
onSelect={this.handleSelect}
value={this.props.value}
/>
</div>
);
}
}
export default TimePicker;
export default from './TimePicker.jsx';

View File

@ -0,0 +1,69 @@
import React from 'react';
import ReactDOM from 'react-dom';
import style from './style';
const HIDE_TIMEOUT = 100;
class Tooltip extends React.Component {
static propTypes = {
className: React.PropTypes.string,
delay: React.PropTypes.number,
label: React.PropTypes.string
};
static defaultProps = {
className: '',
delay: 0
};
state = {
active: false
};
componentDidMount = () => {
const parent = ReactDOM.findDOMNode(this).parentNode;
if (parent.style.position !== 'relative' && parent.style.position !== 'absolute'){
parent.style.position = 'relative';
}
parent.onmouseover = this.handleParentMouseOver;
parent.onmouseout = this.handleParentMouseOut;
};
handleParentMouseOver = () => {
setTimeout(() => {
if (this.deferredHide) clearTimeout(this.deferredHide);
const node = ReactDOM.findDOMNode(this);
const parent = node.parentNode;
const parentStyle = parent.currentStyle || window.getComputedStyle(parent);
const offset = parseFloat(parentStyle['margin-bottom']) + parseFloat(parentStyle['padding-bottom']);
const position = parent.getBoundingClientRect();
node.style.top = `${position.height - offset}px`;
node.style.left = `${parseInt((position.width / 2) - (node.offsetWidth / 2))}px`;
if (!this.state.active) this.setState({ active: true});
}, this.props.delay);
};
handleParentMouseOut = () => {
if (this.state.active) {
this.deferredHide = setTimeout(() => { this.setState({active: false}); }, HIDE_TIMEOUT);
console.log(this.deferredHide);
}
};
render () {
let className = style.root;
if (this.props.className) className += ` ${this.props.className}`;
if (this.state.active) className += ` ${style.active}`;
return (
<span data-react-toolbox='tooltip' className={className}>
{this.props.label}
</span>
);
}
}
export default Tooltip;

View File

@ -1,69 +1 @@
import React from 'react';
import ReactDOM from 'react-dom';
import style from './style';
const HIDE_TIMEOUT = 100;
class Tooltip extends React.Component {
static propTypes = {
className: React.PropTypes.string,
delay: React.PropTypes.number,
label: React.PropTypes.string
};
static defaultProps = {
className: '',
delay: 0
};
state = {
active: false
};
componentDidMount = () => {
const parent = ReactDOM.findDOMNode(this).parentNode;
if (parent.style.position !== 'relative' && parent.style.position !== 'absolute'){
parent.style.position = 'relative';
}
parent.onmouseover = this.handleParentMouseOver;
parent.onmouseout = this.handleParentMouseOut;
};
handleParentMouseOver = () => {
setTimeout(() => {
if (this.deferredHide) clearTimeout(this.deferredHide);
const node = ReactDOM.findDOMNode(this);
const parent = node.parentNode;
const parentStyle = parent.currentStyle || window.getComputedStyle(parent);
const offset = parseFloat(parentStyle['margin-bottom']) + parseFloat(parentStyle['padding-bottom']);
const position = parent.getBoundingClientRect();
node.style.top = `${position.height - offset}px`;
node.style.left = `${parseInt((position.width / 2) - (node.offsetWidth / 2))}px`;
if (!this.state.active) this.setState({ active: true});
}, this.props.delay);
};
handleParentMouseOut = () => {
if (this.state.active) {
this.deferredHide = setTimeout(() => { this.setState({active: false}); }, HIDE_TIMEOUT);
console.log(this.deferredHide);
}
};
render () {
let className = style.root;
if (this.props.className) className += ` ${this.props.className}`;
if (this.state.active) className += ` ${style.active}`;
return (
<span data-react-toolbox='tooltip' className={className}>
{this.props.label}
</span>
);
}
}
export default Tooltip;
export default from './Tooltip.jsx';

View File

@ -31,7 +31,7 @@ class TableTest extends React.Component {
model={UserModel}
onChange={this.handleChange}
onSelect={this.handleSelect}
selectable={true}
selectable
selected={this.state.selected}
source={this.state.source}
/>

View File

@ -67,7 +67,7 @@ class DropdownTest extends React.Component {
<Dropdown
source={countries}
disabled={true}
disabled
onChange={this.handleChange.bind(this, 'dropdown3')}
/>
</section>

View File

@ -31,12 +31,12 @@ class IconMenuTest extends React.Component {
<IconMenu
icon='more-vert'
position='auto'
iconRipple={true}
menuRipple={true}
iconRipple
menuRipple
onShow={this.handleShow}
onHide={this.handleHide}
onSelect={this.handleSelect}
selectable={true}
selectable
selected={this.state.selected}
>
<MenuItem onClick={this.handleItem} value='refresh' caption='Refresh' />

View File

@ -3,6 +3,10 @@ import Button from '../../components/button';
import Snackbar from '../../components/snackbar';
class SnackbarTest extends React.Component {
state = {
active: false
};
handleSnackbarClick = () => {
this.setState({active: false});
};
@ -15,10 +19,6 @@ class SnackbarTest extends React.Component {
this.setState({active: true});
};
state = {
active: false
};
render () {
return (
<section>

View File

@ -40,7 +40,7 @@ class TableTest extends React.Component {
model={UserModel}
onChange={this.handleChange}
onSelect={this.handleSelect}
selectable={true}
selectable
selected={this.state.selected}
source={this.state.source}
/>