mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-26 01:04:13 +08:00
Reorganize style resolver
The style resolver is reorganized to remove React Native style registration, and contain all the stateful resolver logic (previously partially found in NativeMethodsMixin). Style registration is done in 'StyleSheet.create', and will subsequently be decoupled from eager style resolution. The stateful resolver is fully contained in the ReactNativeStyleResolver. The options are removed from the resolver as they aren't needed to implement the correct i18n-ified styles. This functionality is also covered by unit tests now.
This commit is contained in:
@@ -9,17 +9,14 @@ exports[`apis/AppRegistry/renderApplication getApplication 1`] = `
|
||||
`;
|
||||
|
||||
exports[`apis/AppRegistry/renderApplication getApplication 2`] = `
|
||||
"<style id=\\"react-native-stylesheet\\">html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
"<style id=\\"react-native-stylesheet\\">@media all{
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
body{margin:0;}
|
||||
@media all{button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}}
|
||||
@media all{input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}}
|
||||
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
|
||||
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
|
||||
}
|
||||
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
|
||||
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
|
||||
.rn-bottom-1p0dtai{bottom:0px}
|
||||
.rn-left-1d2f490{left:0px}
|
||||
.rn-position-u8s1d{position:absolute}
|
||||
.rn-right-zchlnj{right:0px}
|
||||
.rn-top-ipm5af{top:0px}
|
||||
.rn-cursor-1loqt21{cursor:pointer}
|
||||
.rn-appearance-30o5oe{-moz-appearance:none;-webkit-appearance:none;appearance:none}
|
||||
.rn-backgroundColor-wib322{background-color:transparent}
|
||||
@@ -61,6 +58,11 @@ body{margin:0;}
|
||||
.rn-position-bnwqim{position:relative}
|
||||
.rn-zIndex-1lgpqti{z-index:0}
|
||||
.rn-display-xoduu5{display:-webkit-inline-box;display:-moz-inline-box;display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex}
|
||||
.rn-bottom-1p0dtai{bottom:0px}
|
||||
.rn-left-1d2f490{left:0px}
|
||||
.rn-position-u8s1d{position:absolute}
|
||||
.rn-right-zchlnj{right:0px}
|
||||
.rn-top-ipm5af{top:0px}
|
||||
.rn-zIndex-1wyyakw{z-index:-1}
|
||||
.rn-backgroundPosition-vvn4in{background-position:center}
|
||||
.rn-backgroundRepeat-u6sd8q{background-repeat:no-repeat}
|
||||
|
||||
@@ -17,36 +17,20 @@ import flattenStyle from './flattenStyle';
|
||||
import I18nManager from '../I18nManager';
|
||||
import i18nStyle from './i18nStyle';
|
||||
import { prefixInlineStyles } from '../../modules/prefixStyles';
|
||||
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
|
||||
import StyleSheetManager from './StyleSheetManager';
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
const createCacheKey = id => {
|
||||
const prefix = 'rn';
|
||||
return `${prefix}-${id}`;
|
||||
};
|
||||
|
||||
const classListToString = list => list.join(' ').trim();
|
||||
|
||||
export default class StyleSheetRegistry {
|
||||
export default class ReactNativeStyleResolver {
|
||||
cache = { ltr: {}, rtl: {} };
|
||||
|
||||
styleSheetManager = new StyleSheetManager();
|
||||
|
||||
getStyleSheets() {
|
||||
return this.styleSheetManager.getStyleSheets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers and precaches a React Native style object to HTML class names
|
||||
*/
|
||||
register(flatStyle) {
|
||||
const id = ReactNativePropRegistry.register(flatStyle);
|
||||
this._registerById(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
_registerById(id) {
|
||||
_injectRegisteredStyle(id) {
|
||||
const dir = I18nManager.isRTL ? 'rtl' : 'ltr';
|
||||
if (!this.cache[dir][id]) {
|
||||
const style = flattenStyle(id);
|
||||
@@ -54,7 +38,7 @@ export default class StyleSheetRegistry {
|
||||
Object.keys(domStyle).forEach(styleProp => {
|
||||
const value = domStyle[styleProp];
|
||||
if (value != null) {
|
||||
this.styleSheetManager.setDeclaration(styleProp, value);
|
||||
this.styleSheetManager.injectDeclaration(styleProp, value);
|
||||
}
|
||||
});
|
||||
this.cache[dir][id] = true;
|
||||
@@ -64,21 +48,21 @@ export default class StyleSheetRegistry {
|
||||
/**
|
||||
* Resolves a React Native style object to DOM attributes
|
||||
*/
|
||||
resolve(reactNativeStyle, options = emptyObject) {
|
||||
resolve(reactNativeStyle) {
|
||||
if (!reactNativeStyle) {
|
||||
return emptyObject;
|
||||
}
|
||||
|
||||
// fast and cachable
|
||||
if (typeof reactNativeStyle === 'number') {
|
||||
this._registerById(reactNativeStyle);
|
||||
this._injectRegisteredStyle(reactNativeStyle);
|
||||
const key = createCacheKey(reactNativeStyle);
|
||||
return this._resolveStyleIfNeeded(reactNativeStyle, options, key);
|
||||
return this._resolveStyleIfNeeded(reactNativeStyle, key);
|
||||
}
|
||||
|
||||
// resolve a plain RN style object
|
||||
if (!Array.isArray(reactNativeStyle)) {
|
||||
return this._resolveStyle(reactNativeStyle, options);
|
||||
return this._resolveStyleIfNeeded(reactNativeStyle);
|
||||
}
|
||||
|
||||
// flatten the style array
|
||||
@@ -91,11 +75,11 @@ export default class StyleSheetRegistry {
|
||||
if (typeof id !== 'number') {
|
||||
isArrayOfNumbers = false;
|
||||
} else {
|
||||
this._registerById(id);
|
||||
this._injectRegisteredStyle(id);
|
||||
}
|
||||
}
|
||||
const key = isArrayOfNumbers ? createCacheKey(flatArray.join('-')) : null;
|
||||
return this._resolveStyleIfNeeded(flatArray, options, key);
|
||||
return this._resolveStyleIfNeeded(flatArray, key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,9 +89,8 @@ export default class StyleSheetRegistry {
|
||||
* To determine the next style, some of the existing DOM state must be
|
||||
* converted back into React Native styles.
|
||||
*/
|
||||
resolveStateful(rnStyleNext, domStyleProps, options) {
|
||||
const { classList: rdomClassList, style: rdomStyle } = domStyleProps;
|
||||
|
||||
resolveWithNode(rnStyleNext, node) {
|
||||
const { classList: rdomClassList, style: rdomStyle } = getDOMStyleInfo(node);
|
||||
// Convert the DOM classList back into a React Native form
|
||||
// Preserves unrecognized class names.
|
||||
const { classList: rnClassList, style: rnStyle } = rdomClassList.reduce(
|
||||
@@ -124,11 +107,16 @@ export default class StyleSheetRegistry {
|
||||
);
|
||||
|
||||
// Create next DOM style props from current and next RN styles
|
||||
const { classList: rdomClassListNext, style: rdomStyleNext } = this.resolve(
|
||||
[rnStyle, rnStyleNext],
|
||||
options
|
||||
);
|
||||
const { classList: rdomClassListNext, style: rdomStyleNext } = this.resolve([
|
||||
I18nManager.isRTL ? i18nStyle(rnStyle) : rnStyle,
|
||||
rnStyleNext
|
||||
]);
|
||||
|
||||
// Final className
|
||||
// Add the current class names not managed by React Native
|
||||
const className = classListToString(rdomClassListNext.concat(rnClassList));
|
||||
|
||||
// Final style
|
||||
// Next class names take priority over current inline styles
|
||||
const style = { ...rdomStyle };
|
||||
rdomClassListNext.forEach(className => {
|
||||
@@ -137,22 +125,18 @@ export default class StyleSheetRegistry {
|
||||
style[prop] = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Next inline styles take priority over current inline styles
|
||||
Object.assign(style, rdomStyleNext);
|
||||
|
||||
// Add the current class names not managed by React Native
|
||||
const className = classListToString(rdomClassListNext.concat(rnClassList));
|
||||
|
||||
return { className, style };
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a React Native style object
|
||||
*/
|
||||
_resolveStyle(reactNativeStyle, options) {
|
||||
_resolveStyle(reactNativeStyle) {
|
||||
const flatStyle = flattenStyle(reactNativeStyle);
|
||||
const domStyle = createReactDOMStyle(options.i18n === false ? flatStyle : i18nStyle(flatStyle));
|
||||
const domStyle = createReactDOMStyle(i18nStyle(flatStyle));
|
||||
|
||||
const props = Object.keys(domStyle).reduce(
|
||||
(props, styleProp) => {
|
||||
@@ -166,7 +150,7 @@ export default class StyleSheetRegistry {
|
||||
// require more complex transforms into multiple CSS rules. Here we make sure the styles
|
||||
// can be represented by a className and don't end up as invalid inline-styles.
|
||||
if (styleProp === 'pointerEvents') {
|
||||
const className = this.styleSheetManager.setDeclaration(styleProp, value);
|
||||
const className = this.styleSheetManager.injectDeclaration(styleProp, value);
|
||||
props.classList.push(className);
|
||||
// } else if (styleProp ==='placeholderTextColor') {
|
||||
// } else if (styleProp ==='animationName') {
|
||||
@@ -194,15 +178,48 @@ export default class StyleSheetRegistry {
|
||||
/**
|
||||
* Caching layer over 'resolveStyle'
|
||||
*/
|
||||
_resolveStyleIfNeeded(style, options, key) {
|
||||
const dir = I18nManager.isRTL ? 'rtl' : 'ltr';
|
||||
_resolveStyleIfNeeded(style, key) {
|
||||
if (key) {
|
||||
const dir = I18nManager.isRTL ? 'rtl' : 'ltr';
|
||||
if (!this.cache[dir][key]) {
|
||||
// slow: convert style object to props and cache
|
||||
this.cache[dir][key] = this._resolveStyle(style, options);
|
||||
this.cache[dir][key] = this._resolveStyle(style);
|
||||
}
|
||||
return this.cache[dir][key];
|
||||
}
|
||||
return this._resolveStyle(style, options);
|
||||
return this._resolveStyle(style);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Misc helpers
|
||||
*/
|
||||
const createCacheKey = id => {
|
||||
const prefix = 'rn';
|
||||
return `${prefix}-${id}`;
|
||||
};
|
||||
|
||||
const classListToString = list => list.join(' ').trim();
|
||||
|
||||
/**
|
||||
* Copies classList and style data from a DOM node
|
||||
*/
|
||||
const hyphenPattern = /-([a-z])/g;
|
||||
const toCamelCase = str => str.replace(hyphenPattern, m => m[1].toUpperCase());
|
||||
|
||||
const getDOMStyleInfo = node => {
|
||||
const nodeStyle = node.style;
|
||||
const classList = Array.prototype.slice.call(node.classList);
|
||||
const style = {};
|
||||
// DOM style is a CSSStyleDeclaration
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
|
||||
for (let i = 0; i < nodeStyle.length; i += 1) {
|
||||
const property = nodeStyle.item(i);
|
||||
if (property) {
|
||||
// DOM style uses hyphenated prop names and may include vendor prefixes
|
||||
// Transform back into React DOM style.
|
||||
style[toCamelCase(property)] = nodeStyle.getPropertyValue(property);
|
||||
}
|
||||
}
|
||||
return { classList, style };
|
||||
};
|
||||
@@ -8,7 +8,7 @@
|
||||
* @noflow
|
||||
*/
|
||||
|
||||
import generateCss from './generateCss';
|
||||
import createAtomicRules from './createAtomicRules';
|
||||
import hash from '../../vendor/hash';
|
||||
import initialRules from './initialRules';
|
||||
import WebStyleSheet from './WebStyleSheet';
|
||||
@@ -21,62 +21,31 @@ const createClassName = (prop, value) => {
|
||||
return process.env.NODE_ENV !== 'production' ? `rn-${prop}-${hashed}` : `rn-${hashed}`;
|
||||
};
|
||||
|
||||
const createCssRules = (selector, prop, value) => {
|
||||
const rules = [];
|
||||
let v = value;
|
||||
|
||||
// pointerEvents is a special case that requires custom values and additional css rules
|
||||
// See #513
|
||||
if (prop === 'pointerEvents') {
|
||||
if (value === 'auto' || value === 'box-only') {
|
||||
v = 'auto !important';
|
||||
if (value === 'box-only') {
|
||||
const css = generateCss({ [prop]: 'none' });
|
||||
rules.push(`${selector} > *{${css}}`);
|
||||
}
|
||||
} else if (value === 'none' || value === 'box-none') {
|
||||
v = 'none !important';
|
||||
if (value === 'box-none') {
|
||||
const css = generateCss({ [prop]: 'auto' });
|
||||
rules.push(`${selector} > *{${css}}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const css = generateCss({ [prop]: v });
|
||||
rules.push(`${selector}{${css}}`);
|
||||
|
||||
return rules;
|
||||
};
|
||||
|
||||
export default class StyleSheetManager {
|
||||
cache = null;
|
||||
_webStyleSheet = null;
|
||||
_cache = {
|
||||
byClassName: {},
|
||||
byProp: {}
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.cache = {
|
||||
byClassName: {},
|
||||
byProp: {}
|
||||
};
|
||||
|
||||
this._webStyleSheet = new WebStyleSheet(STYLE_ELEMENT_ID);
|
||||
constructor(id) {
|
||||
this._id = this._sheet = new WebStyleSheet(STYLE_ELEMENT_ID);
|
||||
initialRules.forEach(rule => {
|
||||
this._webStyleSheet.insertRuleOnce(rule);
|
||||
this._sheet.insertRuleOnce(rule);
|
||||
});
|
||||
}
|
||||
|
||||
getClassName(prop, value) {
|
||||
const cache = this.cache.byProp;
|
||||
const cache = this._cache.byProp;
|
||||
return cache[prop] && cache[prop].hasOwnProperty(value) && cache[prop][value];
|
||||
}
|
||||
|
||||
getDeclaration(className) {
|
||||
const cache = this.cache.byClassName;
|
||||
const cache = this._cache.byClassName;
|
||||
return cache[className] || emptyObject;
|
||||
}
|
||||
|
||||
getStyleSheets() {
|
||||
const { cssText } = this._webStyleSheet;
|
||||
const { cssText } = this._sheet;
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -86,21 +55,21 @@ export default class StyleSheetManager {
|
||||
];
|
||||
}
|
||||
|
||||
setDeclaration(prop, value) {
|
||||
injectDeclaration(prop, value): string {
|
||||
let className = this.getClassName(prop, value);
|
||||
if (!className) {
|
||||
className = createClassName(prop, value);
|
||||
this._addToCache(className, prop, value);
|
||||
const rules = createCssRules(`.${className}`, prop, value);
|
||||
const rules = createAtomicRules(`.${className}`, prop, value);
|
||||
rules.forEach(rule => {
|
||||
this._webStyleSheet.insertRuleOnce(rule);
|
||||
this._sheet.insertRuleOnce(rule);
|
||||
});
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
||||
_addToCache(className, prop, value) {
|
||||
const cache = this.cache;
|
||||
const cache = this._cache;
|
||||
if (!cache.byProp[prop]) {
|
||||
cache.byProp[prop] = {};
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
|
||||
export default class WebStyleSheet {
|
||||
_cssRules = [];
|
||||
_domStyleElement = null;
|
||||
|
||||
constructor(id: string) {
|
||||
let _domStyleElement;
|
||||
|
||||
@@ -26,12 +29,10 @@ export default class WebStyleSheet {
|
||||
}
|
||||
}
|
||||
|
||||
this.id = id;
|
||||
this._domStyleElement = _domStyleElement;
|
||||
}
|
||||
|
||||
_cssRules = [];
|
||||
_domStyleElement = null;
|
||||
|
||||
containsRule(rule: string): boolean {
|
||||
return this._cssRules.indexOf(rule) > -1;
|
||||
}
|
||||
@@ -40,17 +41,21 @@ export default class WebStyleSheet {
|
||||
return this._cssRules.join('\n');
|
||||
}
|
||||
|
||||
insertRuleOnce(rule: string) {
|
||||
insertRuleOnce(rule: string, position: ?number) {
|
||||
// prevent duplicate rules
|
||||
if (!this.containsRule(rule)) {
|
||||
this._cssRules.push(rule);
|
||||
|
||||
// update the native stylesheet (i.e., browser)
|
||||
if (this._domStyleElement) {
|
||||
// Check whether a rule was part of any prerendered styles (textContent
|
||||
// doesn't include styles injected via 'insertRule')
|
||||
if (this._domStyleElement.textContent.indexOf(rule) === -1) {
|
||||
// $FlowFixMe
|
||||
this._domStyleElement.sheet.insertRule(rule, this._domStyleElement.sheet.cssRules.length);
|
||||
this._domStyleElement.sheet.insertRule(
|
||||
rule,
|
||||
position || this._domStyleElement.sheet.cssRules.length
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
125
packages/react-native-web/src/exports/StyleSheet/__tests__/ReactNativeStyleResolver-test.js
vendored
Normal file
125
packages/react-native-web/src/exports/StyleSheet/__tests__/ReactNativeStyleResolver-test.js
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import I18nManager from '../../I18nManager';
|
||||
import ReactNativePropRegistry from '../../../modules/ReactNativePropRegistry';
|
||||
import ReactNativeStyleResolver from '../ReactNativeStyleResolver';
|
||||
|
||||
let styleResolver;
|
||||
|
||||
describe('StyleSheet/ReactNativeStyleResolver', () => {
|
||||
beforeEach(() => {
|
||||
styleResolver = new ReactNativeStyleResolver();
|
||||
});
|
||||
|
||||
describe('resolve', () => {
|
||||
const styleA = { borderWidth: 0, borderColor: 'red', width: 100 };
|
||||
const styleB = {
|
||||
position: 'absolute',
|
||||
left: 50,
|
||||
opacity: 0.5
|
||||
};
|
||||
const styleC = { width: 200 };
|
||||
|
||||
const testResolve = (a, b, c) => {
|
||||
// no common properties, different resolving order, same result
|
||||
const resolve1 = styleResolver.resolve([a, b]);
|
||||
expect(resolve1).toMatchSnapshot();
|
||||
const resolve2 = styleResolver.resolve([b, a]);
|
||||
expect(resolve1).toEqual(resolve2);
|
||||
|
||||
// common properties, different resolving order, different result
|
||||
const resolve3 = styleResolver.resolve([a, b, c]);
|
||||
expect(resolve3).toMatchSnapshot();
|
||||
const resolve4 = styleResolver.resolve([c, a, b]);
|
||||
expect(resolve4).toMatchSnapshot();
|
||||
expect(resolve3).not.toEqual(resolve4);
|
||||
};
|
||||
|
||||
test('with register, resolves to className', () => {
|
||||
const a = ReactNativePropRegistry.register(styleA);
|
||||
const b = ReactNativePropRegistry.register(styleB);
|
||||
const c = ReactNativePropRegistry.register(styleC);
|
||||
testResolve(a, b, c);
|
||||
});
|
||||
|
||||
test('with register before RTL, resolves to className', () => {
|
||||
const a = ReactNativePropRegistry.register({ left: '12.34%' });
|
||||
const b = ReactNativePropRegistry.register({ textAlign: 'left' });
|
||||
const c = ReactNativePropRegistry.register({ marginLeft: 10 });
|
||||
I18nManager.forceRTL(true);
|
||||
const resolved = styleResolver.resolve([a, b, c]);
|
||||
I18nManager.forceRTL(false);
|
||||
expect(resolved).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('with register, resolves to mixed', () => {
|
||||
const a = styleA;
|
||||
const b = ReactNativePropRegistry.register(styleB);
|
||||
const c = ReactNativePropRegistry.register(styleC);
|
||||
testResolve(a, b, c);
|
||||
});
|
||||
|
||||
test('without register, resolves to inline styles', () => {
|
||||
testResolve(styleA, styleB, styleC);
|
||||
});
|
||||
|
||||
test('resolves inline-style pointerEvents to classname', () => {
|
||||
expect(styleResolver.resolve({ pointerEvents: 'box-none' })).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveWithNode', () => {
|
||||
let node;
|
||||
|
||||
beforeEach(() => {
|
||||
node = document.createElement('div');
|
||||
});
|
||||
|
||||
test('preserves unrelated class names', () => {
|
||||
node.classList.add('unknown-class-1', 'unknown-class-2');
|
||||
const resolved = styleResolver.resolveWithNode({}, node);
|
||||
expect(resolved).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('preserves unrelated inline styles', () => {
|
||||
node.style.cssText = 'font-size: 20px;';
|
||||
const resolved = styleResolver.resolveWithNode({ opacity: 1 }, node);
|
||||
expect(resolved).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('next class names have priority over current inline styles', () => {
|
||||
node.style.cssText = 'opacity: 0.5;';
|
||||
const nextStyle = ReactNativePropRegistry.register({ opacity: 1 });
|
||||
const resolved = styleResolver.resolveWithNode(nextStyle, node);
|
||||
expect(resolved).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('next inline styles have priority over current inline styles', () => {
|
||||
// note: this also checks for correctly uppercasing the first letter of DOM vendor prefixes
|
||||
node.style.cssText = 'opacity: 0.5; transform: scale(1);';
|
||||
const style = { opacity: 1, transform: [{ scale: 2 }] };
|
||||
const resolved = styleResolver.resolveWithNode(style, node);
|
||||
expect(resolved).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when RTL=true, resolves to flipped inline styles', () => {
|
||||
// note: DOM state resolved from { marginLeft: 5, left: 5 } in RTL mode
|
||||
node.style.cssText = 'margin-right: 5px; right: 5px;';
|
||||
I18nManager.forceRTL(true);
|
||||
const resolved = styleResolver.resolveWithNode({ marginLeft: 10, right: 10 }, node);
|
||||
I18nManager.forceRTL(false);
|
||||
expect(resolved).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when RTL=true, resolves to flipped classNames', () => {
|
||||
// note: DOM state resolved from { marginLeft: 5, left: 5 } in RTL mode
|
||||
node.style.cssText = 'margin-right: 5px; right: 5px;';
|
||||
const nextStyle = ReactNativePropRegistry.register({ marginLeft: 10, right: 1 });
|
||||
|
||||
I18nManager.forceRTL(true);
|
||||
const resolved = styleResolver.resolveWithNode(nextStyle, node);
|
||||
I18nManager.forceRTL(false);
|
||||
expect(resolved).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,19 +4,19 @@ import StyleSheetManager from '../StyleSheetManager';
|
||||
|
||||
let styleSheetManager;
|
||||
|
||||
describe('apis/StyleSheet/StyleSheetManager', () => {
|
||||
describe('StyleSheet/StyleSheetManager', () => {
|
||||
beforeEach(() => {
|
||||
styleSheetManager = new StyleSheetManager();
|
||||
});
|
||||
|
||||
test('getClassName', () => {
|
||||
expect(styleSheetManager.getClassName('pointerEvents', 'box-only')).toMatchSnapshot();
|
||||
const className = styleSheetManager.setDeclaration('width', '100px');
|
||||
const className = styleSheetManager.injectDeclaration('width', '100px');
|
||||
expect(styleSheetManager.getClassName('width', '100px')).toEqual(className);
|
||||
});
|
||||
|
||||
test('getDeclaration', () => {
|
||||
const className = styleSheetManager.setDeclaration('width', '100px');
|
||||
const className = styleSheetManager.injectDeclaration('width', '100px');
|
||||
expect(styleSheetManager.getDeclaration(className)).toEqual({
|
||||
prop: 'width',
|
||||
value: '100px'
|
||||
@@ -24,15 +24,15 @@ describe('apis/StyleSheet/StyleSheetManager', () => {
|
||||
});
|
||||
|
||||
test('getStyleSheets', () => {
|
||||
styleSheetManager.setDeclaration('--test-property', 'test-value');
|
||||
styleSheetManager.injectDeclaration('--test-property', 'test-value');
|
||||
expect(styleSheetManager.getStyleSheets()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('setDeclaration', () => {
|
||||
styleSheetManager._webStyleSheet.insertRuleOnce = (rule, position) => {
|
||||
test('injectDeclaration', () => {
|
||||
styleSheetManager._sheet.insertRuleOnce = (rule, position) => {
|
||||
// check for regressions in CSS write path (e.g., 0 => 0px)
|
||||
expect(rule.indexOf('-webkit-flex-shrink:0;')).not.toEqual(-1);
|
||||
};
|
||||
styleSheetManager.setDeclaration('flexShrink', 0);
|
||||
styleSheetManager.injectDeclaration('flexShrink', 0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import I18nManager from '../../I18nManager';
|
||||
import StyleSheetRegistry from '../StyleSheetRegistry';
|
||||
|
||||
let styleSheetRegistry;
|
||||
|
||||
describe('apis/StyleSheet/StyleSheetRegistry', () => {
|
||||
beforeEach(() => {
|
||||
styleSheetRegistry = new StyleSheetRegistry();
|
||||
});
|
||||
|
||||
test('register', () => {
|
||||
const style = { opacity: 0 };
|
||||
const id = styleSheetRegistry.register(style);
|
||||
expect(typeof id === 'number').toBe(true);
|
||||
});
|
||||
|
||||
describe('resolve', () => {
|
||||
const styleA = { borderWidth: 0, borderColor: 'red', width: 100 };
|
||||
const styleB = {
|
||||
position: 'absolute',
|
||||
left: 50,
|
||||
opacity: 0.5
|
||||
};
|
||||
const styleC = { width: 200 };
|
||||
|
||||
const testResolve = (a, b, c) => {
|
||||
// no common properties, different resolving order, same result
|
||||
const resolve1 = styleSheetRegistry.resolve([a, b]);
|
||||
expect(resolve1).toMatchSnapshot();
|
||||
const resolve2 = styleSheetRegistry.resolve([b, a]);
|
||||
expect(resolve1).toEqual(resolve2);
|
||||
|
||||
// common properties, different resolving order, different result
|
||||
const resolve3 = styleSheetRegistry.resolve([a, b, c]);
|
||||
expect(resolve3).toMatchSnapshot();
|
||||
const resolve4 = styleSheetRegistry.resolve([c, a, b]);
|
||||
expect(resolve4).toMatchSnapshot();
|
||||
expect(resolve3).not.toEqual(resolve4);
|
||||
};
|
||||
|
||||
test('with register, resolves to className', () => {
|
||||
const a = styleSheetRegistry.register(styleA);
|
||||
const b = styleSheetRegistry.register(styleB);
|
||||
const c = styleSheetRegistry.register(styleC);
|
||||
testResolve(a, b, c);
|
||||
});
|
||||
|
||||
test('with register before RTL, resolves to className', () => {
|
||||
const a = styleSheetRegistry.register({ left: '12.34%' });
|
||||
const b = styleSheetRegistry.register({ textAlign: 'left' });
|
||||
const c = styleSheetRegistry.register({ marginLeft: 10 });
|
||||
I18nManager.forceRTL(true);
|
||||
const resolved = styleSheetRegistry.resolve([a, b, c]);
|
||||
I18nManager.forceRTL(false);
|
||||
expect(resolved).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('with register, resolves to mixed', () => {
|
||||
const a = styleA;
|
||||
const b = styleSheetRegistry.register(styleB);
|
||||
const c = styleSheetRegistry.register(styleC);
|
||||
testResolve(a, b, c);
|
||||
});
|
||||
|
||||
test('without register, resolves to inline styles', () => {
|
||||
testResolve(styleA, styleB, styleC);
|
||||
});
|
||||
|
||||
test('resolves inline-style pointerEvents to classname', () => {
|
||||
expect(styleSheetRegistry.resolve({ pointerEvents: 'box-none' })).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveStateful', () => {
|
||||
test('preserves unrelated class names', () => {
|
||||
const domStyleProps = { classList: ['unknown-class-1', 'unknown-class-2'], style: {} };
|
||||
const domStyleNextProps = styleSheetRegistry.resolveStateful({}, domStyleProps);
|
||||
expect(domStyleNextProps).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('preserves unrelated inline styles', () => {
|
||||
const domStyleProps = { classList: [], style: { fontSize: '20px' } };
|
||||
const domStyleNextProps = styleSheetRegistry.resolveStateful({ opacity: 1 }, domStyleProps);
|
||||
expect(domStyleNextProps).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('next class names have priority over current inline styles', () => {
|
||||
const domStyleProps = { classList: [], style: { opacity: 0.5 } };
|
||||
const nextStyle = styleSheetRegistry.register({ opacity: 1 });
|
||||
const domStyleNextProps = styleSheetRegistry.resolveStateful(nextStyle, domStyleProps);
|
||||
expect(domStyleNextProps).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('next inline styles have priority over current inline styles', () => {
|
||||
// note: this also checks for correctly uppercasing the first letter of DOM vendor prefixes
|
||||
const domStyleProps = {
|
||||
classList: [],
|
||||
style: { opacity: 0.5, WebkitTransform: 'scale(1)', transform: 'scale(1)' }
|
||||
};
|
||||
const domStyleNextProps = styleSheetRegistry.resolveStateful(
|
||||
{ opacity: 1, transform: [{ scale: 2 }] },
|
||||
domStyleProps
|
||||
);
|
||||
expect(domStyleNextProps).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolve resolves inline-style pointerEvents to classname 1`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve resolves inline-style pointerEvents to classname 1`] = `
|
||||
Object {
|
||||
"classList": Array [
|
||||
"rn-pointerEvents-12vffkv",
|
||||
@@ -9,7 +9,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolve with register before RTL, resolves to className 1`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve with register before RTL, resolves to className 1`] = `
|
||||
Object {
|
||||
"classList": Array [
|
||||
"rn-marginRight-zso239",
|
||||
@@ -20,7 +20,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolve with register, resolves to className 1`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 1`] = `
|
||||
Object {
|
||||
"classList": Array [
|
||||
"rn-borderTopColor-1gxhl28",
|
||||
@@ -40,7 +40,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolve with register, resolves to className 2`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 2`] = `
|
||||
Object {
|
||||
"classList": Array [
|
||||
"rn-borderTopColor-1gxhl28",
|
||||
@@ -60,7 +60,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolve with register, resolves to className 3`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 3`] = `
|
||||
Object {
|
||||
"classList": Array [
|
||||
"rn-borderTopColor-1gxhl28",
|
||||
@@ -80,7 +80,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolve with register, resolves to mixed 1`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to mixed 1`] = `
|
||||
Object {
|
||||
"classList": Array [
|
||||
"rn-left-1tsx3h3",
|
||||
@@ -102,7 +102,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolve with register, resolves to mixed 2`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to mixed 2`] = `
|
||||
Object {
|
||||
"classList": Array [
|
||||
"rn-left-1tsx3h3",
|
||||
@@ -124,7 +124,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolve with register, resolves to mixed 3`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to mixed 3`] = `
|
||||
Object {
|
||||
"classList": Array [
|
||||
"rn-left-1tsx3h3",
|
||||
@@ -146,7 +146,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolve without register, resolves to inline styles 1`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve without register, resolves to inline styles 1`] = `
|
||||
Object {
|
||||
"classList": Array [],
|
||||
"className": "",
|
||||
@@ -167,7 +167,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolve without register, resolves to inline styles 2`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve without register, resolves to inline styles 2`] = `
|
||||
Object {
|
||||
"classList": Array [],
|
||||
"className": "",
|
||||
@@ -188,7 +188,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolve without register, resolves to inline styles 3`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve without register, resolves to inline styles 3`] = `
|
||||
Object {
|
||||
"classList": Array [],
|
||||
"className": "",
|
||||
@@ -209,7 +209,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolveStateful next class names have priority over current inline styles 1`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode next class names have priority over current inline styles 1`] = `
|
||||
Object {
|
||||
"className": "rn-opacity-6dt33c",
|
||||
"style": Object {
|
||||
@@ -218,7 +218,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolveStateful next inline styles have priority over current inline styles 1`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode next inline styles have priority over current inline styles 1`] = `
|
||||
Object {
|
||||
"className": "",
|
||||
"style": Object {
|
||||
@@ -229,14 +229,14 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolveStateful preserves unrelated class names 1`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode preserves unrelated class names 1`] = `
|
||||
Object {
|
||||
"className": "unknown-class-1 unknown-class-2",
|
||||
"style": Object {},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetRegistry resolveStateful preserves unrelated inline styles 1`] = `
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode preserves unrelated inline styles 1`] = `
|
||||
Object {
|
||||
"className": "",
|
||||
"style": Object {
|
||||
@@ -245,3 +245,24 @@ Object {
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode when RTL=true, resolves to flipped classNames 1`] = `
|
||||
Object {
|
||||
"className": "rn-left-1u10d71 rn-marginRight-zso239",
|
||||
"style": Object {
|
||||
"marginRight": "",
|
||||
"right": "5px",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode when RTL=true, resolves to flipped inline styles 1`] = `
|
||||
Object {
|
||||
"className": "",
|
||||
"style": Object {
|
||||
"left": "10px",
|
||||
"marginRight": "10px",
|
||||
"right": "5px",
|
||||
},
|
||||
}
|
||||
`;
|
||||
@@ -1,15 +1,17 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetManager getClassName 1`] = `undefined`;
|
||||
exports[`StyleSheet/StyleSheetManager getClassName 1`] = `undefined`;
|
||||
|
||||
exports[`apis/StyleSheet/StyleSheetManager getStyleSheets 1`] = `
|
||||
exports[`StyleSheet/StyleSheetManager getStyleSheets 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"id": "react-native-stylesheet",
|
||||
"textContent": "html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
"textContent": "@media all{
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
body{margin:0;}
|
||||
@media all{button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}}
|
||||
@media all{input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}}
|
||||
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
|
||||
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
|
||||
}
|
||||
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
|
||||
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
|
||||
.rn---test-property-ax3bxi{--test-property:test-value}",
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`apis/StyleSheet/createReactDOMStyle fontFamily fontFamily: "System" 1`] = `
|
||||
exports[`StyleSheet/createReactDOMStyle fontFamily fontFamily: "System" 1`] = `
|
||||
Object {
|
||||
"fontFamily": "-apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/createReactDOMStyle fontFamily fontFamily: "monospace" 1`] = `
|
||||
exports[`StyleSheet/createReactDOMStyle fontFamily fontFamily: "monospace" 1`] = `
|
||||
Object {
|
||||
"fontFamily": "monospace, monospace",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/createReactDOMStyle fontFamily general case 1`] = `
|
||||
exports[`StyleSheet/createReactDOMStyle fontFamily general case 1`] = `
|
||||
Object {
|
||||
"fontFamily": "Georgia, Times, serif",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/createReactDOMStyle shortform -> longform 1`] = `
|
||||
exports[`StyleSheet/createReactDOMStyle shortform -> longform 1`] = `
|
||||
Object {
|
||||
"borderBottomColor": "white",
|
||||
"borderBottomStyle": "solid",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`StyleSheet/createRuleBlock generates correct css 1`] = `"-webkit-transition-duration:0.1s;border-width-left:2px;border-width-right:3px;box-shadow:1px 1px 1px 1px #000;position:absolute;transition-duration:0.1s"`;
|
||||
@@ -1,26 +1,26 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`apis/StyleSheet/flattenStyle should merge style objects 1`] = `
|
||||
exports[`StyleSheet/flattenStyle should merge style objects 1`] = `
|
||||
Object {
|
||||
"opacity": 1,
|
||||
"order": 2,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/flattenStyle should override style properties 1`] = `
|
||||
exports[`StyleSheet/flattenStyle should override style properties 1`] = `
|
||||
Object {
|
||||
"backgroundColor": "#023c69",
|
||||
"order": null,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/flattenStyle should overwrite properties with \`undefined\` 1`] = `
|
||||
exports[`StyleSheet/flattenStyle should overwrite properties with \`undefined\` 1`] = `
|
||||
Object {
|
||||
"backgroundColor": undefined,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/flattenStyle should recursively flatten arrays 1`] = `
|
||||
exports[`StyleSheet/flattenStyle should recursively flatten arrays 1`] = `
|
||||
Object {
|
||||
"opacity": 1,
|
||||
"order": 3,
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`apis/StyleSheet/generateCss generates correct css 1`] = `"-webkit-transition-duration:0.1s;border-width-left:2px;border-width-right:3px;box-shadow:1px 1px 1px 1px #000;position:absolute;transition-duration:0.1s"`;
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`apis/StyleSheet/i18nStyle LTR mode does not auto-flip 1`] = `
|
||||
exports[`StyleSheet/i18nStyle LTR mode does not auto-flip 1`] = `
|
||||
Object {
|
||||
"borderBottomLeftRadius": 20,
|
||||
"borderBottomRightRadius": "2rem",
|
||||
@@ -26,7 +26,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/i18nStyle RTL mode does auto-flip 1`] = `
|
||||
exports[`StyleSheet/i18nStyle RTL mode does auto-flip 1`] = `
|
||||
Object {
|
||||
"borderBottomLeftRadius": "2rem",
|
||||
"borderBottomRightRadius": 20,
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`apis/StyleSheet getStyleSheets 1`] = `
|
||||
exports[`StyleSheet getStyleSheets 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"id": "react-native-stylesheet",
|
||||
"textContent": "html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
"textContent": "@media all{
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
body{margin:0;}
|
||||
@media all{button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}}
|
||||
@media all{input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}}
|
||||
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
|
||||
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
|
||||
}
|
||||
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
|
||||
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
|
||||
.rn-bottom-1p0dtai{bottom:0px}
|
||||
.rn-left-1d2f490{left:0px}
|
||||
.rn-position-u8s1d{position:absolute}
|
||||
.rn-right-zchlnj{right:0px}
|
||||
.rn-top-ipm5af{top:0px}
|
||||
.rn-cursor-1loqt21{cursor:pointer}
|
||||
.rn-appearance-30o5oe{-moz-appearance:none;-webkit-appearance:none;appearance:none}
|
||||
.rn-backgroundColor-wib322{background-color:transparent}
|
||||
@@ -56,6 +53,11 @@ body{margin:0;}
|
||||
.rn-position-bnwqim{position:relative}
|
||||
.rn-zIndex-1lgpqti{z-index:0}
|
||||
.rn-display-xoduu5{display:-webkit-inline-box;display:-moz-inline-box;display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex}
|
||||
.rn-bottom-1p0dtai{bottom:0px}
|
||||
.rn-left-1d2f490{left:0px}
|
||||
.rn-position-u8s1d{position:absolute}
|
||||
.rn-right-zchlnj{right:0px}
|
||||
.rn-top-ipm5af{top:0px}
|
||||
.rn-zIndex-1wyyakw{z-index:-1}
|
||||
.rn-backgroundPosition-vvn4in{background-position:center}
|
||||
.rn-backgroundRepeat-u6sd8q{background-repeat:no-repeat}
|
||||
|
||||
@@ -15,7 +15,7 @@ const reactNativeStyle = {
|
||||
resizeMode: 'contain'
|
||||
};
|
||||
|
||||
describe('apis/StyleSheet/createReactDOMStyle', () => {
|
||||
describe('StyleSheet/createReactDOMStyle', () => {
|
||||
test('noop on DOM styles', () => {
|
||||
const firstStyle = createReactDOMStyle(reactNativeStyle);
|
||||
const secondStyle = createReactDOMStyle(firstStyle);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import generateCss from '../generateCss';
|
||||
import createRuleBlock from '../createRuleBlock';
|
||||
|
||||
describe('apis/StyleSheet/generateCss', () => {
|
||||
describe('StyleSheet/createRuleBlock', () => {
|
||||
test('generates correct css', () => {
|
||||
const style = {
|
||||
boxShadow: '1px 1px 1px 1px #000',
|
||||
@@ -11,6 +11,6 @@ describe('apis/StyleSheet/generateCss', () => {
|
||||
position: 'absolute',
|
||||
transitionDuration: '0.1s'
|
||||
};
|
||||
expect(generateCss(style)).toMatchSnapshot();
|
||||
expect(createRuleBlock(style)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import flattenStyle from '../flattenStyle';
|
||||
|
||||
describe('apis/StyleSheet/flattenStyle', () => {
|
||||
describe('StyleSheet/flattenStyle', () => {
|
||||
test('should merge style objects', () => {
|
||||
const style = flattenStyle([{ opacity: 1 }, { order: 2 }]);
|
||||
expect(style).toMatchSnapshot();
|
||||
|
||||
@@ -24,7 +24,7 @@ const style = {
|
||||
textShadowOffset: { width: '1rem', height: 10 }
|
||||
};
|
||||
|
||||
describe('apis/StyleSheet/i18nStyle', () => {
|
||||
describe('StyleSheet/i18nStyle', () => {
|
||||
describe('LTR mode', () => {
|
||||
beforeEach(() => {
|
||||
I18nManager.allowRTL(false);
|
||||
|
||||
@@ -13,7 +13,7 @@ const isPlainObject = x => {
|
||||
/* eslint-enable */
|
||||
};
|
||||
|
||||
describe('apis/StyleSheet', () => {
|
||||
describe('StyleSheet', () => {
|
||||
test('absoluteFill', () => {
|
||||
expect(Number.isInteger(StyleSheet.absoluteFill) === true).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import normalizeValue from '../normalizeValue';
|
||||
|
||||
describe('apis/StyleSheet/normalizeValue', () => {
|
||||
describe('StyleSheet/normalizeValue', () => {
|
||||
test('normalizes property values requiring units', () => {
|
||||
expect(normalizeValue('margin', 0)).toEqual('0px');
|
||||
});
|
||||
|
||||
31
packages/react-native-web/src/exports/StyleSheet/createAtomicRules.js
vendored
Normal file
31
packages/react-native-web/src/exports/StyleSheet/createAtomicRules.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import createRuleBlock from './createRuleBlock';
|
||||
|
||||
const createAtomicRules = (selector, prop, value) => {
|
||||
const rules = [];
|
||||
let val = value;
|
||||
|
||||
// pointerEvents is a special case that requires custom values and additional rules
|
||||
// See #513
|
||||
if (prop === 'pointerEvents') {
|
||||
if (value === 'auto' || value === 'box-only') {
|
||||
val = 'auto !important';
|
||||
if (value === 'box-only') {
|
||||
const block = createRuleBlock({ [prop]: 'none' });
|
||||
rules.push(`${selector} > *{${block}}`);
|
||||
}
|
||||
} else if (value === 'none' || value === 'box-none') {
|
||||
val = 'none !important';
|
||||
if (value === 'box-none') {
|
||||
const block = createRuleBlock({ [prop]: 'auto' });
|
||||
rules.push(`${selector} > *{${block}}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const block = createRuleBlock({ [prop]: val });
|
||||
rules.push(`${selector}{${block}}`);
|
||||
|
||||
return rules;
|
||||
};
|
||||
|
||||
export default createAtomicRules;
|
||||
@@ -12,7 +12,8 @@
|
||||
import flattenStyle from './flattenStyle';
|
||||
import getHairlineWidth from './getHairlineWidth';
|
||||
import modality from '../../modules/modality';
|
||||
import StyleSheetRegistry from './registry';
|
||||
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
|
||||
import styleResolver from './styleResolver';
|
||||
|
||||
// initialize focus-ring fix
|
||||
modality();
|
||||
@@ -32,7 +33,7 @@ const absoluteFillObject = {
|
||||
top: 0,
|
||||
bottom: 0
|
||||
};
|
||||
const absoluteFill = StyleSheetRegistry.register(absoluteFillObject);
|
||||
const absoluteFill = ReactNativePropRegistry.register(absoluteFillObject);
|
||||
|
||||
const StyleSheet = {
|
||||
absoluteFill,
|
||||
@@ -51,13 +52,16 @@ const StyleSheet = {
|
||||
const StyleSheetValidation = require('./StyleSheetValidation').default;
|
||||
StyleSheetValidation.validateStyle(key, styles);
|
||||
}
|
||||
result[key] = StyleSheetRegistry.register(styles[key]);
|
||||
const id = styles[key] && ReactNativePropRegistry.register(styles[key]);
|
||||
result[key] = id;
|
||||
// TODO: remove
|
||||
styleResolver._injectRegisteredStyle(id);
|
||||
});
|
||||
return result;
|
||||
},
|
||||
flatten: flattenStyle,
|
||||
getStyleSheets() {
|
||||
return StyleSheetRegistry.getStyleSheets();
|
||||
return styleResolver.getStyleSheets();
|
||||
},
|
||||
hairlineWidth: getHairlineWidth()
|
||||
};
|
||||
|
||||
@@ -8,20 +8,24 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
// "media all" wrapper stops browsers throwing errors on each others vendor-prefixed pseudo-elements in selectors
|
||||
const safeRule = rule => `@media all{${rule}}`;
|
||||
// Prevent browsers throwing parse errors, e.g., on vendor-prefixed pseudo-elements
|
||||
const safeRule = rule => `@media all{\n${rule}\n}`;
|
||||
|
||||
const initialRules = [
|
||||
const resets = [
|
||||
// minimal top-level reset
|
||||
'html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}',
|
||||
'body{margin:0;}',
|
||||
// minimal form pseudo-element reset
|
||||
safeRule('button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}'),
|
||||
safeRule(
|
||||
'input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,' +
|
||||
'input::-webkit-search-cancel-button,input::-webkit-search-decoration,' +
|
||||
'input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}'
|
||||
),
|
||||
'button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}',
|
||||
'input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,' +
|
||||
'input::-webkit-search-cancel-button,input::-webkit-search-decoration,' +
|
||||
'input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}'
|
||||
];
|
||||
|
||||
const reset = safeRule(resets.join('\n'));
|
||||
|
||||
const initialRules = [
|
||||
reset,
|
||||
// temporary keyframes
|
||||
'@keyframes rn-ActivityIndicator-animation{' +
|
||||
'0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}' +
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import StyleSheetRegistry from './StyleSheetRegistry';
|
||||
const registry = new StyleSheetRegistry();
|
||||
export default registry;
|
||||
import ReactNativeStyleResolver from './ReactNativeStyleResolver';
|
||||
const styleResolver = new ReactNativeStyleResolver();
|
||||
export default styleResolver;
|
||||
@@ -12,13 +12,9 @@
|
||||
|
||||
import createDOMProps from '../createDOMProps';
|
||||
import findNodeHandle from '../../exports/findNodeHandle';
|
||||
import i18nStyle from '../../exports/StyleSheet/i18nStyle';
|
||||
import styleSheetRegistry from '../../exports/StyleSheet/registry';
|
||||
import styleResolver from '../../exports/StyleSheet/styleResolver';
|
||||
import UIManager from '../../exports/UIManager';
|
||||
|
||||
const hyphenPattern = /-([a-z])/g;
|
||||
const toCamelCase = str => str.replace(hyphenPattern, m => m[1].toUpperCase());
|
||||
|
||||
type MeasureOnSuccessCallback = (
|
||||
x: number,
|
||||
y: number,
|
||||
@@ -105,31 +101,11 @@ const NativeMethodsMixin = {
|
||||
if (!nativeProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy of existing DOM state
|
||||
const node = findNodeHandle(this);
|
||||
const nodeStyle = node.style;
|
||||
const classList = Array.prototype.slice.call(node.classList);
|
||||
const style = {};
|
||||
// DOM style is a CSSStyleDeclaration
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
|
||||
for (let i = 0; i < node.style.length; i += 1) {
|
||||
const property = nodeStyle.item(i);
|
||||
if (property) {
|
||||
// DOM style uses hyphenated prop names and may include vendor prefixes
|
||||
// Transform back into React DOM style.
|
||||
style[toCamelCase(property)] = nodeStyle.getPropertyValue(property);
|
||||
}
|
||||
}
|
||||
|
||||
const domStyleProps = { classList, style };
|
||||
const props = {
|
||||
...nativeProps,
|
||||
style: i18nStyle(nativeProps.style)
|
||||
};
|
||||
// Next DOM state
|
||||
const domProps = createDOMProps(null, props, style =>
|
||||
styleSheetRegistry.resolveStateful(style, domStyleProps, { i18n: false })
|
||||
// Next state is determined by comparison to existing state (in the DOM).
|
||||
// Existing state has already gone through i18n transform
|
||||
const domProps = createDOMProps(null, nativeProps, style =>
|
||||
styleResolver.resolveWithNode(style, node)
|
||||
);
|
||||
UIManager.updateView(node, domProps, this);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
import AccessibilityUtil from '../AccessibilityUtil';
|
||||
import StyleSheet from '../../exports/StyleSheet';
|
||||
import StyleSheetRegistry from '../../exports/StyleSheet/registry';
|
||||
import styleResolver from '../../exports/StyleSheet/styleResolver';
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
@@ -53,7 +53,7 @@ const pointerEventsStyles = StyleSheet.create({
|
||||
}
|
||||
});
|
||||
|
||||
const defaultStyleResolver = style => StyleSheetRegistry.resolve(style);
|
||||
const defaultStyleResolver = style => styleResolver.resolve(style);
|
||||
|
||||
const createDOMProps = (component, props, styleResolver) => {
|
||||
if (!styleResolver) {
|
||||
|
||||
Reference in New Issue
Block a user