Skip to content

Commit

Permalink
Merge pull request #340 from mmrtnz/css-in-js
Browse files Browse the repository at this point in the history
CSS-in-JS for Switches
  • Loading branch information
Hai Nguyen committed Feb 16, 2015
2 parents fb0b511 + 078c6e6 commit 01f4aae
Show file tree
Hide file tree
Showing 12 changed files with 291 additions and 332 deletions.
7 changes: 3 additions & 4 deletions docs/src/app/components/pages/components/switches.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,9 @@ var SwitchesPage = React.createClass({
},
{
name: 'setSelectedValue',
header: 'RadioButtonGroup.setSelectedValue(newSelection)',
header: 'RadioButtonGroup.setSelectedValue(newSelectionValue)',
desc: 'Sets the selected radio button to the radio button whose value matches ' +
'newSelection'
'newSelectionValue'
},
{
name: 'clearValue',
Expand Down Expand Up @@ -338,7 +338,7 @@ var SwitchesPage = React.createClass({
name="checkboxName2"
value="checkboxValue2"
label="fed the dog"
defaultSwitched={true}/>
defaultChecked={true}/>
</div>
<div className="switches-example-container">
<Checkbox
Expand Down Expand Up @@ -384,7 +384,6 @@ var SwitchesPage = React.createClass({
label="initiate self-destruct sequence"
disabled={true}/>
</div>

</div>
);
},
Expand Down
81 changes: 74 additions & 7 deletions src/js/checkbox.jsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,101 @@
var React = require('react');
var EnhancedSwitch = require('./enhanced-switch');
var Classable = require('./mixins/classable');
var StylePropable = require('./mixins/style-propable.js');
var Transitions = require('./styles/mixins/transitions.js');
var CheckboxOutline = require('./svg-icons/toggle-check-box-outline-blank');
var CheckboxChecked = require('./svg-icons/toggle-check-box-checked');
var CustomVariables = require('./styles/variables/custom-variables.js');

var Checkbox = React.createClass({

mixins: [Classable],
mixins: [StylePropable],

propTypes: {
onCheck: React.PropTypes.func,
},

getInitialState: function() {
return {
switched:
this.props.checked ||
this.props.defaultChecked ||
(this.props.valueLink && this.props.valueLink.value) ||
false,
}
},

render: function() {
var {
onCheck,
...other
} = this.props;

var classes = this.getClasses("mui-checkbox");
var checkboxSize = 24;

var iconStyles = {
height: checkboxSize,
width: checkboxSize,
}

var checkStyles = {
positiion: 'absolute',
opacity: 0,
transform: 'scale(0)',
transitionOrigin: '50% 50%',
transition: Transitions.easeOut('450ms', 'opacity', '0ms') + ', ' +
Transitions.easeOut('0ms', 'transform', '450ms'),
fill: CustomVariables.checkboxCheckedColor
}

var boxStyles = {
position: 'absolute',
opacity: 1,
fill: CustomVariables.checkboxBoxColor,
transition: Transitions.easeOut('2s', null, '200ms')
}

if (this.state.switched) {
checkStyles = this.mergePropStyles(checkStyles, {
opacity: 1,
transform: 'scale(1)',
transition: Transitions.easeOut('0ms', 'opacity', '0ms') + ', ' +
Transitions.easeOut('800ms', 'transform', '0ms')
});
boxStyles = this.mergePropStyles(boxStyles, {
transition: Transitions.easeOut('100ms', null, '0ms'),
fill: CustomVariables.checkboxCheckedColor
});
}

if (this.props.disabled) {
checkStyles = this.mergePropStyles(checkStyles, {
opacity: 0.3,
fill: CustomVariables.disabledColor,
});
boxStyles = this.mergePropStyles(boxStyles, {
opacity: 0.3,
fill: CustomVariables.disabledColor,
});
}

if (this.state.switched && this.props.disabled) boxStyles.opacity = 0;

var checkboxElement = (
<div>
<CheckboxOutline className="mui-checkbox-box" />
<CheckboxChecked className="mui-checkbox-check" />
<CheckboxOutline style={boxStyles} />
<CheckboxChecked style={checkStyles} />
</div>
);

var enhancedSwitchProps = {
ref: "enhancedSwitch",
inputType: "checkbox",
switched: this.state.switched,
switchElement: checkboxElement,
className: classes,
iconClassName: "mui-checkbox-icon",
iconStyle: iconStyles,
onSwitch: this._handleCheck,
onParentShouldUpdate: this._handleStateChange,
defaultSwitched: this.props.defaultChecked,
labelPosition: (this.props.labelPosition) ? this.props.labelPosition : "right"
};

Expand All @@ -54,7 +116,12 @@ var Checkbox = React.createClass({

_handleCheck: function(e, isInputChecked) {
if (this.props.onCheck) this.props.onCheck(e, isInputChecked);
},

_handleStateChange: function(newSwitched) {
this.setState({switched: newSwitched});
}

});

module.exports = Checkbox;
109 changes: 75 additions & 34 deletions src/js/enhanced-switch.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
var React = require('react');
var KeyCode = require('./utils/key-code');
var Classable = require('./mixins/classable');
var DomIdable = require('./mixins/dom-idable');
var StylePropable = require('./mixins/style-propable.js');
var Transitions = require('./styles/mixins/transitions.js');
var WindowListenable = require('./mixins/window-listenable');
var CustomVariables = require('./styles/variables/custom-variables.js');
var FocusRipple = require('./ripples/focus-ripple');
var TouchRipple = require('./ripples/touch-ripple');
var Paper = require('./paper');
var Theme = require('./styles/theme.js').get();

var EnhancedSwitch = React.createClass({

mixins: [Classable, DomIdable, WindowListenable, StylePropable],
mixins: [DomIdable, WindowListenable, StylePropable],

propTypes: {
id: React.PropTypes.string,
inputType: React.PropTypes.string.isRequired,
switchElement: React.PropTypes.element.isRequired,
iconClassName: React.PropTypes.string.isRequired,
onParentShouldUpdate: React.PropTypes.func.isRequired,
switched: React.PropTypes.bool.isRequired,
rippleStyle: React.PropTypes.object,
iconStyle: React.PropTypes.object,
thumbStyle: React.PropTypes.object,
trackStyle: React.PropTypes.object,
name: React.PropTypes.string,
value: React.PropTypes.string,
label: React.PropTypes.string,
Expand All @@ -28,31 +33,35 @@ var EnhancedSwitch = React.createClass({
defaultSwitched: React.PropTypes.bool,
labelPosition: React.PropTypes.oneOf(['left', 'right']),
disableFocusRipple: React.PropTypes.bool,
disableTouchRipple: React.PropTypes.bool
disableTouchRipple: React.PropTypes.bool,
},

windowListeners: {
'keydown': '_handleWindowKeydown',
'keyup': '_handleWindowKeyup'
},

getDefaultProps: function() {
return {
iconClassName: ''
};
},

getInitialState: function() {
return {
switched: this.props.defaultSwitched ||
(this.props.valueLink && this.props.valueLink.value),
isKeyboardFocused: false
}
},

getEvenWidth: function(){
return (
parseInt(window
.getComputedStyle(this.getDOMNode())
.getPropertyValue('width'), 10)
);
},

componentDidMount: function() {
var inputNode = this.refs.checkbox.getDOMNode();
this.setState({switched: inputNode.checked});
if (!this.props.switched ||
this.props.switched == undefined ||
inputNode.checked != this.props.switched) this.props.onParentShouldUpdate(inputNode.checked);

this.setState({parentWidth: this.getEvenWidth()});
},

componentWillReceiveProps: function(nextProps) {
Expand All @@ -72,7 +81,7 @@ var EnhancedSwitch = React.createClass({
newState.switched = nextProps.checkedLink.value;
}

if (newState) this.setState(newState);
if (newState.switched != undefined && (newState.switched != this.props.switched)) this.props.onParentShouldUpdate(newState.switched);
},

render: function() {
Expand All @@ -92,20 +101,52 @@ var EnhancedSwitch = React.createClass({
onTouchEnd,
disableTouchRipple,
disableFocusRipple,
iconClassName,
...other
} = this.props;

var classes = this.getClasses('mui-enhanced-switch', {
'mui-is-switched': this.state.switched,
'mui-is-disabled': this.props.disabled,
'mui-is-required': this.props.required
var switchWidth = 60 - CustomVariables.spacing.desktopGutterLess;
var labelWidth = this.state.parentWidth - switchWidth - 30;
var styles = this.mergePropStyles({
position: 'relative',
cursor: this.props.disabled ? 'default' : 'pointer',
overflow: 'visible',
display: 'table',
height: 'auto',
width: '100%'
});
var inputStyles = {
position: 'absolute',
cursor: this.props.disabled ? 'default' : 'pointer',
pointerEvents: 'all',
opacity: 0,
width: '100%',
height: '100%',
zIndex: 2,
left: 0
};
var wrapStyles = this.mergePropStyles({
transition: Transitions.easeOut(),
float: 'left',
position: 'relative',
display: 'table-column',
width: switchWidth,
marginRight: (this.props.labelPosition == 'right') ?
CustomVariables.spacing.desktopGutterLess : 0,
marginLeft: (this.props.labelPosition == 'left') ?
CustomVariables.spacing.desktopGutterLess : 0
}, this.props.iconStyle);
var labelStyles = {
float: 'left',
position: 'relative',
display: 'table-column',
width: labelWidth,
lineHeight: '24px'
}

var inputId = this.props.id || this.getDomId();

var labelElement = this.props.label ? (
<label className="mui-switch-label" htmlFor={inputId}>
<label style={labelStyles} htmlFor={inputId}>
{this.props.label}
</label>
) : null;
Expand Down Expand Up @@ -133,7 +174,7 @@ var EnhancedSwitch = React.createClass({
<input
{...other}
{...inputProps}
className="mui-enhanced-switch-input"/>
style={inputStyles}/>
);

var rippleStyle = this.mergePropStyles({
Expand All @@ -148,15 +189,15 @@ var EnhancedSwitch = React.createClass({
ref="touchRipple"
key="touchRipple"
style={rippleStyle}
color={this.state.switched ? Theme.primary1Color : Theme.textColor}
color={this.props.switched ? Theme.primary1Color : Theme.textColor}
centerRipple={true} />
);

var focusRipple = (
<FocusRipple
key="focusRipple"
innerStyle={rippleStyle}
color={this.state.switched ? Theme.primary1Color : Theme.textColor}
color={this.props.switched ? Theme.primary1Color : Theme.textColor}
show={this.state.isKeyboardFocused} />
);

Expand All @@ -165,17 +206,17 @@ var EnhancedSwitch = React.createClass({
this.props.disabled || disableFocusRipple ? null : focusRipple
];

iconClassName += ' mui-enhanced-switch-wrap';

var switchElement = (this.props.iconClassName.indexOf("toggle") == -1) ? (
<div className={iconClassName}>
// If toggle component (indicated by whether the style includes thumb) manually lay out
// elements in order to nest ripple elements
var switchElement = !this.props.thumbStyle ? (
<div style={wrapStyles}>
{this.props.switchElement}
{ripples}
</div>
) : (
<div className={iconClassName}>
<div className="mui-toggle-track" />
<Paper className="mui-toggle-thumb" zDepth={1}> {ripples} </Paper>
<div style={wrapStyles}>
<div style={this.props.trackStyle}/>
<Paper style={this.props.thumbStyle} zDepth={1}> {ripples} </Paper>
</div>
);

Expand All @@ -196,7 +237,7 @@ var EnhancedSwitch = React.createClass({
);

return (
<div className={classes}>
<div style={styles}>
{inputElement}
{elementsInOrder}
</div>
Expand All @@ -211,7 +252,8 @@ var EnhancedSwitch = React.createClass({
// no callback here because there is no event
setSwitched: function(newSwitchedValue) {
if (!this.props.hasOwnProperty('checked') || this.props.checked == false) {
this.setState({switched: newSwitchedValue});

this.props.onParentShouldUpdate(newSwitchedValue);
this.refs.checkbox.getDOMNode().checked = newSwitchedValue;
} else {
var message = 'Cannot call set method while checked is defined as a property.';
Expand All @@ -228,15 +270,14 @@ var EnhancedSwitch = React.createClass({
},

_handleChange: function(e) {

this._tabPressed = false;
this.setState({
isKeyboardFocused: false
});

var isInputChecked = this.refs.checkbox.getDOMNode().checked;

if (!this.props.hasOwnProperty('checked')) this.setState({switched: isInputChecked});
if (!this.props.hasOwnProperty('checked')) this.props.onParentShouldUpdate(isInputChecked);
if (this.props.onSwitch) this.props.onSwitch(e, isInputChecked);
},

Expand Down
Loading

0 comments on commit 01f4aae

Please sign in to comment.