Skip to content

Commit 01f4aae

Browse files
committed
Merge pull request #340 from mmrtnz/css-in-js
CSS-in-JS for Switches
2 parents fb0b511 + 078c6e6 commit 01f4aae

File tree

12 files changed

+291
-332
lines changed

12 files changed

+291
-332
lines changed

docs/src/app/components/pages/components/switches.jsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,9 @@ var SwitchesPage = React.createClass({
202202
},
203203
{
204204
name: 'setSelectedValue',
205-
header: 'RadioButtonGroup.setSelectedValue(newSelection)',
205+
header: 'RadioButtonGroup.setSelectedValue(newSelectionValue)',
206206
desc: 'Sets the selected radio button to the radio button whose value matches ' +
207-
'newSelection'
207+
'newSelectionValue'
208208
},
209209
{
210210
name: 'clearValue',
@@ -338,7 +338,7 @@ var SwitchesPage = React.createClass({
338338
name="checkboxName2"
339339
value="checkboxValue2"
340340
label="fed the dog"
341-
defaultSwitched={true}/>
341+
defaultChecked={true}/>
342342
</div>
343343
<div className="switches-example-container">
344344
<Checkbox
@@ -384,7 +384,6 @@ var SwitchesPage = React.createClass({
384384
label="initiate self-destruct sequence"
385385
disabled={true}/>
386386
</div>
387-
388387
</div>
389388
);
390389
},

src/js/checkbox.jsx

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,101 @@
11
var React = require('react');
22
var EnhancedSwitch = require('./enhanced-switch');
3-
var Classable = require('./mixins/classable');
3+
var StylePropable = require('./mixins/style-propable.js');
4+
var Transitions = require('./styles/mixins/transitions.js');
45
var CheckboxOutline = require('./svg-icons/toggle-check-box-outline-blank');
56
var CheckboxChecked = require('./svg-icons/toggle-check-box-checked');
7+
var CustomVariables = require('./styles/variables/custom-variables.js');
68

79
var Checkbox = React.createClass({
810

9-
mixins: [Classable],
11+
mixins: [StylePropable],
1012

1113
propTypes: {
1214
onCheck: React.PropTypes.func,
1315
},
1416

17+
getInitialState: function() {
18+
return {
19+
switched:
20+
this.props.checked ||
21+
this.props.defaultChecked ||
22+
(this.props.valueLink && this.props.valueLink.value) ||
23+
false,
24+
}
25+
},
26+
1527
render: function() {
1628
var {
1729
onCheck,
1830
...other
1931
} = this.props;
2032

21-
var classes = this.getClasses("mui-checkbox");
33+
var checkboxSize = 24;
34+
35+
var iconStyles = {
36+
height: checkboxSize,
37+
width: checkboxSize,
38+
}
39+
40+
var checkStyles = {
41+
positiion: 'absolute',
42+
opacity: 0,
43+
transform: 'scale(0)',
44+
transitionOrigin: '50% 50%',
45+
transition: Transitions.easeOut('450ms', 'opacity', '0ms') + ', ' +
46+
Transitions.easeOut('0ms', 'transform', '450ms'),
47+
fill: CustomVariables.checkboxCheckedColor
48+
}
49+
50+
var boxStyles = {
51+
position: 'absolute',
52+
opacity: 1,
53+
fill: CustomVariables.checkboxBoxColor,
54+
transition: Transitions.easeOut('2s', null, '200ms')
55+
}
56+
57+
if (this.state.switched) {
58+
checkStyles = this.mergePropStyles(checkStyles, {
59+
opacity: 1,
60+
transform: 'scale(1)',
61+
transition: Transitions.easeOut('0ms', 'opacity', '0ms') + ', ' +
62+
Transitions.easeOut('800ms', 'transform', '0ms')
63+
});
64+
boxStyles = this.mergePropStyles(boxStyles, {
65+
transition: Transitions.easeOut('100ms', null, '0ms'),
66+
fill: CustomVariables.checkboxCheckedColor
67+
});
68+
}
69+
70+
if (this.props.disabled) {
71+
checkStyles = this.mergePropStyles(checkStyles, {
72+
opacity: 0.3,
73+
fill: CustomVariables.disabledColor,
74+
});
75+
boxStyles = this.mergePropStyles(boxStyles, {
76+
opacity: 0.3,
77+
fill: CustomVariables.disabledColor,
78+
});
79+
}
80+
81+
if (this.state.switched && this.props.disabled) boxStyles.opacity = 0;
2282

2383
var checkboxElement = (
2484
<div>
25-
<CheckboxOutline className="mui-checkbox-box" />
26-
<CheckboxChecked className="mui-checkbox-check" />
85+
<CheckboxOutline style={boxStyles} />
86+
<CheckboxChecked style={checkStyles} />
2787
</div>
2888
);
2989

3090
var enhancedSwitchProps = {
3191
ref: "enhancedSwitch",
3292
inputType: "checkbox",
93+
switched: this.state.switched,
3394
switchElement: checkboxElement,
34-
className: classes,
35-
iconClassName: "mui-checkbox-icon",
95+
iconStyle: iconStyles,
3696
onSwitch: this._handleCheck,
97+
onParentShouldUpdate: this._handleStateChange,
98+
defaultSwitched: this.props.defaultChecked,
3799
labelPosition: (this.props.labelPosition) ? this.props.labelPosition : "right"
38100
};
39101

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

55117
_handleCheck: function(e, isInputChecked) {
56118
if (this.props.onCheck) this.props.onCheck(e, isInputChecked);
119+
},
120+
121+
_handleStateChange: function(newSwitched) {
122+
this.setState({switched: newSwitched});
57123
}
124+
58125
});
59126

60127
module.exports = Checkbox;

src/js/enhanced-switch.jsx

Lines changed: 75 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
11
var React = require('react');
22
var KeyCode = require('./utils/key-code');
3-
var Classable = require('./mixins/classable');
43
var DomIdable = require('./mixins/dom-idable');
54
var StylePropable = require('./mixins/style-propable.js');
5+
var Transitions = require('./styles/mixins/transitions.js');
66
var WindowListenable = require('./mixins/window-listenable');
7+
var CustomVariables = require('./styles/variables/custom-variables.js');
78
var FocusRipple = require('./ripples/focus-ripple');
89
var TouchRipple = require('./ripples/touch-ripple');
910
var Paper = require('./paper');
1011
var Theme = require('./styles/theme.js').get();
1112

1213
var EnhancedSwitch = React.createClass({
1314

14-
mixins: [Classable, DomIdable, WindowListenable, StylePropable],
15+
mixins: [DomIdable, WindowListenable, StylePropable],
1516

1617
propTypes: {
1718
id: React.PropTypes.string,
1819
inputType: React.PropTypes.string.isRequired,
1920
switchElement: React.PropTypes.element.isRequired,
20-
iconClassName: React.PropTypes.string.isRequired,
21+
onParentShouldUpdate: React.PropTypes.func.isRequired,
22+
switched: React.PropTypes.bool.isRequired,
2123
rippleStyle: React.PropTypes.object,
24+
iconStyle: React.PropTypes.object,
25+
thumbStyle: React.PropTypes.object,
26+
trackStyle: React.PropTypes.object,
2227
name: React.PropTypes.string,
2328
value: React.PropTypes.string,
2429
label: React.PropTypes.string,
@@ -28,31 +33,35 @@ var EnhancedSwitch = React.createClass({
2833
defaultSwitched: React.PropTypes.bool,
2934
labelPosition: React.PropTypes.oneOf(['left', 'right']),
3035
disableFocusRipple: React.PropTypes.bool,
31-
disableTouchRipple: React.PropTypes.bool
36+
disableTouchRipple: React.PropTypes.bool,
3237
},
3338

3439
windowListeners: {
3540
'keydown': '_handleWindowKeydown',
3641
'keyup': '_handleWindowKeyup'
3742
},
3843

39-
getDefaultProps: function() {
40-
return {
41-
iconClassName: ''
42-
};
43-
},
44-
4544
getInitialState: function() {
4645
return {
47-
switched: this.props.defaultSwitched ||
48-
(this.props.valueLink && this.props.valueLink.value),
4946
isKeyboardFocused: false
5047
}
5148
},
5249

50+
getEvenWidth: function(){
51+
return (
52+
parseInt(window
53+
.getComputedStyle(this.getDOMNode())
54+
.getPropertyValue('width'), 10)
55+
);
56+
},
57+
5358
componentDidMount: function() {
5459
var inputNode = this.refs.checkbox.getDOMNode();
55-
this.setState({switched: inputNode.checked});
60+
if (!this.props.switched ||
61+
this.props.switched == undefined ||
62+
inputNode.checked != this.props.switched) this.props.onParentShouldUpdate(inputNode.checked);
63+
64+
this.setState({parentWidth: this.getEvenWidth()});
5665
},
5766

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

75-
if (newState) this.setState(newState);
84+
if (newState.switched != undefined && (newState.switched != this.props.switched)) this.props.onParentShouldUpdate(newState.switched);
7685
},
7786

7887
render: function() {
@@ -92,20 +101,52 @@ var EnhancedSwitch = React.createClass({
92101
onTouchEnd,
93102
disableTouchRipple,
94103
disableFocusRipple,
95-
iconClassName,
96104
...other
97105
} = this.props;
98106

99-
var classes = this.getClasses('mui-enhanced-switch', {
100-
'mui-is-switched': this.state.switched,
101-
'mui-is-disabled': this.props.disabled,
102-
'mui-is-required': this.props.required
107+
var switchWidth = 60 - CustomVariables.spacing.desktopGutterLess;
108+
var labelWidth = this.state.parentWidth - switchWidth - 30;
109+
var styles = this.mergePropStyles({
110+
position: 'relative',
111+
cursor: this.props.disabled ? 'default' : 'pointer',
112+
overflow: 'visible',
113+
display: 'table',
114+
height: 'auto',
115+
width: '100%'
103116
});
117+
var inputStyles = {
118+
position: 'absolute',
119+
cursor: this.props.disabled ? 'default' : 'pointer',
120+
pointerEvents: 'all',
121+
opacity: 0,
122+
width: '100%',
123+
height: '100%',
124+
zIndex: 2,
125+
left: 0
126+
};
127+
var wrapStyles = this.mergePropStyles({
128+
transition: Transitions.easeOut(),
129+
float: 'left',
130+
position: 'relative',
131+
display: 'table-column',
132+
width: switchWidth,
133+
marginRight: (this.props.labelPosition == 'right') ?
134+
CustomVariables.spacing.desktopGutterLess : 0,
135+
marginLeft: (this.props.labelPosition == 'left') ?
136+
CustomVariables.spacing.desktopGutterLess : 0
137+
}, this.props.iconStyle);
138+
var labelStyles = {
139+
float: 'left',
140+
position: 'relative',
141+
display: 'table-column',
142+
width: labelWidth,
143+
lineHeight: '24px'
144+
}
104145

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

107148
var labelElement = this.props.label ? (
108-
<label className="mui-switch-label" htmlFor={inputId}>
149+
<label style={labelStyles} htmlFor={inputId}>
109150
{this.props.label}
110151
</label>
111152
) : null;
@@ -133,7 +174,7 @@ var EnhancedSwitch = React.createClass({
133174
<input
134175
{...other}
135176
{...inputProps}
136-
className="mui-enhanced-switch-input"/>
177+
style={inputStyles}/>
137178
);
138179

139180
var rippleStyle = this.mergePropStyles({
@@ -148,15 +189,15 @@ var EnhancedSwitch = React.createClass({
148189
ref="touchRipple"
149190
key="touchRipple"
150191
style={rippleStyle}
151-
color={this.state.switched ? Theme.primary1Color : Theme.textColor}
192+
color={this.props.switched ? Theme.primary1Color : Theme.textColor}
152193
centerRipple={true} />
153194
);
154195

155196
var focusRipple = (
156197
<FocusRipple
157198
key="focusRipple"
158199
innerStyle={rippleStyle}
159-
color={this.state.switched ? Theme.primary1Color : Theme.textColor}
200+
color={this.props.switched ? Theme.primary1Color : Theme.textColor}
160201
show={this.state.isKeyboardFocused} />
161202
);
162203

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

168-
iconClassName += ' mui-enhanced-switch-wrap';
169-
170-
var switchElement = (this.props.iconClassName.indexOf("toggle") == -1) ? (
171-
<div className={iconClassName}>
209+
// If toggle component (indicated by whether the style includes thumb) manually lay out
210+
// elements in order to nest ripple elements
211+
var switchElement = !this.props.thumbStyle ? (
212+
<div style={wrapStyles}>
172213
{this.props.switchElement}
173214
{ripples}
174215
</div>
175216
) : (
176-
<div className={iconClassName}>
177-
<div className="mui-toggle-track" />
178-
<Paper className="mui-toggle-thumb" zDepth={1}> {ripples} </Paper>
217+
<div style={wrapStyles}>
218+
<div style={this.props.trackStyle}/>
219+
<Paper style={this.props.thumbStyle} zDepth={1}> {ripples} </Paper>
179220
</div>
180221
);
181222

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

198239
return (
199-
<div className={classes}>
240+
<div style={styles}>
200241
{inputElement}
201242
{elementsInOrder}
202243
</div>
@@ -211,7 +252,8 @@ var EnhancedSwitch = React.createClass({
211252
// no callback here because there is no event
212253
setSwitched: function(newSwitchedValue) {
213254
if (!this.props.hasOwnProperty('checked') || this.props.checked == false) {
214-
this.setState({switched: newSwitchedValue});
255+
256+
this.props.onParentShouldUpdate(newSwitchedValue);
215257
this.refs.checkbox.getDOMNode().checked = newSwitchedValue;
216258
} else {
217259
var message = 'Cannot call set method while checked is defined as a property.';
@@ -228,15 +270,14 @@ var EnhancedSwitch = React.createClass({
228270
},
229271

230272
_handleChange: function(e) {
231-
232273
this._tabPressed = false;
233274
this.setState({
234275
isKeyboardFocused: false
235276
});
236277

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

239-
if (!this.props.hasOwnProperty('checked')) this.setState({switched: isInputChecked});
280+
if (!this.props.hasOwnProperty('checked')) this.props.onParentShouldUpdate(isInputChecked);
240281
if (this.props.onSwitch) this.props.onSwitch(e, isInputChecked);
241282
},
242283

0 commit comments

Comments
 (0)