Skip to content

Commit d4417e9

Browse files
committed
[change] Use classic CSS in View, Text, etc., implementations
The CSS base styles for certain primitives are implemented using classic CSS to reduce browser layout times and better support 'null' values in StyleSheet-defined styles. Combined with the previous patch this reduces the benchmark layout times by about 30%. Ref necolas#1136 Fix necolas#1044 Fix necolas#1223 Fix necolas#13
1 parent 9f860b8 commit d4417e9

File tree

16 files changed

+253
-205
lines changed

16 files changed

+253
-205
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ React Native v0.55
146146
| SwipeableFlatList || |
147147
| SwipeableListView || |
148148
| Switch || |
149-
| Text || Missing `onLongPress` ([#1011](https://github.com/necolas/react-native-web/issues/1011)) and `numberOfLines` ([#13](https://github.com/necolas/react-native-web/issues/13)) support. |
149+
| Text || Missing `onLongPress` ([#1011](https://github.com/necolas/react-native-web/issues/1011)) support. |
150150
| TextInput || Missing rich text features ([#1023](https://github.com/necolas/react-native-web/issues/1023)), and auto-expanding behaviour ([#795](https://github.com/necolas/react-native-web/issues/795)). |
151151
| Touchable || Includes additional support for mouse and keyboard interactions. |
152152
| TouchableHighlight || |

Diff for: packages/react-native-web/src/exports/AppRegistry/__tests__/__snapshots__/index-test.js.snap

+13
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,18 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
1919
@media all {
2020
[stylesheet-group=\\"0.1\\"]{}
2121
:focus:not([data-focusvisible-polyfill]){outline: none;}
22+
}
23+
@media all {
24+
[stylesheet-group=\\"1\\"]{}
25+
.css-reset-4rbku5 { background-color: rgba(0,0,0,0.00); color: inherit; font: inherit; list-style: none; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: inherit; text-decoration: none; }
26+
.css-cursor-18t94o4 { cursor: pointer; }
27+
.css-view-1dbjc4n { -ms-flex-align: stretch; -ms-flex-direction: column; -ms-flex-negative: 0; -ms-flex-preferred-size: auto; -webkit-align-items: stretch; -webkit-box-align: stretch; -webkit-box-direction: normal; -webkit-box-orient: vertical; -webkit-flex-basis: auto; -webkit-flex-direction: column; -webkit-flex-shrink: 0; align-items: stretch; border: 0 solid black; box-sizing: border-box; display: -webkit-box;display: -moz-box;display: -ms-flexbox;display: -webkit-flex;display: flex; flex-basis: auto; flex-direction: column; flex-shrink: 0; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; min-height: 0px; min-width: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; position: relative; z-index: 0; }
28+
.css-hitSlop-mjp8i1 { bottom: 0px; left: 0px; position: absolute; right: 0px; top: 0px; z-index: -1; }
29+
.css-accessibilityImage-9pa8cd { bottom: 0px; height: 100%; left: 0px; opacity: 0; position: absolute; right: 0px; top: 0px; width: 100%; z-index: -1; }
30+
.css-text-76zvg2 { border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; box-sizing: border-box; color: rgba(0,0,0,1.00); display: inline; font: 14px system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; white-space: pre-wrap; word-wrap: break-word; }
31+
.css-textHasAncestor-16my406 { color: inherit; font: inherit; white-space: inherit; }
32+
.css-textOneLine-bfa6kz { max-width: 100%; overflow-x: hidden; overflow-y: hidden; text-overflow: ellipsis; white-space: nowrap; }
33+
.css-textMultiLine-cens5h { -webkit-box-orient: vertical; display: -webkit-box; max-width: 100%; overflow-x: hidden; overflow-y: hidden; text-overflow: ellipsis; }
34+
.css-textinput-1cwyjr8 { -moz-appearance: textfield; -webkit-appearance: none; background-color: rgba(0,0,0,0.00); border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; border-top-left-radius: 0px; border-top-right-radius: 0px; border: 0 solid black; box-sizing: border-box; font: 14px system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; resize: none; }
2235
}</style>"
2336
`;

Diff for: packages/react-native-web/src/exports/Image/index.js

+13-9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import applyNativeMethods from '../../modules/applyNativeMethods';
1212
import createElement from '../createElement';
13+
import css from '../StyleSheet/css';
1314
import { getAssetByID } from '../../modules/AssetRegistry';
1415
import resolveShadowValue from '../StyleSheet/resolveShadowValue';
1516
import ImageLoader from '../../modules/ImageLoader';
@@ -254,10 +255,10 @@ class Image extends Component<*, State> {
254255
const hiddenImage = displayImageUri
255256
? createElement('img', {
256257
alt: accessibilityLabel || '',
258+
className: classes.accessibilityImage,
257259
draggable: draggable || false,
258260
ref: this._setImageRef,
259-
src: displayImageUri,
260-
style: styles.accessibilityImage
261+
src: displayImageUri
261262
})
262263
: null;
263264

@@ -387,6 +388,16 @@ class Image extends Component<*, State> {
387388
}
388389
}
389390

391+
const classes = css.create({
392+
accessibilityImage: {
393+
...StyleSheet.absoluteFillObject,
394+
height: '100%',
395+
opacity: 0,
396+
width: '100%',
397+
zIndex: -1
398+
}
399+
});
400+
390401
const styles = StyleSheet.create({
391402
root: {
392403
flexBasis: 'auto',
@@ -405,13 +416,6 @@ const styles = StyleSheet.create({
405416
height: '100%',
406417
width: '100%',
407418
zIndex: -1
408-
},
409-
accessibilityImage: {
410-
...StyleSheet.absoluteFillObject,
411-
height: '100%',
412-
opacity: 0,
413-
width: '100%',
414-
zIndex: -1
415419
}
416420
});
417421

Diff for: packages/react-native-web/src/exports/StyleSheet/StyleSheetValidation.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,7 @@ StyleSheetValidation.addValidStylePropTypes({
103103
objectFit: oneOf(['fill', 'contain', 'cover', 'none', 'scale-down']),
104104
objectPosition: string,
105105
pointerEvents: string,
106-
tableLayout: string,
107-
/* @private */
108-
MozAppearance: string,
109-
WebkitAppearance: string
106+
tableLayout: string
110107
});
111108

112109
export default StyleSheetValidation;

Diff for: packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/createReactDOMStyle-test.js.snap

+18
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,35 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`StyleSheet/createReactDOMStyle fontFamily "Noto, BlinkMacSystemFont" 1`] = `
4+
Object {
5+
"fontFamily": "Noto, BlinkMacSystemFont",
6+
}
7+
`;
8+
39
exports[`StyleSheet/createReactDOMStyle fontFamily "Noto, System" 1`] = `
410
Object {
511
"fontFamily": "Noto, system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
612
}
713
`;
814

15+
exports[`StyleSheet/createReactDOMStyle fontFamily "Noto, System" 2`] = `
16+
Object {
17+
"font": "14px Noto, system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
18+
}
19+
`;
20+
921
exports[`StyleSheet/createReactDOMStyle fontFamily "System" 1`] = `
1022
Object {
1123
"fontFamily": "system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
1224
}
1325
`;
1426

27+
exports[`StyleSheet/createReactDOMStyle fontFamily "System" 2`] = `
28+
Object {
29+
"font": "14px system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
30+
}
31+
`;
32+
1533
exports[`StyleSheet/createReactDOMStyle fontFamily "monospace" 1`] = `
1634
Object {
1735
"fontFamily": "monospace, monospace",

Diff for: packages/react-native-web/src/exports/StyleSheet/__tests__/createReactDOMStyle-test.js

+17-46
Original file line numberDiff line numberDiff line change
@@ -39,62 +39,33 @@ describe('StyleSheet/createReactDOMStyle', () => {
3939
expect(createReactDOMStyle(style)).toMatchSnapshot();
4040
});
4141

42-
describe('borderWidth styles', () => {
43-
test('defaults to 0 when "null"', () => {
44-
expect(createReactDOMStyle({ borderWidth: null })).toEqual({
45-
borderTopWidth: '0px',
46-
borderRightWidth: '0px',
47-
borderBottomWidth: '0px',
48-
borderLeftWidth: '0px'
49-
});
50-
expect(createReactDOMStyle({ borderWidth: 2, borderRightWidth: null })).toEqual({
51-
borderTopWidth: '2px',
52-
borderRightWidth: '0px',
53-
borderBottomWidth: '2px',
54-
borderLeftWidth: '2px'
55-
});
56-
});
57-
});
58-
5942
describe('flexbox styles', () => {
60-
test('flex defaults', () => {
61-
expect(createReactDOMStyle({ display: 'flex' })).toEqual({
62-
display: 'flex',
63-
flexShrink: 0,
64-
flexBasis: 'auto'
65-
});
66-
});
67-
6843
test('flex: -1', () => {
69-
expect(createReactDOMStyle({ display: 'flex', flex: -1 })).toEqual({
70-
display: 'flex',
44+
expect(createReactDOMStyle({ flex: -1 })).toEqual({
45+
flexBasis: 'auto',
7146
flexGrow: 0,
72-
flexShrink: 1,
73-
flexBasis: 'auto'
47+
flexShrink: 1
7448
});
7549
});
7650

7751
test('flex: 0', () => {
78-
expect(createReactDOMStyle({ display: 'flex', flex: 0 })).toEqual({
79-
display: 'flex',
52+
expect(createReactDOMStyle({ flex: 0 })).toEqual({
8053
flexGrow: 0,
8154
flexShrink: 0,
8255
flexBasis: '0%'
8356
});
8457
});
8558

8659
test('flex: 1', () => {
87-
expect(createReactDOMStyle({ display: 'flex', flex: 1 })).toEqual({
88-
display: 'flex',
60+
expect(createReactDOMStyle({ flex: 1 })).toEqual({
8961
flexGrow: 1,
9062
flexShrink: 1,
9163
flexBasis: '0%'
9264
});
9365
});
9466

9567
test('flex: 10', () => {
96-
expect(createReactDOMStyle({ display: 'flex', flex: 10 })).toEqual({
97-
display: 'flex',
68+
expect(createReactDOMStyle({ flex: 10 })).toEqual({
9869
flexGrow: 10,
9970
flexShrink: 1,
10071
flexBasis: '0%'
@@ -103,15 +74,12 @@ describe('StyleSheet/createReactDOMStyle', () => {
10374

10475
test('flexBasis overrides', () => {
10576
// is flex-basis applied?
106-
expect(createReactDOMStyle({ display: 'flex', flexBasis: '25%' })).toEqual({
107-
display: 'flex',
108-
flexShrink: 0,
77+
expect(createReactDOMStyle({ flexBasis: '25%' })).toEqual({
10978
flexBasis: '25%'
11079
});
11180

11281
// can flex-basis override the 'flex' expansion?
113-
expect(createReactDOMStyle({ display: 'flex', flex: 1, flexBasis: '25%' })).toEqual({
114-
display: 'flex',
82+
expect(createReactDOMStyle({ flex: 1, flexBasis: '25%' })).toEqual({
11583
flexGrow: 1,
11684
flexShrink: 1,
11785
flexBasis: '25%'
@@ -120,15 +88,12 @@ describe('StyleSheet/createReactDOMStyle', () => {
12088

12189
test('flexShrink overrides', () => {
12290
// is flex-shrink applied?
123-
expect(createReactDOMStyle({ display: 'flex', flexShrink: 1 })).toEqual({
124-
display: 'flex',
125-
flexShrink: 1,
126-
flexBasis: 'auto'
91+
expect(createReactDOMStyle({ flexShrink: 1 })).toEqual({
92+
flexShrink: 1
12793
});
12894

12995
// can flex-shrink override the 'flex' expansion?
130-
expect(createReactDOMStyle({ display: 'flex', flex: 1, flexShrink: 2 })).toEqual({
131-
display: 'flex',
96+
expect(createReactDOMStyle({ flex: 1, flexShrink: 2 })).toEqual({
13297
flexGrow: 1,
13398
flexShrink: 2,
13499
flexBasis: '0%'
@@ -147,10 +112,16 @@ describe('StyleSheet/createReactDOMStyle', () => {
147112

148113
test('"System"', () => {
149114
expect(createReactDOMStyle({ fontFamily: 'System' })).toMatchSnapshot();
115+
expect(createReactDOMStyle({ font: '14px System' })).toMatchSnapshot();
150116
});
151117

152118
test('"Noto, System"', () => {
153119
expect(createReactDOMStyle({ fontFamily: 'Noto, System' })).toMatchSnapshot();
120+
expect(createReactDOMStyle({ font: '14px Noto, System' })).toMatchSnapshot();
121+
});
122+
123+
test('"Noto, BlinkMacSystemFont"', () => {
124+
expect(createReactDOMStyle({ fontFamily: 'Noto, BlinkMacSystemFont' })).toMatchSnapshot();
154125
});
155126
});
156127

Diff for: packages/react-native-web/src/exports/StyleSheet/createReactDOMStyle.js

+1-30
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,6 @@ const styleShortFormProperties = {
4242
writingDirection: ['direction']
4343
};
4444

45-
const borderWidthProps = {
46-
borderWidth: true,
47-
borderTopWidth: true,
48-
borderRightWidth: true,
49-
borderBottomWidth: true,
50-
borderLeftWidth: true
51-
};
52-
5345
const monospaceFontStack = 'monospace, monospace';
5446
const systemFontStack =
5547
'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif';
@@ -96,13 +88,7 @@ const createReactDOMStyle = style => {
9688
Object.keys(style)
9789
.sort()
9890
.forEach(prop => {
99-
let value = normalizeValueWithProperty(style[prop], prop);
100-
101-
// Make sure the default border width is explicitly set to '0' to avoid
102-
// falling back to any unwanted user-agent styles.
103-
if (borderWidthProps[prop]) {
104-
value = value == null ? normalizeValueWithProperty(0) : value;
105-
}
91+
const value = normalizeValueWithProperty(style[prop], prop);
10692

10793
// Ignore everything else with a null value
10894
if (value == null) {
@@ -129,21 +115,6 @@ const createReactDOMStyle = style => {
129115
break;
130116
}
131117

132-
case 'display': {
133-
resolvedStyle.display = value;
134-
// A flex container in React Native has these defaults which should be
135-
// set only if there is no otherwise supplied flex style.
136-
if (style.display === 'flex' && style.flex == null) {
137-
if (style.flexShrink == null) {
138-
resolvedStyle.flexShrink = 0;
139-
}
140-
if (style.flexBasis == null) {
141-
resolvedStyle.flexBasis = 'auto';
142-
}
143-
}
144-
break;
145-
}
146-
147118
// The 'flex' property value in React Native must be a positive integer,
148119
// 0, or -1.
149120
case 'flex': {
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Copyright (c) 2016-present, Nicolas Gallagher.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @noflow
8+
*/
9+
10+
import { classic } from './compile';
11+
import styleResolver from './styleResolver';
12+
import { STYLE_GROUPS } from './constants';
13+
14+
/**
15+
* A simple (and dangerous) CSS system.
16+
* The order of CSS rule insertion is not guaranteed.
17+
* Avoiding combining 2 or more classes that modify the same property.
18+
*/
19+
const css = {
20+
/**
21+
* const classes = css.create({ base: {}, extra: {} })
22+
*/
23+
create(rules) {
24+
const result = {};
25+
Object.keys(rules).forEach(name => {
26+
const style = rules[name];
27+
const compiled = classic(style, name);
28+
29+
Object.values(compiled).forEach(({ identifier, rules }) => {
30+
rules.forEach(rule => {
31+
styleResolver.sheet.insert(rule, STYLE_GROUPS.classic);
32+
});
33+
result[name] = identifier;
34+
});
35+
});
36+
return result;
37+
},
38+
/**
39+
* css.combine(classes.base, classes.extra)
40+
*/
41+
combine(...args) {
42+
return args.reduce((className, value) => {
43+
if (value) {
44+
className += className.length > 0 ? ' ' + value : value;
45+
}
46+
return className;
47+
}, '');
48+
}
49+
};
50+
51+
export default css;

Diff for: packages/react-native-web/src/exports/Text/__tests__/__snapshots__/index-test.js.snap

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
exports[`components/Text prop "onPress" 1`] = `
44
<div
5-
className="rn-borderWidth-1yadl64 rn-boxSizing-deolkf rn-color-homxoj rn-cursor-1loqt21 rn-display-1471scf rn-fontFamily-1qd0xha rn-fontSize-1b43r93 rn-fontStyle-o11vmf rn-fontVariant-1kfwfc5 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-margin-crgep1 rn-padding-t60dpp rn-textDecorationLine-13wfysu rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
5+
className="css-text-76zvg2 rn-cursor-1loqt21"
66
data-focusable={true}
77
dir="auto"
88
onClick={[Function]}
@@ -13,14 +13,14 @@ exports[`components/Text prop "onPress" 1`] = `
1313

1414
exports[`components/Text prop "selectable" 1`] = `
1515
<div
16-
className="rn-borderWidth-1yadl64 rn-boxSizing-deolkf rn-color-homxoj rn-display-1471scf rn-fontFamily-1qd0xha rn-fontSize-1b43r93 rn-fontStyle-o11vmf rn-fontVariant-1kfwfc5 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-margin-crgep1 rn-padding-t60dpp rn-textDecorationLine-13wfysu rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
16+
className="css-text-76zvg2"
1717
dir="auto"
1818
/>
1919
`;
2020

2121
exports[`components/Text prop "selectable" 2`] = `
2222
<div
23-
className="rn-borderWidth-1yadl64 rn-boxSizing-deolkf rn-color-homxoj rn-display-1471scf rn-fontFamily-1qd0xha rn-fontSize-1b43r93 rn-fontStyle-o11vmf rn-fontVariant-1kfwfc5 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-margin-crgep1 rn-padding-t60dpp rn-textDecorationLine-13wfysu rn-userSelect-lrvibr rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
23+
className="css-text-76zvg2 rn-userSelect-lrvibr"
2424
dir="auto"
2525
/>
2626
`;

0 commit comments

Comments
 (0)