[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 #1136
Fix #1044
Fix #1223
Fix #13
This commit is contained in:
Nicolas Gallagher
2019-03-12 13:00:57 -07:00
parent 9f860b8dfc
commit d4417e93a3
16 changed files with 255 additions and 207 deletions
+1 -1
View File
@@ -146,7 +146,7 @@ React Native v0.55
| SwipeableFlatList | ✓ | |
| SwipeableListView | ✓ | |
| Switch | ✓ | |
| 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. |
| Text | ✓ | Missing `onLongPress` ([#1011](https://github.com/necolas/react-native-web/issues/1011)) support. |
| 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)). |
| Touchable | ✓ | Includes additional support for mouse and keyboard interactions. |
| TouchableHighlight | ✓ | |
@@ -19,5 +19,18 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
@media all {
[stylesheet-group=\\"0.1\\"]{}
:focus:not([data-focusvisible-polyfill]){outline: none;}
}
@media all {
[stylesheet-group=\\"1\\"]{}
.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; }
.css-cursor-18t94o4 { cursor: pointer; }
.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; }
.css-hitSlop-mjp8i1 { bottom: 0px; left: 0px; position: absolute; right: 0px; top: 0px; z-index: -1; }
.css-accessibilityImage-9pa8cd { bottom: 0px; height: 100%; left: 0px; opacity: 0; position: absolute; right: 0px; top: 0px; width: 100%; z-index: -1; }
.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; }
.css-textHasAncestor-16my406 { color: inherit; font: inherit; white-space: inherit; }
.css-textOneLine-bfa6kz { max-width: 100%; overflow-x: hidden; overflow-y: hidden; text-overflow: ellipsis; white-space: nowrap; }
.css-textMultiLine-cens5h { -webkit-box-orient: vertical; display: -webkit-box; max-width: 100%; overflow-x: hidden; overflow-y: hidden; text-overflow: ellipsis; }
.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; }
}</style>"
`;
+13 -9
View File
@@ -10,6 +10,7 @@
import applyNativeMethods from '../../modules/applyNativeMethods';
import createElement from '../createElement';
import css from '../StyleSheet/css';
import { getAssetByID } from '../../modules/AssetRegistry';
import resolveShadowValue from '../StyleSheet/resolveShadowValue';
import ImageLoader from '../../modules/ImageLoader';
@@ -254,10 +255,10 @@ class Image extends Component<*, State> {
const hiddenImage = displayImageUri
? createElement('img', {
alt: accessibilityLabel || '',
className: classes.accessibilityImage,
draggable: draggable || false,
ref: this._setImageRef,
src: displayImageUri,
style: styles.accessibilityImage
src: displayImageUri
})
: null;
@@ -387,6 +388,16 @@ class Image extends Component<*, State> {
}
}
const classes = css.create({
accessibilityImage: {
...StyleSheet.absoluteFillObject,
height: '100%',
opacity: 0,
width: '100%',
zIndex: -1
}
});
const styles = StyleSheet.create({
root: {
flexBasis: 'auto',
@@ -405,13 +416,6 @@ const styles = StyleSheet.create({
height: '100%',
width: '100%',
zIndex: -1
},
accessibilityImage: {
...StyleSheet.absoluteFillObject,
height: '100%',
opacity: 0,
width: '100%',
zIndex: -1
}
});
@@ -103,10 +103,7 @@ StyleSheetValidation.addValidStylePropTypes({
objectFit: oneOf(['fill', 'contain', 'cover', 'none', 'scale-down']),
objectPosition: string,
pointerEvents: string,
tableLayout: string,
/* @private */
MozAppearance: string,
WebkitAppearance: string
tableLayout: string
});
export default StyleSheetValidation;
@@ -1,17 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StyleSheet/createReactDOMStyle fontFamily "Noto, BlinkMacSystemFont" 1`] = `
Object {
"fontFamily": "Noto, BlinkMacSystemFont",
}
`;
exports[`StyleSheet/createReactDOMStyle fontFamily "Noto, System" 1`] = `
Object {
"fontFamily": "Noto, system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
}
`;
exports[`StyleSheet/createReactDOMStyle fontFamily "Noto, System" 2`] = `
Object {
"font": "14px Noto, system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
}
`;
exports[`StyleSheet/createReactDOMStyle fontFamily "System" 1`] = `
Object {
"fontFamily": "system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
}
`;
exports[`StyleSheet/createReactDOMStyle fontFamily "System" 2`] = `
Object {
"font": "14px system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
}
`;
exports[`StyleSheet/createReactDOMStyle fontFamily "monospace" 1`] = `
Object {
"fontFamily": "monospace, monospace",
@@ -39,44 +39,17 @@ describe('StyleSheet/createReactDOMStyle', () => {
expect(createReactDOMStyle(style)).toMatchSnapshot();
});
describe('borderWidth styles', () => {
test('defaults to 0 when "null"', () => {
expect(createReactDOMStyle({ borderWidth: null })).toEqual({
borderTopWidth: '0px',
borderRightWidth: '0px',
borderBottomWidth: '0px',
borderLeftWidth: '0px'
});
expect(createReactDOMStyle({ borderWidth: 2, borderRightWidth: null })).toEqual({
borderTopWidth: '2px',
borderRightWidth: '0px',
borderBottomWidth: '2px',
borderLeftWidth: '2px'
});
});
});
describe('flexbox styles', () => {
test('flex defaults', () => {
expect(createReactDOMStyle({ display: 'flex' })).toEqual({
display: 'flex',
flexShrink: 0,
flexBasis: 'auto'
});
});
test('flex: -1', () => {
expect(createReactDOMStyle({ display: 'flex', flex: -1 })).toEqual({
display: 'flex',
expect(createReactDOMStyle({ flex: -1 })).toEqual({
flexBasis: 'auto',
flexGrow: 0,
flexShrink: 1,
flexBasis: 'auto'
flexShrink: 1
});
});
test('flex: 0', () => {
expect(createReactDOMStyle({ display: 'flex', flex: 0 })).toEqual({
display: 'flex',
expect(createReactDOMStyle({ flex: 0 })).toEqual({
flexGrow: 0,
flexShrink: 0,
flexBasis: '0%'
@@ -84,8 +57,7 @@ describe('StyleSheet/createReactDOMStyle', () => {
});
test('flex: 1', () => {
expect(createReactDOMStyle({ display: 'flex', flex: 1 })).toEqual({
display: 'flex',
expect(createReactDOMStyle({ flex: 1 })).toEqual({
flexGrow: 1,
flexShrink: 1,
flexBasis: '0%'
@@ -93,8 +65,7 @@ describe('StyleSheet/createReactDOMStyle', () => {
});
test('flex: 10', () => {
expect(createReactDOMStyle({ display: 'flex', flex: 10 })).toEqual({
display: 'flex',
expect(createReactDOMStyle({ flex: 10 })).toEqual({
flexGrow: 10,
flexShrink: 1,
flexBasis: '0%'
@@ -103,15 +74,12 @@ describe('StyleSheet/createReactDOMStyle', () => {
test('flexBasis overrides', () => {
// is flex-basis applied?
expect(createReactDOMStyle({ display: 'flex', flexBasis: '25%' })).toEqual({
display: 'flex',
flexShrink: 0,
expect(createReactDOMStyle({ flexBasis: '25%' })).toEqual({
flexBasis: '25%'
});
// can flex-basis override the 'flex' expansion?
expect(createReactDOMStyle({ display: 'flex', flex: 1, flexBasis: '25%' })).toEqual({
display: 'flex',
expect(createReactDOMStyle({ flex: 1, flexBasis: '25%' })).toEqual({
flexGrow: 1,
flexShrink: 1,
flexBasis: '25%'
@@ -120,15 +88,12 @@ describe('StyleSheet/createReactDOMStyle', () => {
test('flexShrink overrides', () => {
// is flex-shrink applied?
expect(createReactDOMStyle({ display: 'flex', flexShrink: 1 })).toEqual({
display: 'flex',
flexShrink: 1,
flexBasis: 'auto'
expect(createReactDOMStyle({ flexShrink: 1 })).toEqual({
flexShrink: 1
});
// can flex-shrink override the 'flex' expansion?
expect(createReactDOMStyle({ display: 'flex', flex: 1, flexShrink: 2 })).toEqual({
display: 'flex',
expect(createReactDOMStyle({ flex: 1, flexShrink: 2 })).toEqual({
flexGrow: 1,
flexShrink: 2,
flexBasis: '0%'
@@ -147,10 +112,16 @@ describe('StyleSheet/createReactDOMStyle', () => {
test('"System"', () => {
expect(createReactDOMStyle({ fontFamily: 'System' })).toMatchSnapshot();
expect(createReactDOMStyle({ font: '14px System' })).toMatchSnapshot();
});
test('"Noto, System"', () => {
expect(createReactDOMStyle({ fontFamily: 'Noto, System' })).toMatchSnapshot();
expect(createReactDOMStyle({ font: '14px Noto, System' })).toMatchSnapshot();
});
test('"Noto, BlinkMacSystemFont"', () => {
expect(createReactDOMStyle({ fontFamily: 'Noto, BlinkMacSystemFont' })).toMatchSnapshot();
});
});
@@ -42,14 +42,6 @@ const styleShortFormProperties = {
writingDirection: ['direction']
};
const borderWidthProps = {
borderWidth: true,
borderTopWidth: true,
borderRightWidth: true,
borderBottomWidth: true,
borderLeftWidth: true
};
const monospaceFontStack = 'monospace, monospace';
const systemFontStack =
'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif';
@@ -96,13 +88,7 @@ const createReactDOMStyle = style => {
Object.keys(style)
.sort()
.forEach(prop => {
let value = normalizeValueWithProperty(style[prop], prop);
// Make sure the default border width is explicitly set to '0' to avoid
// falling back to any unwanted user-agent styles.
if (borderWidthProps[prop]) {
value = value == null ? normalizeValueWithProperty(0) : value;
}
const value = normalizeValueWithProperty(style[prop], prop);
// Ignore everything else with a null value
if (value == null) {
@@ -129,21 +115,6 @@ const createReactDOMStyle = style => {
break;
}
case 'display': {
resolvedStyle.display = value;
// A flex container in React Native has these defaults which should be
// set only if there is no otherwise supplied flex style.
if (style.display === 'flex' && style.flex == null) {
if (style.flexShrink == null) {
resolvedStyle.flexShrink = 0;
}
if (style.flexBasis == null) {
resolvedStyle.flexBasis = 'auto';
}
}
break;
}
// The 'flex' property value in React Native must be a positive integer,
// 0, or -1.
case 'flex': {
+51
View File
@@ -0,0 +1,51 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
*/
import { classic } from './compile';
import styleResolver from './styleResolver';
import { STYLE_GROUPS } from './constants';
/**
* A simple (and dangerous) CSS system.
* The order of CSS rule insertion is not guaranteed.
* Avoiding combining 2 or more classes that modify the same property.
*/
const css = {
/**
* const classes = css.create({ base: {}, extra: {} })
*/
create(rules) {
const result = {};
Object.keys(rules).forEach(name => {
const style = rules[name];
const compiled = classic(style, name);
Object.values(compiled).forEach(({ identifier, rules }) => {
rules.forEach(rule => {
styleResolver.sheet.insert(rule, STYLE_GROUPS.classic);
});
result[name] = identifier;
});
});
return result;
},
/**
* css.combine(classes.base, classes.extra)
*/
combine(...args) {
return args.reduce((className, value) => {
if (value) {
className += className.length > 0 ? ' ' + value : value;
}
return className;
}, '');
}
};
export default css;
@@ -2,7 +2,7 @@
exports[`components/Text prop "onPress" 1`] = `
<div
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"
className="css-text-76zvg2 rn-cursor-1loqt21"
data-focusable={true}
dir="auto"
onClick={[Function]}
@@ -13,14 +13,14 @@ exports[`components/Text prop "onPress" 1`] = `
exports[`components/Text prop "selectable" 1`] = `
<div
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"
className="css-text-76zvg2"
dir="auto"
/>
`;
exports[`components/Text prop "selectable" 2`] = `
<div
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"
className="css-text-76zvg2 rn-userSelect-lrvibr"
dir="auto"
/>
`;
+33 -23
View File
@@ -13,6 +13,7 @@ import applyNativeMethods from '../../modules/applyNativeMethods';
import { bool } from 'prop-types';
import { Component } from 'react';
import createElement from '../createElement';
import css from '../StyleSheet/css';
import StyleSheet from '../StyleSheet';
import TextPropTypes from './TextPropTypes';
@@ -65,14 +66,19 @@ class Text extends Component<*> {
otherProps.onKeyDown = this._createEnterHandler(onPress);
}
otherProps.className = css.combine(
this.props.className,
classes.text,
this.context.isInAParentText === true && classes.textHasAncestor,
numberOfLines === 1 && classes.textOneLine,
numberOfLines > 1 && classes.textMultiLine
);
// allow browsers to automatically infer the language writing direction
otherProps.dir = dir !== undefined ? dir : 'auto';
otherProps.style = [
styles.initial,
this.context.isInAParentText === true && styles.isInAParentText,
style,
numberOfLines > 1 && { WebkitLineClamp: numberOfLines },
selectable === false && styles.notSelectable,
numberOfLines === 1 && styles.singleLineStyle,
onPress && styles.pressable
];
@@ -97,41 +103,45 @@ class Text extends Component<*> {
}
}
const styles = StyleSheet.create({
initial: {
const classes = css.create({
text: {
borderWidth: 0,
boxSizing: 'border-box',
color: 'inherit',
color: 'black',
display: 'inline',
fontFamily: 'System',
fontSize: 14,
fontStyle: 'inherit',
fontVariant: ['inherit'],
fontWeight: 'inherit',
lineHeight: 'inherit',
font: '14px System',
margin: 0,
padding: 0,
textDecorationLine: 'none',
whiteSpace: 'pre-wrap',
wordWrap: 'break-word'
},
isInAParentText: {
// inherit parent font styles
fontFamily: 'inherit',
fontSize: 'inherit',
textHasAncestor: {
color: 'inherit',
font: 'inherit',
whiteSpace: 'inherit'
},
textOneLine: {
maxWidth: '100%',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
},
// See #13
textMultiLine: {
display: '-webkit-box',
maxWidth: '100%',
overflow: 'hidden',
textOverflow: 'ellipsis',
WebkitBoxOrient: 'vertical'
}
});
const styles = StyleSheet.create({
notSelectable: {
userSelect: 'none'
},
pressable: {
cursor: 'pointer'
},
singleLineStyle: {
maxWidth: '100%',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}
});
+8 -13
View File
@@ -14,8 +14,8 @@ import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import { Component } from 'react';
import ColorPropType from '../ColorPropType';
import createElement from '../createElement';
import css from '../StyleSheet/css';
import findNodeHandle from '../findNodeHandle';
import StyleSheet from '../StyleSheet';
import StyleSheetPropType from '../../modules/StyleSheetPropType';
import TextInputStylePropTypes from './TextInputStylePropTypes';
import TextInputState from '../../modules/TextInputState';
@@ -146,8 +146,7 @@ class TextInput extends Component<*> {
keyboardType: 'default',
multiline: false,
numberOfLines: 1,
secureTextEntry: false,
style: emptyObject
secureTextEntry: false
};
static State = TextInputState;
@@ -180,7 +179,6 @@ class TextInput extends Component<*> {
multiline,
numberOfLines,
secureTextEntry,
style,
/* eslint-disable */
blurOnSubmit,
clearTextOnFocus,
@@ -260,6 +258,7 @@ class TextInput extends Component<*> {
// https://bugs.chromium.org/p/chromium/issues/detail?id=468153#c164
autoComplete: autoComplete === 'off' ? 'noop' : autoComplete,
autoCorrect: autoCorrect ? 'on' : 'off',
className: classes.textinput,
dir: 'auto',
onBlur: normalizeEventHandler(this._handleBlur),
onChange: normalizeEventHandler(this._handleChange),
@@ -269,8 +268,7 @@ class TextInput extends Component<*> {
onSelect: normalizeEventHandler(this._handleSelectionChange),
readOnly: !editable,
ref: this._setNode,
spellCheck: spellCheck != null ? spellCheck : autoCorrect,
style: [styles.initial, style]
spellCheck: spellCheck != null ? spellCheck : autoCorrect
});
if (multiline) {
@@ -418,18 +416,15 @@ class TextInput extends Component<*> {
};
}
const styles = StyleSheet.create({
initial: {
const classes = css.create({
textinput: {
MozAppearance: 'textfield',
WebkitAppearance: 'none',
backgroundColor: 'transparent',
borderColor: 'black',
border: '0 solid black',
borderRadius: 0,
borderStyle: 'solid',
borderWidth: 0,
boxSizing: 'border-box',
fontFamily: 'System',
fontSize: 14,
font: '14px System',
padding: 0,
resize: 'none'
}
@@ -37,6 +37,7 @@ export type ViewProps = {
accessibilityTraits?: string | Array<string>,
accessible?: boolean,
children?: any,
className?: string,
hitSlop?: EdgeInsetsProp,
importantForAccessibility?: 'auto' | 'yes' | 'no' | 'no-hide-descendants',
nativeID?: string,
+27 -16
View File
@@ -10,6 +10,7 @@ import applyLayout from '../../modules/applyLayout';
import applyNativeMethods from '../../modules/applyNativeMethods';
import { bool } from 'prop-types';
import createElement from '../createElement';
import css from '../StyleSheet/css';
import filterSupportedProps from './filterSupportedProps';
import invariant from 'fbjs/lib/invariant';
import StyleSheet from '../StyleSheet';
@@ -51,14 +52,18 @@ class View extends Component<ViewProps> {
const { isInAParentText } = this.context;
supportedProps.className = css.combine(this.props.className, classes.view);
supportedProps.style = StyleSheet.compose(
styles.initial,
StyleSheet.compose(isInAParentText && styles.inline, this.props.style)
isInAParentText && styles.inline,
this.props.style
);
if (hitSlop) {
const hitSlopStyle = calculateHitSlopStyle(hitSlop);
const hitSlopChild = createElement('span', { style: [styles.hitSlop, hitSlopStyle] });
const hitSlopChild = createElement('span', {
className: classes.hitSlop,
style: hitSlopStyle
});
supportedProps.children = React.Children.toArray([hitSlopChild, supportedProps.children]);
}
@@ -66,32 +71,38 @@ class View extends Component<ViewProps> {
}
}
const styles = StyleSheet.create({
// https://github.com/facebook/css-layout#default-values
initial: {
const classes = css.create({
view: {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
border: '0 solid black',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
minHeight: 0,
minWidth: 0,
padding: 0,
position: 'relative',
zIndex: 0,
// fix flexbox bugs
minHeight: 0,
minWidth: 0
},
inline: {
display: 'inline-flex'
zIndex: 0
},
// this zIndex-ordering positions the hitSlop above the View but behind
// its children
hitSlop: {
...StyleSheet.absoluteFillObject,
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: -1
}
});
const styles = StyleSheet.create({
inline: {
display: 'inline-flex'
}
});
export default applyLayout(applyNativeMethods(View));
@@ -2,10 +2,14 @@
exports[`modules/createDOMProps includes "rel" values for "a" elements (to securely open external links) 1`] = `" noopener noreferrer"`;
exports[`modules/createDOMProps includes cursor style for "button" role 1`] = `"rn-cursor-1loqt21"`;
exports[`modules/createDOMProps includes base reset style for browser-styled elements 1`] = `"css-reset-4rbku5"`;
exports[`modules/createDOMProps includes reset styles for "a" elements 1`] = `"rn-backgroundColor-1niwhzg rn-color-homxoj rn-textDecorationLine-13wfysu"`;
exports[`modules/createDOMProps includes base reset style for browser-styled elements 2`] = `"css-reset-4rbku5"`;
exports[`modules/createDOMProps includes reset styles for "button" elements 1`] = `"rn-appearance-30o5oe rn-backgroundColor-1niwhzg rn-color-homxoj rn-fontFamily-poiln3 rn-fontSize-7cikom rn-fontStyle-o11vmf rn-fontVariant-1kfwfc5 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-textAlign-1ttztb7"`;
exports[`modules/createDOMProps includes base reset style for browser-styled elements 3`] = `"css-reset-4rbku5"`;
exports[`modules/createDOMProps includes reset styles for "ul" elements 1`] = `"rn-listStyle-1ebb2ja"`;
exports[`modules/createDOMProps includes base reset style for browser-styled elements 4`] = `"css-reset-4rbku5"`;
exports[`modules/createDOMProps includes cursor style for pressable roles 1`] = `"css-cursor-18t94o4"`;
exports[`modules/createDOMProps includes cursor style for pressable roles 2`] = `"css-cursor-18t94o4"`;
@@ -193,23 +193,15 @@ describe('modules/createDOMProps', () => {
expect(props.rel).toMatchSnapshot();
});
test('includes reset styles for "a" elements', () => {
const props = createDOMProps('a');
expect(props.className).toMatchSnapshot();
test('includes cursor style for pressable roles', () => {
expect(createDOMProps('span', { accessibilityRole: 'link' }).className).toMatchSnapshot();
expect(createDOMProps('span', { accessibilityRole: 'button' }).className).toMatchSnapshot();
});
test('includes reset styles for "button" elements', () => {
const props = createDOMProps('button');
expect(props.className).toMatchSnapshot();
});
test('includes cursor style for "button" role', () => {
const props = createDOMProps('span', { accessibilityRole: 'button' });
expect(props.className).toMatchSnapshot();
});
test('includes reset styles for "ul" elements', () => {
const props = createDOMProps('ul');
expect(props.className).toMatchSnapshot();
test('includes base reset style for browser-styled elements', () => {
expect(createDOMProps('a').className).toMatchSnapshot();
expect(createDOMProps('button').className).toMatchSnapshot();
expect(createDOMProps('li').className).toMatchSnapshot();
expect(createDOMProps('ul').className).toMatchSnapshot();
});
});
+52 -42
View File
@@ -8,42 +8,25 @@
*/
import AccessibilityUtil from '../AccessibilityUtil';
import css from '../../exports/StyleSheet/css';
import StyleSheet from '../../exports/StyleSheet';
import styleResolver from '../../exports/StyleSheet/styleResolver';
const emptyObject = {};
const resetStyles = StyleSheet.create({
ariaButton: {
// Reset styles for heading, link, and list DOM elements
const classes = css.create({
reset: {
backgroundColor: 'transparent',
color: 'inherit',
font: 'inherit',
listStyle: 'none',
margin: 0,
textAlign: 'inherit',
textDecoration: 'none'
},
cursor: {
cursor: 'pointer'
},
button: {
appearance: 'none',
backgroundColor: 'transparent',
color: 'inherit',
fontFamily: 'inherit',
fontSize: 'inherit',
fontStyle: 'inherit',
fontVariant: ['inherit'],
fontWeight: 'inherit',
lineHeight: 'inherit',
textAlign: 'inherit'
},
heading: {
fontFamily: 'inherit',
fontSize: 'inherit',
fontStyle: 'inherit',
fontVariant: ['inherit'],
fontWeight: 'inherit',
lineHeight: 'inherit'
},
link: {
backgroundColor: 'transparent',
color: 'inherit',
textDecorationLine: 'none'
},
list: {
listStyle: 'none'
}
});
@@ -129,6 +112,8 @@ const createDOMProps = (component, props, styleResolver) => {
importantForAccessibility !== 'no-hide-descendants';
if (
role === 'link' ||
component === 'a' ||
component === 'button' ||
component === 'input' ||
component === 'select' ||
component === 'textarea'
@@ -152,24 +137,49 @@ const createDOMProps = (component, props, styleResolver) => {
// STYLE
// Resolve React Native styles to optimized browser equivalent
const reactNativeStyle = [
component === 'a' && resetStyles.link,
component === 'button' && resetStyles.button,
role === 'heading' && resetStyles.heading,
component === 'ul' && resetStyles.list,
role === 'button' && !disabled && resetStyles.ariaButton,
const reactNativeStyle = StyleSheet.compose(
pointerEvents && pointerEventsStyles[pointerEvents],
providedStyle,
placeholderTextColor && { placeholderTextColor }
];
StyleSheet.compose(
providedStyle,
placeholderTextColor && { placeholderTextColor }
)
);
const { className, style } = styleResolver(reactNativeStyle);
if (className && className.constructor === String) {
domProps.className = props.className ? `${props.className} ${className}` : className;
}
if (style) {
domProps.style = style;
}
// CLASSNAME
// Apply static style resets
let c;
// style interactive elements for mouse and mobile browsers
if ((role === 'button' || role === 'link') && !disabled) {
c = classes.cursor;
}
// style reset various elements (not all are used internally)
if (
component === 'a' ||
component === 'button' ||
component === 'li' ||
component === 'ul' ||
role === 'heading'
) {
c = classes.reset + (c != null ? ' ' + c : '');
}
// style from createElement use
if (props.className != null) {
c = props.className + (c != null ? ' ' + c : '');
}
// style from React Native StyleSheets
if (className != null && className !== '') {
c = (c != null ? c + ' ' : '') + className;
}
if (c != null) {
domProps.className = c;
}
// OTHER
// Native element ID
if (nativeID && nativeID.constructor === String) {