mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-08-15 18:45:06 +00:00
[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:
@@ -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 | ✓ | |
|
||||
|
||||
+13
@@ -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
@@ -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;
|
||||
|
||||
+18
@@ -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",
|
||||
|
||||
+17
-46
@@ -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': {
|
||||
|
||||
@@ -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;
|
||||
+3
-3
@@ -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
@@ -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
@@ -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
@@ -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));
|
||||
|
||||
+8
-4
@@ -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"`;
|
||||
|
||||
+8
-16
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user