Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSS-in-JS for Switches #340

Merged
merged 6 commits into from
Feb 16, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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