Skip to content

Instantly share code, notes, and snippets.

@narqo
Last active April 28, 2016 00:47
Show Gist options
  • Save narqo/856ddcb73d5e7bad80ffa715720ddebb to your computer and use it in GitHub Desktop.
Save narqo/856ddcb73d5e7bad80ffa715720ddebb to your computer and use it in GitHub Desktop.
import React from 'react';
import { findDOMNode } from 'react-dom';
import bem from 'b_';
const b = bem.with('button');
const KEY_SPACE = ' ';
const KEY_ENTER = 'Enter';
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {
hovered: false,
pressed: false,
focused: props.focused,
};
this.onMouseEnter = this.onMouseEnter.bind(this);
this.onMouseLeave = this.onMouseLeave.bind(this);
this.onMouseDown = this.onMouseDown.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
this.onFocus = this.onFocus.bind(this);
this.onBlur = this.onBlur.bind(this);
}
componentWillReceiveProps({ disabled }) {
if (disabled === true) {
this.setState({ focused: false, pressed: false });
}
}
componentWillUpdate(nextProps) {
if (this.state.focused && nextProps.focused !== this.state.focused) {
findDOMNode(this).focus();
}
}
render() {
const { pressed, hovered, focused } = this.state;
const {
theme,
size,
view,
name,
disabled,
checked,
children,
} = this.props;
const className = b({
theme,
size,
view,
disabled,
checked,
hovered,
focused,
pressed,
});
return (
<button
className={className}
name={name}
disabled={disabled}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
onMouseDown={this.onMouseDown}
onMouseUp={this.onMouseUp}
onKeyDown={this.onKeyDown}
onKeyUp={this.onKeyUp}
onFocus={this.onFocus}
onBlur={this.onBlur}
>
<span className={b('text')}>{children}</span>
</button>
);
}
handleClick() {
this.props.onClick();
}
onMouseEnter() {
if (!this.props.disabled) {
this.setState({ hovered: true });
}
}
onMouseLeave() {
this.setState({ hovered: false, pressed: false });
}
onMouseDown() {
if (!this.props.disabled) {
this.setState({ pressed: true });
}
}
onMouseUp() {
if (this.state.pressed) {
this.setState({ pressed: false });
this.handleClick();
}
}
onKeyDown(e) {
if (this.props.disabled || !this.state.focused) {
return;
}
if (e.key === KEY_SPACE || e.key === KEY_ENTER) {
this.setState({ pressed: true });
}
}
onKeyUp() {
if (this.state.pressed && this.state.focused) {
this.setState({ pressed: false });
this.handleClick();
}
}
onFocus() {
if (!this.props.disabled) {
this.setState({ focused: true });
}
}
onBlur() {
this.setState({ focused: false });
}
}
Button.propsType = {
theme: React.PropTypes.string,
size: React.PropTypes.string,
disabled: React.PropTypes.bool,
focused: React.PropTypes.bool,
checked: React.PropTypes.bool,
onClick: React.PropTypes.func,
};
Button.defaultProps = {
disabled: false,
checked: false,
focused: false,
onClick() {},
};
export default Button;
import React from 'react';
import Control from '../Control';
import pressable from '../pressable';
class Button extends Control {
constructor(props, context) {
super(props, context);
this.children = null;
}
componentWillUpdate(nextProps, nextState) {
if (super.componentWillUpdate) {
super.componentWillUpdate(nextProps, nextState);
}
if (this.props.children !== nextProps.children) {
this.children = null;
}
}
render() {
if (!this.children) {
this.children = React.Children.map(this.props.children, child => {
if (React.isValidElement(child)) {
return child;
} else {
return <span className="button__text">{child}</span>
}
});
}
if (this.props.type === 'link') {
const url = this.props.disabled ? null : this.props.url;
return (
<a className={this.className()} {...this.getControlHandlers()} ref="control" role="link" href={url}>
{this.children}
</a>
);
} else {
return (
<button className={this.className()} {...this.getControlHandlers()} ref="control" name={this.props.name} disabled={this.props.disabled}>
{this.children}
</button>
);
}
}
className() {
var className = 'button';
const theme = this.props.theme || this.context.theme;
if (theme) {
className += ' button_theme_' + theme;
}
if (this.props.size) {
className += ' button_size_' + this.props.size;
}
if (this.props.type) {
className += ' button_type_' + this.props.type;
}
if (this.props.view) {
className += ' button_view_' + this.props.view;
}
if (this.props.disabled) {
className += ' button_disabled';
}
if (this.state.hovered) {
className += ' button_hovered';
}
if (this.state.pressed) {
className += ' button_pressed';
}
if (this.state.focused === 'hard') {
className += ' button_focused button_focused-hard';
} else if (this.state.focused) {
className += ' button_focused';
}
if (this.props.checked) {
className += ' button_checked';
}
if (this.props.className) {
className += ' ' + this.props.className;
}
return className;
}
}
Button.propTypes = {
size: React.PropTypes.oneOf(['s', 'm', 'l', 'xl'])
};
Button.contextTypes = {
theme: React.PropTypes.string
};
export default pressable(Button);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment