-
Notifications
You must be signed in to change notification settings - Fork 46.9k
/
CSSPropertyOperations.js
161 lines (149 loc) · 4.81 KB
/
CSSPropertyOperations.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {shorthandToLonghand} from './CSSShorthandProperty';
import dangerousStyleValue from './dangerousStyleValue';
import hyphenateStyleName from './hyphenateStyleName';
import warnValidStyle from './warnValidStyle';
import warning from 'shared/warning';
import {warnAboutShorthandPropertyCollision} from 'shared/ReactFeatureFlags';
/**
* Operations for dealing with CSS properties.
*/
/**
* This creates a string that is expected to be equivalent to the style
* attribute generated by server-side rendering. It by-passes warnings and
* security checks so it's not safe to use this value for anything other than
* comparison. It is only used in DEV for SSR validation.
*/
export function createDangerousStringForStyles(styles) {
if (__DEV__) {
let serialized = '';
let delimiter = '';
for (const styleName in styles) {
if (!styles.hasOwnProperty(styleName)) {
continue;
}
const styleValue = styles[styleName];
if (styleValue != null) {
const isCustomProperty = styleName.indexOf('--') === 0;
serialized += delimiter + hyphenateStyleName(styleName) + ':';
serialized += dangerousStyleValue(
styleName,
styleValue,
isCustomProperty,
);
delimiter = ';';
}
}
return serialized || null;
}
}
/**
* Sets the value for multiple styles on a node. If a value is specified as
* '' (empty string), the corresponding style property will be unset.
*
* @param {DOMElement} node
* @param {object} styles
*/
export function setValueForStyles(node, styles) {
const style = node.style;
for (let styleName in styles) {
if (!styles.hasOwnProperty(styleName)) {
continue;
}
const isCustomProperty = styleName.indexOf('--') === 0;
if (__DEV__) {
if (!isCustomProperty) {
warnValidStyle(styleName, styles[styleName]);
}
}
const styleValue = dangerousStyleValue(
styleName,
styles[styleName],
isCustomProperty,
);
if (styleName === 'float') {
styleName = 'cssFloat';
}
if (isCustomProperty) {
style.setProperty(styleName, styleValue);
} else {
style[styleName] = styleValue;
}
}
}
function isValueEmpty(value) {
return value == null || typeof value === 'boolean' || value === '';
}
/**
* Given {color: 'red', overflow: 'hidden'} returns {
* color: 'color',
* overflowX: 'overflow',
* overflowY: 'overflow',
* }. This can be read as "the overflowY property was set by the overflow
* shorthand". That is, the values are the property that each was derived from.
*/
function expandShorthandMap(styles) {
const expanded = {};
for (const key in styles) {
const longhands = shorthandToLonghand[key] || [key];
for (let i = 0; i < longhands.length; i++) {
expanded[longhands[i]] = key;
}
}
return expanded;
}
/**
* When mixing shorthand and longhand property names, we warn during updates if
* we expect an incorrect result to occur. In particular, we warn for:
*
* Updating a shorthand property (longhand gets overwritten):
* {font: 'foo', fontVariant: 'bar'} -> {font: 'baz', fontVariant: 'bar'}
* becomes .style.font = 'baz'
* Removing a shorthand property (longhand gets lost too):
* {font: 'foo', fontVariant: 'bar'} -> {fontVariant: 'bar'}
* becomes .style.font = ''
* Removing a longhand property (should revert to shorthand; doesn't):
* {font: 'foo', fontVariant: 'bar'} -> {font: 'foo'}
* becomes .style.fontVariant = ''
*/
export function validateShorthandPropertyCollisionInDev(
styleUpdates,
nextStyles,
) {
if (!warnAboutShorthandPropertyCollision) {
return;
}
if (!nextStyles) {
return;
}
const expandedUpdates = expandShorthandMap(styleUpdates);
const expandedStyles = expandShorthandMap(nextStyles);
const warnedAbout = {};
for (const key in expandedUpdates) {
const originalKey = expandedUpdates[key];
const correctOriginalKey = expandedStyles[key];
if (correctOriginalKey && originalKey !== correctOriginalKey) {
const warningKey = originalKey + ',' + correctOriginalKey;
if (warnedAbout[warningKey]) {
continue;
}
warnedAbout[warningKey] = true;
warning(
false,
'%s a style property during rerender (%s) when a ' +
'conflicting property is set (%s) can lead to styling bugs. To ' +
"avoid this, don't mix shorthand and non-shorthand properties " +
'for the same value; instead, replace the shorthand with ' +
'separate values.',
isValueEmpty(styleUpdates[originalKey]) ? 'Removing' : 'Updating',
originalKey,
correctOriginalKey,
);
}
}
}