Skip to content

Commit b12bcdd

Browse files
Daviejoe100TheUltDev
authored andcommitted
Implemented inline-style-prefixer
User agent based prefixing for client and server side rendering. When rendering server-side define `navigator.userAgent` after receiving request headers but before rendering styles. A warning will be shown when attempting to use server-side rendering without defining a user agent. Client side rendering should automatically work as all modern browsers provide user agent via the navigator property.
1 parent 68baf9b commit b12bcdd

File tree

9 files changed

+63
-63
lines changed

9 files changed

+63
-63
lines changed

docs/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"react-dom": "^0.14.0",
2424
"react-motion": "^0.3.1",
2525
"react-swipeable-views": "^0.3.0",
26-
"react-tap-event-plugin": "^0.2.0"
26+
"react-tap-event-plugin": "^0.2.0",
27+
"inline-style-prefixer": "^0.3.3"
2728
},
2829
"devDependencies": {
2930
"raw-loader": "^0.5.1",

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
},
3131
"homepage": "http://material-ui.com/",
3232
"peerDependencies": {
33+
"inline-style-prefixer": "^0.3.3",
3334
"react": "^0.14.0",
3435
"react-dom": "^0.14.0",
3536
"react-tap-event-plugin": "^0.2.0",
@@ -54,6 +55,7 @@
5455
"gulp": "^3.9.0",
5556
"gulp-eslint": "^1.0.0",
5657
"html-webpack-plugin": "^1.6.1",
58+
"inline-style-prefixer": "^0.3.3",
5759
"karma": "^0.13.3",
5860
"karma-browserify": "^4.2.1",
5961
"karma-chai-sinon": "^0.1.5",

src/circular-progress.jsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,16 @@ const CircularProgress = React.createClass({
9999
if (!this.isMounted()) return;
100100
if (this.props.mode !== "indeterminate") return;
101101

102-
AutoPrefix.set(wrapper.style, "transform", null);
103-
AutoPrefix.set(wrapper.style, "transform", "rotate(0deg)");
102+
wrapper.style.transform = null;
103+
wrapper.style.transform = "rotate(0deg)";
104104
wrapper.style.transitionDuration = "0ms";
105+
wrapper.style = AutoPrefix.all(wrapper.style);
105106

106107
setTimeout(() => {
107-
AutoPrefix.set(wrapper.style, "transform", "rotate(1800deg)");
108+
wrapper.style.transform = "rotate(1800deg)";
108109
wrapper.style.transitionDuration = "10s";
109-
//wrapper.style.webkitTransitionTimingFunction = "linear";
110-
AutoPrefix.set(wrapper.style, "transitionTimingFunction", "linear");
110+
wrapper.style.transitionTimingFunction = "linear";
111+
wrapper.style = AutoPrefix.all(wrapper.style);
111112
}, 50);
112113
},
113114

src/left-nav.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,9 @@ const LeftNav = React.createClass({
301301

302302
_setPosition(translateX) {
303303
let leftNav = ReactDOM.findDOMNode(this.refs.clickAwayableElement);
304-
leftNav.style[AutoPrefix.single('transform')] =
305-
'translate3d(' + (this._getTranslateMultiplier() * translateX) + 'px, 0, 0)';
304+
let transformCSS = 'translate3d(' + (this._getTranslateMultiplier() * translateX) + 'px, 0, 0)';
306305
this.refs.overlay.setOpacity(1 - translateX / this._getMaxTranslateX());
306+
AutoPrefix.set(leftNav.style, 'transform', transformCSS);
307307
},
308308

309309
_getTranslateX(currentX) {

src/menus/menu.jsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,10 @@ const Menu = React.createClass({
8888

8989
componentWillLeave(callback) {
9090
let rootStyle = ReactDOM.findDOMNode(this).style;
91-
92-
AutoPrefix.set(rootStyle, 'transition', Transitions.easeOut('250ms', ['opacity', 'transform']));
93-
AutoPrefix.set(rootStyle, 'transform', 'translate3d(0,-8px,0)');
91+
rootStyle.transition = Transitions.easeOut('250ms', ['opacity', 'transform']);
92+
rootStyle.transform = 'translate3d(0,-8px,0)';
9493
rootStyle.opacity = 0;
95-
94+
rootStyle = AutoPrefix.all(rootStyle);
9695
setTimeout(() => {
9796
if (this.isMounted()) callback();
9897
}, 250);

src/refresh-indicator.jsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -250,19 +250,24 @@ const RefreshIndicator = React.createClass({
250250
const perimeter = Math.PI * 2 * circle.radiu;
251251
const arcLen = perimeter * 0.64;
252252

253+
let strokeDasharray, strokeDashoffset, transitionDuration;
253254
if (currStep === 0) {
254-
path.style.strokeDasharray = '1, 200';
255-
path.style.strokeDashoffset = 0;
256-
path.style[this.prefixed('transitionDuration')] = '0ms';
255+
strokeDasharray = '1, 200';
256+
strokeDashoffset = 0;
257+
transitionDuration = '0ms';
257258
} else if (currStep === 1) {
258-
path.style.strokeDasharray = arcLen + ', 200';
259-
path.style.strokeDashoffset = -15;
260-
path.style[this.prefixed('transitionDuration')] = '750ms';
259+
strokeDasharray = arcLen + ', 200';
260+
strokeDashoffset = -15;
261+
transitionDuration = '750ms';
261262
} else {
262-
path.style.strokeDasharray = arcLen + ',200';
263-
path.style.strokeDashoffset = -(perimeter - 1);
264-
path.style[this.prefixed('transitionDuration')] = '850ms';
263+
strokeDasharray = arcLen + ',200';
264+
strokeDashoffset = -(perimeter - 1);
265+
transitionDuration = '850ms';
265266
}
267+
268+
AutoPrefix.set(path.style, "strokeDasharray", strokeDasharray);
269+
AutoPrefix.set(path.style, "strokeDashoffset", strokeDashoffset);
270+
AutoPrefix.set(path.style, "transitionDuration", transitionDuration);
266271
},
267272

268273
_rotateWrapper(wrapper) {
@@ -278,7 +283,7 @@ const RefreshIndicator = React.createClass({
278283
setTimeout(() => {
279284
if (this.isMounted()) {
280285
AutoPrefix.set(wrapper.style, "transform", "rotate(1800deg)");
281-
wrapper.style.transitionDuration = "10s";
286+
AutoPrefix.set(wrapper.style, "transitionDuration", "10s");
282287
AutoPrefix.set(wrapper.style, "transitionTimingFunction", "linear");
283288
}
284289
}, 50);

src/ripples/focus-ripple.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,14 @@ const FocusRipple = React.createClass({
9797

9898
const startScale = 'scale(1)';
9999
const endScale = 'scale(0.85)';
100-
let currentScale = innerCircle.style[AutoPrefix.single('transform')];
100+
let currentScale = innerCircle.style.transform;
101101
let nextScale;
102102

103103
currentScale = currentScale || startScale;
104104
nextScale = currentScale === startScale ?
105105
endScale : startScale;
106106

107-
innerCircle.style[AutoPrefix.single('transform')] = nextScale;
107+
AutoPrefix.set(innerCircle.style, 'transform', nextScale);
108108
this._timeout = setTimeout(this._pulsate, pulsateDuration);
109109
},
110110

src/styles/auto-prefix.js

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,44 @@
1-
const isBrowser = require('../utils/is-browser');
1+
import InlineStylePrefixer from 'inline-style-prefixer';
22

3-
const Modernizr = isBrowser ? require('../utils/modernizr.custom') : undefined;
4-
5-
//Keep track of already prefixed keys so we can skip Modernizr prefixing
6-
let prefixedKeys = {};
3+
const prefixers = {};
74

85
module.exports = {
96

10-
all(styles) {
11-
let prefixedStyle = {};
12-
for (let key in styles) {
13-
prefixedStyle[this.single(key)] = styles[key];
7+
getPrefixer() {
8+
let userAgent;
9+
10+
// Server-side renderer needs to supply user agent
11+
if (typeof navigator === 'undefined') {
12+
console.warn(`Material-UI expects the global navigator.userAgent to be defined for server-side rendering. Set this property when receiving the request headers.`)
13+
userAgent = '*';
14+
} else {
15+
userAgent = navigator.userAgent;
1416
}
15-
return prefixedStyle;
16-
},
1717

18-
set(style, key, value) {
19-
style[this.single(key)] = value;
18+
// Get prefixing instance for this user agent
19+
let prefixer = prefixers[userAgent];
20+
// None found, create a new instance
21+
if (!prefixer) {
22+
prefixer = new InlineStylePrefixer(userAgent);
23+
prefixers[userAgent] = prefixer;
24+
}
25+
return prefixer;
2026
},
2127

22-
single(key) {
23-
24-
//If a browser doesn't exist, we can't prefix with Modernizr so
25-
//just return the key
26-
if (!isBrowser) return key;
27-
28-
//Check if we've prefixed this key before, just return it
29-
if (prefixedKeys.hasOwnProperty(key)) return prefixedKeys[key];
30-
31-
//Key hasn't been prefixed yet, prefix with Modernizr
32-
const prefKey = Modernizr.prefixed(key);
33-
34-
// Windows 7 Firefox has an issue with the implementation of Modernizr.prefixed
35-
// and is capturing 'false' as the CSS property name instead of the non-prefixed version.
36-
if (prefKey === false) return key;
37-
38-
//Save the key off for the future and return the prefixed key
39-
prefixedKeys[key] = prefKey;
40-
return prefKey;
41-
28+
all(styles) {
29+
return this.getPrefixer().prefix(styles);
4230
},
4331

44-
singleHyphened(key) {
45-
let str = this.single(key);
32+
set(style, key, value) {
33+
style[key] = value;
34+
style = this.getPrefixer().prefix(style);
35+
},
4636

47-
return !str ? key : str.replace(/([A-Z])/g, (str, m1) => {
48-
return '-' + m1.toLowerCase();
49-
}).replace(/^ms-/, '-ms-');
37+
getPrefix(key) {
38+
let style = {};
39+
style[key] = true;
40+
let prefixes = Object.keys(this.getPrefixer().prefix(style));
41+
return prefixes ? prefixes[0] : key;
5042
},
5143

5244
};

src/styles/transitions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module.exports = {
3232
delay = delay || '0ms';
3333
easeFunction = easeFunction || "linear";
3434

35-
return AutoPrefix.singleHyphened(property) + ' ' +
35+
return property + ' ' +
3636
duration + ' ' +
3737
easeFunction + ' ' +
3838
delay;

0 commit comments

Comments
 (0)