UNPKG

4.6 kBJavaScriptView Raw
1/**
2 * Based on https://github.com/jsstyles/css-vendor, but without having to
3 * convert between different cases all the time.
4 *
5 * @flow
6 */
7
8import createStaticPrefixer from 'inline-style-prefixer/static/createPrefixer';
9import createDynamicPrefixer
10 from 'inline-style-prefixer/dynamic/createPrefixer';
11import ExecutionEnvironment from 'exenv';
12
13import staticData from './prefix-data/static';
14import dynamicData from './prefix-data/dynamic';
15
16import {camelCaseToDashCase} from './camel-case-props-to-dash-case';
17
18const prefixAll: (style: Object) => Object = createStaticPrefixer(staticData);
19const InlineStylePrefixer = createDynamicPrefixer(dynamicData, prefixAll);
20
21function transformValues(style) {
22 return Object.keys(style).reduce(
23 (newStyle, key) => {
24 let value = style[key];
25 if (Array.isArray(value)) {
26 value = value.join(';' + key + ':');
27 } else if (
28 value &&
29 typeof value === 'object' &&
30 typeof value.toString === 'function'
31 ) {
32 value = value.toString();
33 }
34
35 newStyle[key] = value;
36 return newStyle;
37 },
38 {}
39 );
40}
41
42// Flatten prefixed values that are arrays to strings.
43//
44// We get prefixed styles back in the form of:
45// - `display: "flex"` OR
46// - `display: "-webkit-flex"` OR
47// - `display: [/* ... */, "-webkit-flex", "flex"]
48//
49// The last form is problematic for eventual use in the browser and server
50// render. More confusingly, we have to do **different** things on the
51// browser and server (noted inline below).
52//
53// https://github.com/FormidableLabs/radium/issues/958
54function flattenStyleValues(style) {
55 return Object.keys(style).reduce(
56 (newStyle, key) => {
57 let val = style[key];
58 if (Array.isArray(val)) {
59 if (ExecutionEnvironment.canUseDOM) {
60 // For the **browser**, when faced with multiple values, we just take
61 // the **last** one, which is the original passed in value before
62 // prefixing. This _should_ work, because `inline-style-prefixer`
63 // we're just passing through what would happen without ISP.
64
65 val = val[val.length - 1].toString();
66 } else {
67 // For the **server**, we just concatenate things together and convert
68 // the style object values into a hacked-up string of like `display:
69 // "-webkit-flex;display:flex"` that will SSR render correctly to like
70 // `"display:-webkit-flex;display:flex"` but would otherwise be
71 // totally invalid values.
72
73 // We convert keys to dash-case only for the serialize values and
74 // leave the real key camel-cased so it's as expected to React and
75 // other parts of the processing chain.
76 val = val.join(`;${camelCaseToDashCase(key)}:`);
77 }
78 }
79
80 newStyle[key] = val;
81 return newStyle;
82 },
83 {}
84 );
85}
86
87let _hasWarnedAboutUserAgent = false;
88let _lastUserAgent;
89let _cachedPrefixer;
90
91function getPrefixer(
92 userAgent: ?string
93): {
94 +prefix: (style: Object) => Object,
95 prefixedKeyframes: string
96} {
97 const actualUserAgent = userAgent ||
98 (global && global.navigator && global.navigator.userAgent);
99
100 if (process.env.NODE_ENV !== 'production') {
101 if (!actualUserAgent && !_hasWarnedAboutUserAgent) {
102 /* eslint-disable no-console */
103 console.warn(
104 'Radium: userAgent should be supplied for server-side rendering. See ' +
105 'https://github.com/FormidableLabs/radium/tree/master/docs/api#radium ' +
106 'for more information.'
107 );
108 /* eslint-enable no-console */
109 _hasWarnedAboutUserAgent = true;
110 }
111 }
112
113 if (
114 process.env.NODE_ENV === 'test' ||
115 (!_cachedPrefixer || actualUserAgent !== _lastUserAgent)
116 ) {
117 if (actualUserAgent === 'all') {
118 _cachedPrefixer = {
119 prefix: prefixAll,
120 prefixedKeyframes: 'keyframes'
121 };
122 } else {
123 _cachedPrefixer = new InlineStylePrefixer({userAgent: actualUserAgent});
124 }
125 _lastUserAgent = actualUserAgent;
126 }
127
128 return _cachedPrefixer;
129}
130
131export function getPrefixedKeyframes(userAgent?: ?string): string {
132 return getPrefixer(userAgent).prefixedKeyframes || 'keyframes';
133}
134
135// Returns a new style object with vendor prefixes added to property names and
136// values.
137export function getPrefixedStyle(style: Object, userAgent?: ?string): Object {
138 const styleWithFallbacks = transformValues(style);
139 const prefixer = getPrefixer(userAgent);
140 const prefixedStyle = prefixer.prefix(styleWithFallbacks);
141 const flattenedStyle = flattenStyleValues(prefixedStyle);
142 return flattenedStyle;
143}