mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-01-12 22:51:09 +08:00
[change] StyleSheet: compile styles directly to CSS
Introduces a centralized compiler for "atomic" and "classic" CSS output. The
"classic" compiler is for internal use only and offers no CSS safety
guarantees. The "atomic compiler is used to implement the public-facing
StyleSheet API.
The atomic compiler now maps the React style declarations, rather than CSS
style declarations, to CSS rules. This avoids having to convert React styles to
CSS styles before being able to lookup classNames. And it reduces the number of
CSS rules needed by each DOM element.
Before:
{ paddingHorizontal: 0; }
↓
.paddingLeft-0 { padding-left: 0; }
.paddingRight-0 { padding-right: 0; }
After:
{ paddingHorizontal: 0; }
↓
.paddingHorizontal-0 { padding-left: 0; padding-right: 0 }
Overview of previous StyleSheet resolver:
1. Localise styles
2. Transform to CSS styles
3. Expand short-form properties
4a. Lookup Atomic CSS for each declaration
4b. Compile Atomic CSS for each static declaration
i. Vendor prefix
ii. Insert CSS rules
4c. Create inline style for each dynamic-only declaration
i. Vendor prefix
Overview of new StyleSheet design:
1. Localise styles
2a. Lookup Atomic CSS for each declaration
2b. Compile Atomic CSS for each static declarations
i. Transform to CSS styles
ii. Expand short-form properties
iii. Vendor prefix
iiii. Insert CSS rules
2c. Create inline style for each dynamic-only declaration
i. Transform to CSS styles
ii. Expand short-form properties
iii. Vendor prefix
Ref #1136
This commit is contained in:
1
.watchmanconfig
Normal file
1
.watchmanconfig
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -17,7 +17,7 @@ exports[`components/ActivityIndicator prop "animating" is "false" 1`] = `
|
||||
Object {
|
||||
"animationDuration": "0.75s",
|
||||
"animationIterationCount": "infinite",
|
||||
"animationName": Array [
|
||||
"animationKeyframes": Array [
|
||||
Object {
|
||||
"0%": Object {
|
||||
"transform": Array [
|
||||
@@ -97,7 +97,7 @@ exports[`components/ActivityIndicator prop "animating" is "true" 1`] = `
|
||||
Object {
|
||||
"animationDuration": "0.75s",
|
||||
"animationIterationCount": "infinite",
|
||||
"animationName": Array [
|
||||
"animationKeyframes": Array [
|
||||
Object {
|
||||
"0%": Object {
|
||||
"transform": Array [
|
||||
@@ -211,7 +211,7 @@ exports[`components/ActivityIndicator prop "hidesWhenStopped" is "false" 1`] = `
|
||||
Object {
|
||||
"animationDuration": "0.75s",
|
||||
"animationIterationCount": "infinite",
|
||||
"animationName": Array [
|
||||
"animationKeyframes": Array [
|
||||
Object {
|
||||
"0%": Object {
|
||||
"transform": Array [
|
||||
@@ -290,7 +290,7 @@ exports[`components/ActivityIndicator prop "hidesWhenStopped" is "true" 1`] = `
|
||||
Object {
|
||||
"animationDuration": "0.75s",
|
||||
"animationIterationCount": "infinite",
|
||||
"animationName": Array [
|
||||
"animationKeyframes": Array [
|
||||
Object {
|
||||
"0%": Object {
|
||||
"transform": Array [
|
||||
@@ -370,7 +370,7 @@ exports[`components/ActivityIndicator prop "size" is "large" 1`] = `
|
||||
Object {
|
||||
"animationDuration": "0.75s",
|
||||
"animationIterationCount": "infinite",
|
||||
"animationName": Array [
|
||||
"animationKeyframes": Array [
|
||||
Object {
|
||||
"0%": Object {
|
||||
"transform": Array [
|
||||
@@ -448,7 +448,7 @@ exports[`components/ActivityIndicator prop "size" is a number 1`] = `
|
||||
Object {
|
||||
"animationDuration": "0.75s",
|
||||
"animationIterationCount": "infinite",
|
||||
"animationName": Array [
|
||||
"animationKeyframes": Array [
|
||||
Object {
|
||||
"0%": Object {
|
||||
"transform": Array [
|
||||
|
||||
@@ -86,7 +86,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
animation: {
|
||||
animationDuration: '0.75s',
|
||||
animationName: [
|
||||
animationKeyframes: [
|
||||
{
|
||||
'0%': { transform: [{ rotate: '0deg' }] },
|
||||
'100%': { transform: [{ rotate: '360deg' }] }
|
||||
|
||||
@@ -11,10 +11,13 @@ exports[`AppRegistry getApplication returns "element" and "getStyleElement" 1`]
|
||||
exports[`AppRegistry getApplication returns "element" and "getStyleElement" 2`] = `
|
||||
"<style id=\\"react-native-stylesheet\\">@media all {
|
||||
[stylesheet-group=\\"0\\"]{}
|
||||
:focus:not([data-rn-focusvisible-x92cna]){outline: none;}
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
body{margin:0;}
|
||||
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;}
|
||||
}
|
||||
@media all {
|
||||
[stylesheet-group=\\"0.1\\"]{}
|
||||
:focus:not([data-focusvisible-polyfill]){outline: none;}
|
||||
}</style>"
|
||||
`;
|
||||
|
||||
@@ -10,7 +10,7 @@ exports[`components/Picker prop "children" items 1`] = `
|
||||
|
||||
exports[`components/Picker prop "children" renders items 1`] = `
|
||||
<select
|
||||
className="rn-fontFamily-14xgk7a rn-fontSize-7cikom rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw"
|
||||
className="rn-fontFamily-1qd0xha rn-fontSize-7cikom rn-margin-crgep1"
|
||||
data-focusable={true}
|
||||
onChange={[Function]}
|
||||
>
|
||||
|
||||
@@ -94,7 +94,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
animation: {
|
||||
animationDuration: '1s',
|
||||
animationName: [
|
||||
animationKeyframes: [
|
||||
{
|
||||
'0%': { transform: [{ translateX: '-100%' }] },
|
||||
'100%': { transform: [{ translateX: '400%' }] }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Nicolas Gallagher.
|
||||
* Copyright (c) Nicolas Gallagher.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -13,13 +13,17 @@
|
||||
*/
|
||||
|
||||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
import createReactDOMStyle from './createReactDOMStyle';
|
||||
import createCSSStyleSheet from './createCSSStyleSheet';
|
||||
import createCompileableStyle from './createCompileableStyle';
|
||||
import createOrderedCSSStyleSheet from './createOrderedCSSStyleSheet';
|
||||
import flattenArray from '../../modules/flattenArray';
|
||||
import flattenStyle from './flattenStyle';
|
||||
import I18nManager from '../I18nManager';
|
||||
import i18nStyle from './i18nStyle';
|
||||
import { prefixInlineStyles } from '../../modules/prefixStyles';
|
||||
import StyleSheetManager from './StyleSheetManager';
|
||||
import { atomic, inline, stringifyValueWithProperty } from './compile';
|
||||
import initialRules from './initialRules';
|
||||
import modality from './modality';
|
||||
import { STYLE_ELEMENT_ID, STYLE_GROUPS } from './constants';
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
@@ -27,33 +31,65 @@ export default class ReactNativeStyleResolver {
|
||||
_init() {
|
||||
this.cache = { ltr: {}, rtl: {}, rtlNoSwap: {} };
|
||||
this.injectedCache = { ltr: {}, rtl: {}, rtlNoSwap: {} };
|
||||
this.styleSheetManager = new StyleSheetManager();
|
||||
this.sheet = createOrderedCSSStyleSheet(createCSSStyleSheet(STYLE_ELEMENT_ID));
|
||||
this._lookupCache = {
|
||||
byClassName: {},
|
||||
byProp: {}
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this._init();
|
||||
modality(rule => this.sheet.insert(rule, STYLE_GROUPS.modality));
|
||||
initialRules.forEach(rule => {
|
||||
this.sheet.insert(rule, STYLE_GROUPS.reset);
|
||||
});
|
||||
}
|
||||
|
||||
_addToCache(className, prop, value) {
|
||||
const cache = this._lookupCache;
|
||||
if (!cache.byProp[prop]) {
|
||||
cache.byProp[prop] = {};
|
||||
}
|
||||
cache.byProp[prop][value] = className;
|
||||
cache.byClassName[className] = { prop, value };
|
||||
}
|
||||
|
||||
getClassName(prop, value) {
|
||||
const val = stringifyValueWithProperty(value, prop);
|
||||
const cache = this._lookupCache.byProp;
|
||||
return cache[prop] && cache[prop].hasOwnProperty(val) && cache[prop][val];
|
||||
}
|
||||
|
||||
getDeclaration(className) {
|
||||
const cache = this._lookupCache.byClassName;
|
||||
return cache[className] || emptyObject;
|
||||
}
|
||||
|
||||
getStyleSheet() {
|
||||
// reset state on the server so critical css is always the result
|
||||
const sheet = this.styleSheetManager.getStyleSheet();
|
||||
const textContent = this.sheet.getTextContent();
|
||||
if (!canUseDOM) {
|
||||
this._init();
|
||||
}
|
||||
return sheet;
|
||||
return {
|
||||
id: STYLE_ELEMENT_ID,
|
||||
textContent
|
||||
};
|
||||
}
|
||||
|
||||
_injectRegisteredStyle(id) {
|
||||
const { doLeftAndRightSwapInRTL, isRTL } = I18nManager;
|
||||
const dir = isRTL ? (doLeftAndRightSwapInRTL ? 'rtl' : 'rtlNoSwap') : 'ltr';
|
||||
if (!this.injectedCache[dir][id]) {
|
||||
const style = flattenStyle(id);
|
||||
const domStyle = createReactDOMStyle(i18nStyle(style));
|
||||
Object.keys(domStyle).forEach(styleProp => {
|
||||
const value = domStyle[styleProp];
|
||||
if (value != null) {
|
||||
this.styleSheetManager.injectDeclaration(styleProp, value);
|
||||
}
|
||||
const style = createCompileableStyle(i18nStyle(flattenStyle(id)));
|
||||
const results = atomic(style);
|
||||
Object.values(results).forEach(({ identifier, property, rules, value }) => {
|
||||
this._addToCache(identifier, property, value);
|
||||
rules.forEach(rule => {
|
||||
const group = STYLE_GROUPS.custom[property] || STYLE_GROUPS.atomic;
|
||||
this.sheet.insert(rule, group);
|
||||
});
|
||||
});
|
||||
this.injectedCache[dir][id] = true;
|
||||
}
|
||||
@@ -91,7 +127,7 @@ export default class ReactNativeStyleResolver {
|
||||
isArrayOfNumbers = false;
|
||||
} else {
|
||||
if (isArrayOfNumbers) {
|
||||
cacheKey += (id + '-');
|
||||
cacheKey += id + '-';
|
||||
}
|
||||
this._injectRegisteredStyle(id);
|
||||
}
|
||||
@@ -113,7 +149,7 @@ export default class ReactNativeStyleResolver {
|
||||
// Preserves unrecognized class names.
|
||||
const { classList: rnClassList, style: rnStyle } = rdomClassList.reduce(
|
||||
(styleProps, className) => {
|
||||
const { prop, value } = this.styleSheetManager.getDeclaration(className);
|
||||
const { prop, value } = this.getDeclaration(className);
|
||||
if (prop) {
|
||||
styleProps.style[prop] = value;
|
||||
} else {
|
||||
@@ -138,7 +174,7 @@ export default class ReactNativeStyleResolver {
|
||||
// Next class names take priority over current inline styles
|
||||
const style = { ...rdomStyle };
|
||||
rdomClassListNext.forEach(className => {
|
||||
const { prop } = this.styleSheetManager.getDeclaration(className);
|
||||
const { prop } = this.getDeclaration(className);
|
||||
if (style[prop]) {
|
||||
style[prop] = '';
|
||||
}
|
||||
@@ -154,45 +190,50 @@ export default class ReactNativeStyleResolver {
|
||||
*/
|
||||
_resolveStyle(style) {
|
||||
const flatStyle = flattenStyle(style);
|
||||
const domStyle = createReactDOMStyle(i18nStyle(flatStyle));
|
||||
const localizedStyle = createCompileableStyle(i18nStyle(flatStyle));
|
||||
|
||||
const props = Object.keys(domStyle).reduce(
|
||||
(props, styleProp) => {
|
||||
const value = domStyle[styleProp];
|
||||
if (value != null) {
|
||||
const className = this.styleSheetManager.getClassName(styleProp, value);
|
||||
if (className) {
|
||||
props.classList.push(className);
|
||||
} else {
|
||||
// Certain properties and values are not transformed by 'createReactDOMStyle' as they
|
||||
// require more complex transforms into multiple CSS rules. Here we assume that StyleManager
|
||||
// can bind these styles to a className, and prevent them becoming invalid inline-styles.
|
||||
if (
|
||||
styleProp === 'pointerEvents' ||
|
||||
styleProp === 'placeholderTextColor' ||
|
||||
styleProp === 'animationName'
|
||||
) {
|
||||
const className = this.styleSheetManager.injectDeclaration(styleProp, value);
|
||||
if (className) {
|
||||
props.classList.push(className);
|
||||
}
|
||||
const props = Object.keys(localizedStyle)
|
||||
.sort()
|
||||
.reduce(
|
||||
(props, styleProp) => {
|
||||
const value = localizedStyle[styleProp];
|
||||
if (value != null) {
|
||||
const className = this.getClassName(styleProp, value);
|
||||
if (className) {
|
||||
props.classList.push(className);
|
||||
} else {
|
||||
if (!props.style) {
|
||||
props.style = {};
|
||||
// Certain properties and values are not transformed by 'createReactDOMStyle' as they
|
||||
// require more complex transforms into multiple CSS rules. Here we assume that StyleManager
|
||||
// can bind these styles to a className, and prevent them becoming invalid inline-styles.
|
||||
if (
|
||||
styleProp === 'pointerEvents' ||
|
||||
styleProp === 'placeholderTextColor' ||
|
||||
styleProp === 'animationKeyframes'
|
||||
) {
|
||||
const a = atomic({ [styleProp]: value });
|
||||
Object.values(a).forEach(({ identifier, rules }) => {
|
||||
props.classList.push(identifier);
|
||||
rules.forEach(rule => {
|
||||
this.sheet.insert(rule, STYLE_GROUPS.atomic);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (!props.style) {
|
||||
props.style = {};
|
||||
}
|
||||
// 4x slower render
|
||||
props.style[styleProp] = value;
|
||||
}
|
||||
// 4x slower render
|
||||
props.style[styleProp] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return props;
|
||||
},
|
||||
{ classList: [] }
|
||||
);
|
||||
return props;
|
||||
},
|
||||
{ classList: [] }
|
||||
);
|
||||
|
||||
props.className = classListToString(props.classList);
|
||||
if (props.style) {
|
||||
props.style = prefixInlineStyles(props.style);
|
||||
props.style = inline(props.style);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/**
|
||||
* 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 { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
import createAtomicRules from './createAtomicRules';
|
||||
import hash from '../../vendor/hash';
|
||||
import initialRules from './initialRules';
|
||||
import createOrderedCSSStyleSheet from './createOrderedCSSStyleSheet';
|
||||
import modality from './modality';
|
||||
|
||||
const emptyObject = {};
|
||||
const STYLE_ELEMENT_ID = 'react-native-stylesheet';
|
||||
|
||||
const createClassName = (prop, value) => {
|
||||
const hashed = hash(prop + normalizeValue(value));
|
||||
return process.env.NODE_ENV !== 'production' ? `rn-${prop}-${hashed}` : `rn-${hashed}`;
|
||||
};
|
||||
|
||||
const createCSSStyleSheet = () => {
|
||||
const id = STYLE_ELEMENT_ID;
|
||||
|
||||
let element;
|
||||
element = document.getElementById(id);
|
||||
if (!element) {
|
||||
element = document.createElement('style');
|
||||
element.setAttribute('id', id);
|
||||
const head = document.head;
|
||||
if (head) {
|
||||
head.insertBefore(element, head.firstChild);
|
||||
}
|
||||
}
|
||||
return element.sheet;
|
||||
};
|
||||
|
||||
const normalizeValue = value => (typeof value === 'object' ? JSON.stringify(value) : value);
|
||||
|
||||
export default class StyleSheetManager {
|
||||
_cache = {
|
||||
byClassName: {},
|
||||
byProp: {}
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this._sheet = createOrderedCSSStyleSheet(canUseDOM ? createCSSStyleSheet() : null);
|
||||
modality(rule => this._sheet.insert(rule, 0));
|
||||
initialRules.forEach(rule => {
|
||||
this._sheet.insert(rule, 0);
|
||||
});
|
||||
}
|
||||
|
||||
getClassName(prop, value) {
|
||||
const val = normalizeValue(value);
|
||||
const cache = this._cache.byProp;
|
||||
return cache[prop] && cache[prop].hasOwnProperty(val) && cache[prop][val];
|
||||
}
|
||||
|
||||
getDeclaration(className) {
|
||||
const cache = this._cache.byClassName;
|
||||
return cache[className] || emptyObject;
|
||||
}
|
||||
|
||||
getStyleSheet() {
|
||||
const textContent = this._sheet.getTextContent();
|
||||
|
||||
return {
|
||||
id: STYLE_ELEMENT_ID,
|
||||
textContent
|
||||
};
|
||||
}
|
||||
|
||||
injectDeclaration(prop, value): string {
|
||||
const val = normalizeValue(value);
|
||||
let className = this.getClassName(prop, val);
|
||||
if (!className) {
|
||||
className = createClassName(prop, val);
|
||||
this._addToCache(className, prop, val);
|
||||
const rules = createAtomicRules(`.${className}`, prop, value);
|
||||
rules.forEach(rule => {
|
||||
this._sheet.insert(rule, 1);
|
||||
});
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
||||
_addToCache(className, prop, value) {
|
||||
const cache = this._cache;
|
||||
if (!cache.byProp[prop]) {
|
||||
cache.byProp[prop] = {};
|
||||
}
|
||||
cache.byProp[prop][value] = className;
|
||||
cache.byClassName[className] = { prop, value };
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import StyleSheetManager from '../StyleSheetManager';
|
||||
|
||||
let styleSheetManager;
|
||||
|
||||
describe('StyleSheet/StyleSheetManager', () => {
|
||||
beforeEach(() => {
|
||||
styleSheetManager = new StyleSheetManager();
|
||||
});
|
||||
|
||||
test('getClassName', () => {
|
||||
expect(styleSheetManager.getClassName('pointerEvents', 'box-only')).toMatchSnapshot();
|
||||
const className = styleSheetManager.injectDeclaration('width', '100px');
|
||||
expect(styleSheetManager.getClassName('width', '100px')).toEqual(className);
|
||||
});
|
||||
|
||||
test('getDeclaration', () => {
|
||||
const className = styleSheetManager.injectDeclaration('width', '100px');
|
||||
expect(styleSheetManager.getDeclaration(className)).toEqual({
|
||||
prop: 'width',
|
||||
value: '100px'
|
||||
});
|
||||
});
|
||||
|
||||
test('getStyleSheet', () => {
|
||||
expect(styleSheetManager.getStyleSheet()).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
textContent: expect.any(String)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
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.injectDeclaration('flexShrink', 0);
|
||||
});
|
||||
});
|
||||
@@ -34,60 +34,42 @@ Object {
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 1`] = `
|
||||
Object {
|
||||
"classList": Array [
|
||||
"rn-borderTopColor-3vzq9n",
|
||||
"rn-borderRightColor-ycnuvz",
|
||||
"rn-borderBottomColor-wqks8h",
|
||||
"rn-borderLeftColor-3se2kx",
|
||||
"rn-borderTopWidth-13yce4e",
|
||||
"rn-borderRightWidth-fnigne",
|
||||
"rn-borderBottomWidth-ndvcnb",
|
||||
"rn-borderLeftWidth-gxnn5r",
|
||||
"rn-borderColor-4a18lf",
|
||||
"rn-borderWidth-1yadl64",
|
||||
"rn-left-1tsx3h3",
|
||||
"rn-opacity-icoktb",
|
||||
"rn-position-u8s1d",
|
||||
"rn-width-b8lwoo",
|
||||
],
|
||||
"className": "rn-borderTopColor-3vzq9n rn-borderRightColor-ycnuvz rn-borderBottomColor-wqks8h rn-borderLeftColor-3se2kx rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-b8lwoo",
|
||||
"className": "rn-borderColor-4a18lf rn-borderWidth-1yadl64 rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-b8lwoo",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 2`] = `
|
||||
Object {
|
||||
"classList": Array [
|
||||
"rn-borderTopColor-3vzq9n",
|
||||
"rn-borderRightColor-ycnuvz",
|
||||
"rn-borderBottomColor-wqks8h",
|
||||
"rn-borderLeftColor-3se2kx",
|
||||
"rn-borderTopWidth-13yce4e",
|
||||
"rn-borderRightWidth-fnigne",
|
||||
"rn-borderBottomWidth-ndvcnb",
|
||||
"rn-borderLeftWidth-gxnn5r",
|
||||
"rn-borderColor-4a18lf",
|
||||
"rn-borderWidth-1yadl64",
|
||||
"rn-left-1tsx3h3",
|
||||
"rn-opacity-icoktb",
|
||||
"rn-position-u8s1d",
|
||||
"rn-width-l0gwng",
|
||||
],
|
||||
"className": "rn-borderTopColor-3vzq9n rn-borderRightColor-ycnuvz rn-borderBottomColor-wqks8h rn-borderLeftColor-3se2kx rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-l0gwng",
|
||||
"className": "rn-borderColor-4a18lf rn-borderWidth-1yadl64 rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-l0gwng",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 3`] = `
|
||||
Object {
|
||||
"classList": Array [
|
||||
"rn-borderTopColor-3vzq9n",
|
||||
"rn-borderRightColor-ycnuvz",
|
||||
"rn-borderBottomColor-wqks8h",
|
||||
"rn-borderLeftColor-3se2kx",
|
||||
"rn-borderTopWidth-13yce4e",
|
||||
"rn-borderRightWidth-fnigne",
|
||||
"rn-borderBottomWidth-ndvcnb",
|
||||
"rn-borderLeftWidth-gxnn5r",
|
||||
"rn-borderColor-4a18lf",
|
||||
"rn-borderWidth-1yadl64",
|
||||
"rn-left-1tsx3h3",
|
||||
"rn-opacity-icoktb",
|
||||
"rn-position-u8s1d",
|
||||
"rn-width-b8lwoo",
|
||||
],
|
||||
"className": "rn-borderTopColor-3vzq9n rn-borderRightColor-ycnuvz rn-borderBottomColor-wqks8h rn-borderLeftColor-3se2kx rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-b8lwoo",
|
||||
"className": "rn-borderColor-4a18lf rn-borderWidth-1yadl64 rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-b8lwoo",
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`StyleSheet/StyleSheetManager getClassName 1`] = `undefined`;
|
||||
@@ -0,0 +1,127 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`StyleSheet/compile atomic converts style to atomic CSS 1`] = `
|
||||
Object {
|
||||
"rn-animationDirection-1kmv48j": Object {
|
||||
"identifier": "rn-animationDirection-1kmv48j",
|
||||
"property": "animationDirection",
|
||||
"rules": Array [
|
||||
".rn-animationDirection-1kmv48j { -webkit-animation-direction: alternate, alternate-reverse; animation-direction: alternate, alternate-reverse; }",
|
||||
],
|
||||
"value": "[\\"alternate\\",\\"alternate-reverse\\"]",
|
||||
},
|
||||
"rn-animationKeyframes-zacbmr": Object {
|
||||
"identifier": "rn-animationKeyframes-zacbmr",
|
||||
"property": "animationKeyframes",
|
||||
"rules": Array [
|
||||
".rn-animationKeyframes-zacbmr { -webkit-animation-name: r-animation-8jhqzh, r-animation-5azpl5; animation-name: r-animation-8jhqzh, r-animation-5azpl5; }",
|
||||
"@-webkit-keyframes r-animation-8jhqzh {
|
||||
0% { top: 0px; }
|
||||
50% { top: 5px; }
|
||||
100% { top: 10px; }
|
||||
}",
|
||||
"@keyframes r-animation-8jhqzh {
|
||||
0% { top: 0px; }
|
||||
50% { top: 5px; }
|
||||
100% { top: 10px; }
|
||||
}",
|
||||
"@-webkit-keyframes r-animation-5azpl5 {
|
||||
from { left: 0px; }
|
||||
to { left: 10px; }
|
||||
}",
|
||||
"@keyframes r-animation-5azpl5 {
|
||||
from { left: 0px; }
|
||||
to { left: 10px; }
|
||||
}",
|
||||
],
|
||||
"value": "[{\\"0%\\":{\\"top\\":0},\\"50%\\":{\\"top\\":5},\\"100%\\":{\\"top\\":10}},{\\"from\\":{\\"left\\":0},\\"to\\":{\\"left\\":10}}]",
|
||||
},
|
||||
"rn-fontFamily-1qd0xha": Object {
|
||||
"identifier": "rn-fontFamily-1qd0xha",
|
||||
"property": "fontFamily",
|
||||
"rules": Array [
|
||||
".rn-fontFamily-1qd0xha { font-family: system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif; }",
|
||||
],
|
||||
"value": "System",
|
||||
},
|
||||
"rn-marginHorizontal-vlx1xi": Object {
|
||||
"identifier": "rn-marginHorizontal-vlx1xi",
|
||||
"property": "marginHorizontal",
|
||||
"rules": Array [
|
||||
".rn-marginHorizontal-vlx1xi { margin-left: 10px; margin-right: 10px; }",
|
||||
],
|
||||
"value": "10px",
|
||||
},
|
||||
"rn-placeholderTextColor-1418aci": Object {
|
||||
"identifier": "rn-placeholderTextColor-1418aci",
|
||||
"property": "placeholderTextColor",
|
||||
"rules": Array [
|
||||
".rn-placeholderTextColor-1418aci::-webkit-input-placeholder { color: rgba(128,128,128,1.00); opacity: 1; }",
|
||||
".rn-placeholderTextColor-1418aci::-moz-placeholder { color: rgba(128,128,128,1.00); opacity: 1; }",
|
||||
".rn-placeholderTextColor-1418aci:-ms-input-placeholder { color: rgba(128,128,128,1.00); opacity: 1; }",
|
||||
".rn-placeholderTextColor-1418aci::placeholder { color: rgba(128,128,128,1.00); opacity: 1; }",
|
||||
],
|
||||
"value": "gray",
|
||||
},
|
||||
"rn-pointerEvents-ah5dr5": Object {
|
||||
"identifier": "rn-pointerEvents-ah5dr5",
|
||||
"property": "pointerEvents",
|
||||
"rules": Array [
|
||||
".rn-pointerEvents-ah5dr5 > * { pointer-events: none; }",
|
||||
".rn-pointerEvents-ah5dr5 { pointer-events: auto !important; }",
|
||||
],
|
||||
"value": "box-only",
|
||||
},
|
||||
"rn-transform-1ehiua4": Object {
|
||||
"identifier": "rn-transform-1ehiua4",
|
||||
"property": "transform",
|
||||
"rules": Array [
|
||||
".rn-transform-1ehiua4 { -webkit-transform: translateX(50px); transform: translateX(50px); }",
|
||||
],
|
||||
"value": "[{\\"translateX\\":50,\\"scale\\":-1}]",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`StyleSheet/compile classic converts style to classic CSS 1`] = `
|
||||
Object {
|
||||
"css-text-1jr0ypv": Object {
|
||||
"identifier": "css-text-1jr0ypv",
|
||||
"rules": Array [
|
||||
"@-webkit-keyframes r-animation-8jhqzh {
|
||||
0% { top: 0px; }
|
||||
50% { top: 5px; }
|
||||
100% { top: 10px; }
|
||||
}",
|
||||
"@keyframes r-animation-8jhqzh {
|
||||
0% { top: 0px; }
|
||||
50% { top: 5px; }
|
||||
100% { top: 10px; }
|
||||
}",
|
||||
"@-webkit-keyframes r-animation-5azpl5 {
|
||||
from { left: 0px; }
|
||||
to { left: 10px; }
|
||||
}",
|
||||
"@keyframes r-animation-5azpl5 {
|
||||
from { left: 0px; }
|
||||
to { left: 10px; }
|
||||
}",
|
||||
".css-text-1jr0ypv { -webkit-animation-direction: alternate, alternate-reverse; -webkit-animation-name: r-animation-8jhqzh, r-animation-5azpl5; -webkit-transform: translateX(50px); animation-direction: alternate, alternate-reverse; animation-name: r-animation-8jhqzh, r-animation-5azpl5; font: 14px system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif; margin-left: 10px; margin-right: 10px; transform: translateX(50px); }",
|
||||
],
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`StyleSheet/compile inline converts style to inline styles 1`] = `
|
||||
Object {
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexShrink": 1,
|
||||
"display": "flex",
|
||||
"flexBasis": "auto",
|
||||
"flexShrink": 1,
|
||||
"marginLeft": "10px",
|
||||
"marginRight": "10px",
|
||||
"msFlexNegative": 1,
|
||||
"msFlexPreferredSize": "auto",
|
||||
}
|
||||
`;
|
||||
@@ -1,30 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`StyleSheet/createAtomicRules transforms custom "animationName" declaration 1`] = `
|
||||
Array [
|
||||
"@-webkit-keyframes rn-anim-2k74q5{0%{top:0px}50%{top:5px}100%{top:10px}}",
|
||||
"@keyframes rn-anim-2k74q5{0%{top:0px}50%{top:5px}100%{top:10px}}",
|
||||
"@-webkit-keyframes rn-anim-zc91cv{from{left:0px}to{left:10px}}",
|
||||
"@keyframes rn-anim-zc91cv{from{left:0px}to{left:10px}}",
|
||||
".test{-webkit-animation-name:rn-anim-2k74q5,rn-anim-zc91cv;animation-name:rn-anim-2k74q5,rn-anim-zc91cv}",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`StyleSheet/createAtomicRules transforms custom "placeholderTextColor" declaration 1`] = `
|
||||
Array [
|
||||
"@media all {.test::-webkit-input-placeholder{color:gray;opacity:1}.test::-moz-placeholder{color:gray;opacity:1}.test:-ms-input-placeholder{color:gray;opacity:1}.test::placeholder{color:gray;opacity:1}}",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`StyleSheet/createAtomicRules transforms custom "pointerEvents" declaration 1`] = `
|
||||
Array [
|
||||
".test > *{pointer-events:none}",
|
||||
".test{pointer-events:auto !important}",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`StyleSheet/createAtomicRules transforms standard declarations to a single rule 1`] = `
|
||||
Array [
|
||||
".test{margin:0px}",
|
||||
]
|
||||
`;
|
||||
@@ -1,3 +0,0 @@
|
||||
// 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"`;
|
||||
67
packages/react-native-web/src/exports/StyleSheet/__tests__/compile-test.js
vendored
Normal file
67
packages/react-native-web/src/exports/StyleSheet/__tests__/compile-test.js
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
'use strict';
|
||||
|
||||
import { atomic, classic, inline } from '../compile';
|
||||
|
||||
describe('StyleSheet/compile', () => {
|
||||
describe('atomic', () => {
|
||||
test('converts style to atomic CSS', () => {
|
||||
const result = atomic({
|
||||
animationDirection: ['alternate', 'alternate-reverse'],
|
||||
animationKeyframes: [
|
||||
{ '0%': { top: 0 }, '50%': { top: 5 }, '100%': { top: 10 } },
|
||||
{ from: { left: 0 }, to: { left: 10 } }
|
||||
],
|
||||
fontFamily: 'System',
|
||||
marginHorizontal: 10,
|
||||
placeholderTextColor: 'gray',
|
||||
pointerEvents: 'box-only',
|
||||
transform: [
|
||||
{
|
||||
translateX: 50,
|
||||
scale: -1
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(result).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('classic', () => {
|
||||
test('converts style to classic CSS', () => {
|
||||
const result = classic(
|
||||
{
|
||||
animationDirection: ['alternate', 'alternate-reverse'],
|
||||
animationKeyframes: [
|
||||
{ '0%': { top: 0 }, '50%': { top: 5 }, '100%': { top: 10 } },
|
||||
{ from: { left: 0 }, to: { left: 10 } }
|
||||
],
|
||||
marginHorizontal: 10,
|
||||
font: '14px System',
|
||||
transform: [
|
||||
{
|
||||
translateX: 50,
|
||||
scale: -1
|
||||
}
|
||||
]
|
||||
},
|
||||
'text'
|
||||
);
|
||||
expect(result).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('inline', () => {
|
||||
test('converts style to inline styles', () => {
|
||||
const result = inline({
|
||||
marginHorizontal: 10,
|
||||
display: 'flex',
|
||||
flexShrink: 1
|
||||
});
|
||||
|
||||
expect(result).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import createAtomicRules from '../createAtomicRules';
|
||||
|
||||
describe('StyleSheet/createAtomicRules', () => {
|
||||
test('transforms standard declarations to a single rule', () => {
|
||||
expect(createAtomicRules('.test', 'margin', 0)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('transforms custom "animationName" declaration', () => {
|
||||
const value = [
|
||||
{ '0%': { top: 0 }, '50%': { top: 5 }, '100%': { top: 10 } },
|
||||
{ from: { left: 0 }, to: { left: 10 } }
|
||||
];
|
||||
expect(createAtomicRules('.test', 'animationName', value)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('transforms custom "pointerEvents" declaration', () => {
|
||||
expect(createAtomicRules('.test', 'pointerEvents', 'box-only')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('transforms custom "placeholderTextColor" declaration', () => {
|
||||
expect(createAtomicRules('.test', 'placeholderTextColor', 'gray')).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
120
packages/react-native-web/src/exports/StyleSheet/__tests__/createCompileableStyle-test.js
vendored
Normal file
120
packages/react-native-web/src/exports/StyleSheet/__tests__/createCompileableStyle-test.js
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import createCompileableStyle from '../createCompileableStyle';
|
||||
|
||||
const ignoreableStyle = {
|
||||
borderWidthLeft: 2,
|
||||
borderWidth: 1,
|
||||
borderWidthRight: 3,
|
||||
display: 'flex',
|
||||
marginVertical: 0,
|
||||
opacity: 0,
|
||||
resizeMode: 'contain'
|
||||
};
|
||||
|
||||
describe('StyleSheet/createCompileableStyle', () => {
|
||||
test('forwards valid styles', () => {
|
||||
const noop = createCompileableStyle(ignoreableStyle);
|
||||
expect(noop).toEqual(ignoreableStyle);
|
||||
});
|
||||
|
||||
describe('preprocesses multiple shadow styles into a single declaration', () => {
|
||||
test('shadowColor only', () => {
|
||||
const style = { shadowColor: 'red' };
|
||||
const resolved = createCompileableStyle(style);
|
||||
|
||||
expect(resolved).toEqual({
|
||||
boxShadow: '0px 0px 0px rgba(255,0,0,1.00)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowColor and shadowOpacity only', () => {
|
||||
expect(createCompileableStyle({ shadowColor: 'red', shadowOpacity: 0.5 })).toEqual({
|
||||
boxShadow: '0px 0px 0px rgba(255,0,0,0.50)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowOffset only', () => {
|
||||
expect(createCompileableStyle({ shadowOffset: { width: 1, height: 2 } })).toEqual({
|
||||
boxShadow: '1px 2px 0px rgba(0,0,0,1.00)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowRadius only', () => {
|
||||
expect(createCompileableStyle({ shadowRadius: 5 })).toEqual({
|
||||
boxShadow: '0px 0px 5px rgba(0,0,0,1.00)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowOffset, shadowRadius, shadowColor', () => {
|
||||
expect(
|
||||
createCompileableStyle({
|
||||
shadowColor: 'rgba(50,60,70,0.5)',
|
||||
shadowOffset: { width: 1, height: 2 },
|
||||
shadowOpacity: 0.5,
|
||||
shadowRadius: 3
|
||||
})
|
||||
).toEqual({
|
||||
boxShadow: '1px 2px 3px rgba(50,60,70,0.25)'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('preprocesses multiple textShadow styles into a single declaration', () => {
|
||||
test('textShadowColor only', () => {
|
||||
expect(createCompileableStyle({ textShadowColor: 'red' })).toEqual({});
|
||||
});
|
||||
|
||||
test('textShadowOffset only', () => {
|
||||
expect(createCompileableStyle({ textShadowOffset: { width: 1, height: 2 } })).toEqual({});
|
||||
});
|
||||
|
||||
test('textShadowRadius only', () => {
|
||||
expect(createCompileableStyle({ textShadowRadius: 5 })).toEqual({});
|
||||
});
|
||||
|
||||
test('textShadowColor and textShadowOffset only', () => {
|
||||
expect(
|
||||
createCompileableStyle({
|
||||
textShadowColor: 'red',
|
||||
textShadowOffset: { width: 0, height: 0 }
|
||||
})
|
||||
).toEqual({});
|
||||
expect(
|
||||
createCompileableStyle({
|
||||
textShadowColor: 'red',
|
||||
textShadowOffset: { width: -1, height: 0 }
|
||||
})
|
||||
).toEqual({
|
||||
textShadow: '-1px 0px 0px rgba(255,0,0,1.00)'
|
||||
});
|
||||
expect(
|
||||
createCompileableStyle({
|
||||
textShadowColor: 'red',
|
||||
textShadowOffset: { width: 1, height: 2 }
|
||||
})
|
||||
).toEqual({
|
||||
textShadow: '1px 2px 0px rgba(255,0,0,1.00)'
|
||||
});
|
||||
});
|
||||
|
||||
test('textShadowColor and textShadowRadius only', () => {
|
||||
expect(createCompileableStyle({ textShadowColor: 'red', textShadowRadius: 0 })).toEqual({});
|
||||
expect(createCompileableStyle({ textShadowColor: 'red', textShadowRadius: 5 })).toEqual({
|
||||
textShadow: '0px 0px 5px rgba(255,0,0,1.00)'
|
||||
});
|
||||
});
|
||||
|
||||
test('textShadowColor, textShadowOffset, textShadowRadius', () => {
|
||||
expect(
|
||||
createCompileableStyle({
|
||||
textShadowColor: 'rgba(50,60,70,0.50)',
|
||||
textShadowOffset: { width: 5, height: 10 },
|
||||
textShadowRadius: 15
|
||||
})
|
||||
).toEqual({
|
||||
textShadow: '5px 10px 15px rgba(50,60,70,0.50)'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -160,48 +160,6 @@ describe('StyleSheet/createReactDOMStyle', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('shadow styles', () => {
|
||||
test('shadowColor only', () => {
|
||||
const style = { shadowColor: 'red' };
|
||||
const resolved = createReactDOMStyle(style);
|
||||
|
||||
expect(resolved).toEqual({
|
||||
boxShadow: '0px 0px 0px rgba(255,0,0,1.00)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowColor and shadowOpacity only', () => {
|
||||
expect(createReactDOMStyle({ shadowColor: 'red', shadowOpacity: 0.5 })).toEqual({
|
||||
boxShadow: '0px 0px 0px rgba(255,0,0,0.50)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowOffset only', () => {
|
||||
expect(createReactDOMStyle({ shadowOffset: { width: 1, height: 2 } })).toEqual({
|
||||
boxShadow: '1px 2px 0px rgba(0,0,0,1.00)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowRadius only', () => {
|
||||
expect(createReactDOMStyle({ shadowRadius: 5 })).toEqual({
|
||||
boxShadow: '0px 0px 5px rgba(0,0,0,1.00)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowOffset, shadowRadius, shadowColor', () => {
|
||||
expect(
|
||||
createReactDOMStyle({
|
||||
shadowColor: 'rgba(50,60,70,0.5)',
|
||||
shadowOffset: { width: 1, height: 2 },
|
||||
shadowOpacity: 0.5,
|
||||
shadowRadius: 3
|
||||
})
|
||||
).toEqual({
|
||||
boxShadow: '1px 2px 3px rgba(50,60,70,0.25)'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('textAlignVertical', () => {
|
||||
expect(
|
||||
createReactDOMStyle({
|
||||
@@ -212,94 +170,13 @@ describe('StyleSheet/createReactDOMStyle', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('textDecoration styles', () => {
|
||||
test('textDecorationColor only', () => {
|
||||
expect(
|
||||
createReactDOMStyle({
|
||||
textDecorationColor: 'red'
|
||||
})
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
test('textDecorationLine only', () => {
|
||||
expect(
|
||||
createReactDOMStyle({
|
||||
textDecorationLine: 'underline'
|
||||
})
|
||||
).toEqual({
|
||||
textDecoration: 'underline'
|
||||
});
|
||||
});
|
||||
|
||||
test('textDecorationStyle only', () => {
|
||||
expect(
|
||||
createReactDOMStyle({
|
||||
textDecorationStyle: 'dashed'
|
||||
})
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
test('textDecorationColor, textDecorationLine, textDecorationStyle', () => {
|
||||
expect(
|
||||
createReactDOMStyle({
|
||||
textDecorationColor: 'red',
|
||||
textDecorationLine: 'underline',
|
||||
textDecorationStyle: 'dashed'
|
||||
})
|
||||
).toEqual({
|
||||
textDecoration: 'underline',
|
||||
textDecorationColor: 'rgba(255,0,0,1.00)',
|
||||
textDecorationStyle: 'dashed'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('textShadow styles', () => {
|
||||
test('textShadowColor only', () => {
|
||||
expect(createReactDOMStyle({ textShadowColor: 'red' })).toEqual({});
|
||||
});
|
||||
|
||||
test('textShadowOffset only', () => {
|
||||
expect(createReactDOMStyle({ textShadowOffset: { width: 1, height: 2 } })).toEqual({});
|
||||
});
|
||||
|
||||
test('textShadowRadius only', () => {
|
||||
expect(createReactDOMStyle({ textShadowRadius: 5 })).toEqual({});
|
||||
});
|
||||
|
||||
test('textShadowColor and textShadowOffset only', () => {
|
||||
expect(
|
||||
createReactDOMStyle({ textShadowColor: 'red', textShadowOffset: { width: 0, height: 0 } })
|
||||
).toEqual({});
|
||||
expect(
|
||||
createReactDOMStyle({ textShadowColor: 'red', textShadowOffset: { width: -1, height: 0 } })
|
||||
).toEqual({
|
||||
textShadow: '-1px 0px 0px rgba(255,0,0,1.00)'
|
||||
});
|
||||
expect(
|
||||
createReactDOMStyle({ textShadowColor: 'red', textShadowOffset: { width: 1, height: 2 } })
|
||||
).toEqual({
|
||||
textShadow: '1px 2px 0px rgba(255,0,0,1.00)'
|
||||
});
|
||||
});
|
||||
|
||||
test('textShadowColor and textShadowRadius only', () => {
|
||||
expect(createReactDOMStyle({ textShadowColor: 'red', textShadowRadius: 0 })).toEqual({});
|
||||
expect(createReactDOMStyle({ textShadowColor: 'red', textShadowRadius: 5 })).toEqual({
|
||||
textShadow: '0px 0px 5px rgba(255,0,0,1.00)'
|
||||
});
|
||||
});
|
||||
|
||||
test('textShadowColor, textShadowOffset, textShadowRadius', () => {
|
||||
expect(
|
||||
createReactDOMStyle({
|
||||
textShadowColor: 'rgba(50,60,70,0.50)',
|
||||
textShadowOffset: { width: 5, height: 10 },
|
||||
textShadowRadius: 15
|
||||
})
|
||||
).toEqual({
|
||||
textShadow: '5px 10px 15px rgba(50,60,70,0.50)'
|
||||
});
|
||||
test('textDecorationLine', () => {
|
||||
expect(
|
||||
createReactDOMStyle({
|
||||
textDecorationLine: 'underline'
|
||||
})
|
||||
).toEqual({
|
||||
textDecoration: 'underline'
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import createRuleBlock from '../createRuleBlock';
|
||||
|
||||
describe('StyleSheet/createRuleBlock', () => {
|
||||
test('generates correct css', () => {
|
||||
const style = {
|
||||
boxShadow: '1px 1px 1px 1px #000',
|
||||
borderWidthLeft: 2,
|
||||
borderWidthRight: 3,
|
||||
position: 'absolute',
|
||||
transitionDuration: '0.1s'
|
||||
};
|
||||
expect(createRuleBlock(style)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -24,13 +24,28 @@ describe('StyleSheet', () => {
|
||||
|
||||
describe('compose', () => {
|
||||
test('returns array when neither style is falsey', () => {
|
||||
expect(StyleSheet.compose(1, 2)).toEqual([1, 2]);
|
||||
expect(
|
||||
StyleSheet.compose(
|
||||
1,
|
||||
2
|
||||
)
|
||||
).toEqual([1, 2]);
|
||||
});
|
||||
test('returns style1 when style2 is falsey', () => {
|
||||
expect(StyleSheet.compose(1, null)).toBe(1);
|
||||
expect(
|
||||
StyleSheet.compose(
|
||||
1,
|
||||
null
|
||||
)
|
||||
).toBe(1);
|
||||
});
|
||||
test('returns style2 when style1 is falsey', () => {
|
||||
expect(StyleSheet.compose(null, 2)).toBe(2);
|
||||
expect(
|
||||
StyleSheet.compose(
|
||||
null,
|
||||
2
|
||||
)
|
||||
).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import normalizeValue from '../normalizeValue';
|
||||
|
||||
describe('StyleSheet/normalizeValue', () => {
|
||||
test('normalizes property values requiring units', () => {
|
||||
expect(normalizeValue('margin', 0)).toEqual('0px');
|
||||
});
|
||||
test('ignores unitless property values', () => {
|
||||
expect(normalizeValue('flexGrow', 1)).toEqual(1);
|
||||
expect(normalizeValue('scale', 2)).toEqual(2);
|
||||
});
|
||||
});
|
||||
20
packages/react-native-web/src/exports/StyleSheet/__tests__/normalizeValueWithProperty-test.js
vendored
Normal file
20
packages/react-native-web/src/exports/StyleSheet/__tests__/normalizeValueWithProperty-test.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import normalizeValueWithProperty from '../normalizeValueWithProperty';
|
||||
|
||||
describe('StyleSheet/normalizeValueWithProperty', () => {
|
||||
test('normalizes property values requiring units', () => {
|
||||
expect(normalizeValueWithProperty(0, 'margin')).toEqual('0px');
|
||||
});
|
||||
test('normalizes colors', () => {
|
||||
expect(normalizeValueWithProperty('red', 'color')).toEqual('rgba(255,0,0,1.00)');
|
||||
});
|
||||
test('ignores unitless property values', () => {
|
||||
expect(normalizeValueWithProperty(1, 'flexGrow')).toEqual(1);
|
||||
expect(normalizeValueWithProperty(2, 'scale')).toEqual(2);
|
||||
});
|
||||
test('ignores objects and arrays', () => {
|
||||
expect(normalizeValueWithProperty([])).toEqual([]);
|
||||
expect(normalizeValueWithProperty({})).toEqual({});
|
||||
});
|
||||
});
|
||||
262
packages/react-native-web/src/exports/StyleSheet/compile.js
vendored
Normal file
262
packages/react-native-web/src/exports/StyleSheet/compile.js
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* Copyright (c) Nicolas Gallagher.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
import createReactDOMStyle from './createReactDOMStyle';
|
||||
import hash from '../../vendor/hash';
|
||||
import hyphenateStyleName from 'hyphenate-style-name';
|
||||
import normalizeValueWithProperty from './normalizeValueWithProperty';
|
||||
import prefixStyles, { prefixInlineStyles } from '../../modules/prefixStyles';
|
||||
|
||||
type Value = Object | Array<any> | string | number;
|
||||
type Style = { [key: string]: Value };
|
||||
type Rule = string;
|
||||
type Rules = Array<Rule>;
|
||||
type RulesData = {|
|
||||
property?: string,
|
||||
value?: string,
|
||||
identifier: string,
|
||||
rules: Rules
|
||||
|};
|
||||
type CompilerOutput = { [key: string]: RulesData };
|
||||
|
||||
const cache = {
|
||||
get(property, value) {
|
||||
if (
|
||||
cache[property] != null &&
|
||||
cache[property].hasOwnProperty(value) &&
|
||||
cache[property][value] != null
|
||||
) {
|
||||
return cache[property][value];
|
||||
}
|
||||
},
|
||||
set(property, value, object) {
|
||||
if (cache[property] == null) {
|
||||
cache[property] = {};
|
||||
}
|
||||
return (cache[property][value] = object);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compile style to atomic CSS rules.
|
||||
*/
|
||||
export function atomic(style: Style): CompilerOutput {
|
||||
return Object.keys(style)
|
||||
.sort()
|
||||
.reduce((acc, property) => {
|
||||
const value = style[property];
|
||||
if (value != null) {
|
||||
const valueString = stringifyValueWithProperty(value, property);
|
||||
const cachedResult = cache.get(property, valueString);
|
||||
if (cachedResult != null) {
|
||||
const { identifier } = cachedResult;
|
||||
acc[identifier] = cachedResult;
|
||||
} else {
|
||||
const identifier = createIdentifier('rn', property, value);
|
||||
const rules = createAtomicRules(identifier, property, value);
|
||||
const cachedResult = cache.set(property, valueString, {
|
||||
property,
|
||||
value: stringifyValueWithProperty(value, property),
|
||||
identifier,
|
||||
rules
|
||||
});
|
||||
acc[identifier] = cachedResult;
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile simple style object to classic CSS rules.
|
||||
* No support for 'placeholderTextColor' or 'pointerEvents'.
|
||||
*/
|
||||
export function classic(style: Style, name: string): CompilerOutput {
|
||||
const identifier = createIdentifier('css', name, style);
|
||||
const { animationKeyframes, ...rest } = style;
|
||||
|
||||
const rules = [];
|
||||
const selector = `.${identifier}`;
|
||||
let animationName;
|
||||
if (animationKeyframes != null) {
|
||||
const { animationNames, rules: keyframesRules } = processKeyframesValue(animationKeyframes);
|
||||
animationName = animationNames.join(', ');
|
||||
rules.push(...keyframesRules);
|
||||
}
|
||||
const block = createDeclarationBlock({ ...rest, animationName });
|
||||
rules.push(`${selector} ${block}`);
|
||||
|
||||
return { [identifier]: { identifier, rules } };
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile simple style object to inline DOM styles.
|
||||
* No support for 'animationKeyframes', 'placeholderTextColor', or 'pointerEvents'.
|
||||
*/
|
||||
export function inline(style: Style) {
|
||||
return prefixInlineStyles(createReactDOMStyle(style));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a value string that normalizes different input values with a common
|
||||
* output.
|
||||
*/
|
||||
export function stringifyValueWithProperty(value: Value, property: ?string) {
|
||||
// e.g., 0 => '0px', 'black' => 'rgba(0,0,0,1)'
|
||||
const normalizedValue = normalizeValueWithProperty(value, property);
|
||||
return typeof normalizedValue !== 'string' ? JSON.stringify(normalizedValue) : normalizedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the Atomic CSS rules needed for a given StyleSheet rule.
|
||||
* Translates StyleSheet declarations to CSS.
|
||||
*/
|
||||
function createAtomicRules(identifier: string, property, value): Rules {
|
||||
const rules = [];
|
||||
const selector = `.${identifier}`;
|
||||
|
||||
// Handle non-standard properties and object values that require multiple
|
||||
// CSS rules to be created.
|
||||
switch (property) {
|
||||
case 'animationKeyframes': {
|
||||
const { animationNames, rules: keyframesRules } = processKeyframesValue(value);
|
||||
const block = createDeclarationBlock({ animationName: animationNames.join(', ') });
|
||||
rules.push(`${selector} ${block}`, ...keyframesRules);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'placeholderTextColor': {
|
||||
const block = createDeclarationBlock({ color: value, opacity: 1 });
|
||||
rules.push(
|
||||
`${selector}::-webkit-input-placeholder ${block}`,
|
||||
`${selector}::-moz-placeholder ${block}`,
|
||||
`${selector}:-ms-input-placeholder ${block}`,
|
||||
`${selector}::placeholder ${block}`
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// See #513
|
||||
case 'pointerEvents': {
|
||||
let finalValue = value;
|
||||
if (value === 'auto' || value === 'box-only') {
|
||||
finalValue = 'auto !important';
|
||||
if (value === 'box-only') {
|
||||
const block = createDeclarationBlock({ [property]: 'none' });
|
||||
rules.push(`${selector} > * ${block}`);
|
||||
}
|
||||
} else if (value === 'none' || value === 'box-none') {
|
||||
finalValue = 'none !important';
|
||||
if (value === 'box-none') {
|
||||
const block = createDeclarationBlock({ [property]: 'auto' });
|
||||
rules.push(`${selector} > * ${block}`);
|
||||
}
|
||||
}
|
||||
const block = createDeclarationBlock({ [property]: finalValue });
|
||||
rules.push(`${selector} ${block}`);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
const block = createDeclarationBlock({ [property]: value });
|
||||
rules.push(`${selector} ${block}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CSS declaration block from a StyleSheet object.
|
||||
*/
|
||||
function createDeclarationBlock(style: Style) {
|
||||
const domStyle = prefixStyles(createReactDOMStyle(style));
|
||||
const declarationsString = Object.keys(domStyle)
|
||||
.map(property => {
|
||||
const value = domStyle[property];
|
||||
const prop = hyphenateStyleName(property);
|
||||
// The prefixer may return an array of values:
|
||||
// { display: [ '-webkit-flex', 'flex' ] }
|
||||
// to represent "fallback" declarations
|
||||
// { display: -webkit-flex; display: flex; }
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(v => `${prop}: ${v}`).join(';');
|
||||
} else {
|
||||
return `${prop}: ${value}`;
|
||||
}
|
||||
})
|
||||
// Once properties are hyphenated, this will put the vendor
|
||||
// prefixed and short-form properties first in the list.
|
||||
.sort()
|
||||
.join('; ');
|
||||
|
||||
return `{ ${declarationsString}; }`;
|
||||
}
|
||||
|
||||
/**
|
||||
* An identifier is associated with a unique set of styles.
|
||||
*/
|
||||
function createIdentifier(prefix, name, value) {
|
||||
const hashedString = hash(name + stringifyValueWithProperty(value, name));
|
||||
return process.env.NODE_ENV !== 'production'
|
||||
? `${prefix}-${name}-${hashedString}`
|
||||
: `${prefix}-${hashedString}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create individual CSS keyframes rules.
|
||||
*/
|
||||
function createKeyframes(keyframes) {
|
||||
const prefixes = ['-webkit-', ''];
|
||||
const identifier = createIdentifier('r', 'animation', keyframes);
|
||||
|
||||
const steps =
|
||||
'{\n' +
|
||||
Object.keys(keyframes)
|
||||
.map(stepName => {
|
||||
const rule = keyframes[stepName];
|
||||
const block = createDeclarationBlock(rule);
|
||||
return `${stepName} ${block}`;
|
||||
})
|
||||
.join('\n') +
|
||||
'\n}';
|
||||
|
||||
const rules = prefixes.map(prefix => {
|
||||
return `@${prefix}keyframes ${identifier} ${steps}`;
|
||||
});
|
||||
return { identifier, rules };
|
||||
}
|
||||
|
||||
/**
|
||||
* Create CSS keyframes rules and names from a StyleSheet keyframes object.
|
||||
*/
|
||||
function processKeyframesValue(keyframesValue) {
|
||||
if (typeof keyframesValue === 'number') {
|
||||
throw new Error('Invalid CSS keyframes type');
|
||||
}
|
||||
|
||||
const animationNames = [];
|
||||
const rules = [];
|
||||
const value = Array.isArray(keyframesValue) ? keyframesValue : [keyframesValue];
|
||||
|
||||
value.forEach(keyframes => {
|
||||
if (typeof keyframes === 'string') {
|
||||
// Support external animation libraries (identifiers only)
|
||||
animationNames.push(keyframes);
|
||||
} else {
|
||||
// Create rules for each of the keyframes
|
||||
const { identifier, rules: keyframesRules } = createKeyframes(keyframes);
|
||||
animationNames.push(identifier);
|
||||
rules.push(...keyframesRules);
|
||||
}
|
||||
});
|
||||
|
||||
return { animationNames, rules };
|
||||
}
|
||||
25
packages/react-native-web/src/exports/StyleSheet/constants.js
vendored
Normal file
25
packages/react-native-web/src/exports/StyleSheet/constants.js
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
export const STYLE_ELEMENT_ID = 'react-native-stylesheet';
|
||||
|
||||
export const STYLE_GROUPS = {
|
||||
reset: 0,
|
||||
modality: 0.1,
|
||||
classic: 1,
|
||||
atomic: 2.2,
|
||||
custom: {
|
||||
borderColor: 2,
|
||||
borderRadius: 2,
|
||||
borderStyle: 2,
|
||||
borderWidth: 2,
|
||||
display: 2,
|
||||
flex: 2,
|
||||
margin: 2,
|
||||
overflow: 2,
|
||||
overscrollBehavior: 2,
|
||||
padding: 2,
|
||||
|
||||
marginHorizontal: 2.1,
|
||||
marginVertical: 2.1,
|
||||
paddingHorizontal: 2.1,
|
||||
paddingVertical: 2.1
|
||||
}
|
||||
};
|
||||
@@ -1,82 +0,0 @@
|
||||
import createKeyframesRules from './createKeyframesRules';
|
||||
import createRuleBlock from './createRuleBlock';
|
||||
|
||||
const createAtomicRules = (selector, prop, value) => {
|
||||
const rules = [];
|
||||
|
||||
// Handle custom properties and custom values that require additional rules
|
||||
// to be created.
|
||||
switch (prop) {
|
||||
// See #513
|
||||
case 'pointerEvents': {
|
||||
let val = value;
|
||||
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}}`);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'placeholderTextColor': {
|
||||
const block = createRuleBlock({ color: value, opacity: 1 });
|
||||
rules.push(
|
||||
'@media all {' +
|
||||
`${selector}::-webkit-input-placeholder{${block}}` +
|
||||
`${selector}::-moz-placeholder{${block}}` +
|
||||
`${selector}:-ms-input-placeholder{${block}}` +
|
||||
`${selector}::placeholder{${block}}` +
|
||||
'}'
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'animationName': {
|
||||
if (typeof value === 'string') {
|
||||
// add a className referencing the animation
|
||||
const block = createRuleBlock({ [prop]: value });
|
||||
rules.push(`${selector}{${block}}`);
|
||||
} else {
|
||||
const animationNames = [];
|
||||
|
||||
// add the keyframes needed to implement each value
|
||||
value.forEach(keyframes => {
|
||||
if (typeof keyframes === 'string') {
|
||||
animationNames.push(keyframes);
|
||||
} else {
|
||||
const { identifier, rules: keyframesRules } = createKeyframesRules(keyframes);
|
||||
keyframesRules.forEach(rule => {
|
||||
rules.push(rule);
|
||||
});
|
||||
animationNames.push(identifier);
|
||||
}
|
||||
});
|
||||
|
||||
// add a className referencing the animation identifiers
|
||||
const block = createRuleBlock({ [prop]: animationNames.join(',') });
|
||||
rules.push(`${selector}{${block}}`);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
const block = createRuleBlock({ [prop]: value });
|
||||
rules.push(`${selector}{${block}}`);
|
||||
}
|
||||
}
|
||||
|
||||
return rules;
|
||||
};
|
||||
|
||||
export default createAtomicRules;
|
||||
31
packages/react-native-web/src/exports/StyleSheet/createCSSStyleSheet.js
vendored
Normal file
31
packages/react-native-web/src/exports/StyleSheet/createCSSStyleSheet.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) Nicolas Gallagher.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
|
||||
// $FlowFixMe: HTMLStyleElement is incorrectly typed - https://github.com/facebook/flow/issues/2696
|
||||
export default function createCSSStyleSheet(id: string): ?CSSStyleSheet {
|
||||
if (canUseDOM) {
|
||||
const element = document.getElementById(id);
|
||||
if (element != null) {
|
||||
// $FlowFixMe: HTMLElement is incorrectly typed
|
||||
return element.sheet;
|
||||
} else {
|
||||
const element = document.createElement('style');
|
||||
element.setAttribute('id', id);
|
||||
const head = document.head;
|
||||
if (head) {
|
||||
head.insertBefore(element, head.firstChild);
|
||||
}
|
||||
return element.sheet;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
64
packages/react-native-web/src/exports/StyleSheet/createCompileableStyle.js
vendored
Normal file
64
packages/react-native-web/src/exports/StyleSheet/createCompileableStyle.js
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) Nicolas Gallagher.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
import normalizeValueWithProperty from './normalizeValueWithProperty';
|
||||
import resolveShadowValue from './resolveShadowValue';
|
||||
|
||||
const defaultOffset = { height: 0, width: 0 };
|
||||
|
||||
function boxShadowReducer(resolvedStyle, style) {
|
||||
const { boxShadow } = style;
|
||||
const shadow = resolveShadowValue(style);
|
||||
if (shadow != null) {
|
||||
resolvedStyle.boxShadow = boxShadow ? `${boxShadow}, ${shadow}` : shadow;
|
||||
}
|
||||
}
|
||||
|
||||
function textShadowReducer(resolvedStyle, style) {
|
||||
const { textShadowColor, textShadowOffset, textShadowRadius } = style;
|
||||
const { height, width } = textShadowOffset || defaultOffset;
|
||||
const radius = textShadowRadius || 0;
|
||||
const offsetX = normalizeValueWithProperty(width);
|
||||
const offsetY = normalizeValueWithProperty(height);
|
||||
const blurRadius = normalizeValueWithProperty(radius);
|
||||
const color = normalizeValueWithProperty(textShadowColor, 'textShadowColor');
|
||||
|
||||
if (color && (height !== 0 || width !== 0 || radius !== 0)) {
|
||||
resolvedStyle.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
|
||||
}
|
||||
}
|
||||
|
||||
const createCompileableStyle = (styles: Object) => {
|
||||
const {
|
||||
shadowColor,
|
||||
shadowOffset,
|
||||
shadowOpacity,
|
||||
shadowRadius,
|
||||
textShadowColor,
|
||||
textShadowOffset,
|
||||
textShadowRadius,
|
||||
...nextStyles
|
||||
} = styles;
|
||||
|
||||
if (
|
||||
shadowColor != null ||
|
||||
shadowOffset != null ||
|
||||
shadowOpacity != null ||
|
||||
shadowRadius != null
|
||||
) {
|
||||
boxShadowReducer(nextStyles, styles);
|
||||
}
|
||||
|
||||
if (textShadowColor != null || textShadowOffset != null || textShadowRadius != null) {
|
||||
textShadowReducer(nextStyles, styles);
|
||||
}
|
||||
return nextStyles;
|
||||
};
|
||||
|
||||
export default createCompileableStyle;
|
||||
@@ -1,37 +0,0 @@
|
||||
import createRuleBlock from './createRuleBlock';
|
||||
import createReactDOMStyle from './createReactDOMStyle';
|
||||
import i18nStyle from './i18nStyle';
|
||||
import hash from '../../vendor/hash';
|
||||
|
||||
const hashObject = obj => hash(JSON.stringify(obj));
|
||||
|
||||
const createIdentifier = obj => {
|
||||
const hashed = hashObject(obj);
|
||||
return process.env.NODE_ENV !== 'production' ? `rn-anim-${hashed}` : `rn-${hashed}`;
|
||||
};
|
||||
|
||||
const prefixes = ['-webkit-', ''];
|
||||
|
||||
const makeBlock = rule => {
|
||||
const domStyle = createReactDOMStyle(i18nStyle(rule));
|
||||
return createRuleBlock(domStyle);
|
||||
};
|
||||
|
||||
const makeSteps = keyframes =>
|
||||
Object.keys(keyframes)
|
||||
.map(stepName => {
|
||||
const rule = keyframes[stepName];
|
||||
const block = makeBlock(rule);
|
||||
return `${stepName}{${block}}`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
const createKeyframesRules = (keyframes: Object): Array<String> => {
|
||||
const identifier = createIdentifier(keyframes);
|
||||
const rules = prefixes.map(prefix => {
|
||||
return `@${prefix}keyframes ${identifier}{${makeSteps(keyframes)}}`;
|
||||
});
|
||||
return { identifier, rules };
|
||||
};
|
||||
|
||||
export default createKeyframesRules;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Nicolas Gallagher.
|
||||
* Copyright (c) Nicolas Gallagher.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
@@ -7,9 +7,7 @@
|
||||
* @noflow
|
||||
*/
|
||||
|
||||
import normalizeColor from '../../modules/normalizeColor';
|
||||
import normalizeValue from './normalizeValue';
|
||||
import resolveShadowValue from './resolveShadowValue';
|
||||
import normalizeValueWithProperty from './normalizeValueWithProperty';
|
||||
|
||||
/**
|
||||
* The browser implements the CSS cascade, where the order of properties is a
|
||||
@@ -44,16 +42,6 @@ const styleShortFormProperties = {
|
||||
writingDirection: ['direction']
|
||||
};
|
||||
|
||||
const colorProps = {
|
||||
backgroundColor: true,
|
||||
borderColor: true,
|
||||
borderTopColor: true,
|
||||
borderRightColor: true,
|
||||
borderBottomColor: true,
|
||||
borderLeftColor: true,
|
||||
color: true
|
||||
};
|
||||
|
||||
const borderWidthProps = {
|
||||
borderWidth: true,
|
||||
borderTopWidth: true,
|
||||
@@ -66,57 +54,6 @@ const monospaceFontStack = 'monospace, monospace';
|
||||
const systemFontStack =
|
||||
'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif';
|
||||
|
||||
const defaultOffset = { height: 0, width: 0 };
|
||||
|
||||
/**
|
||||
* Shadow
|
||||
*/
|
||||
|
||||
const resolveShadow = (resolvedStyle, style) => {
|
||||
const { boxShadow } = style;
|
||||
const shadow = resolveShadowValue(style);
|
||||
resolvedStyle.boxShadow = boxShadow ? `${boxShadow}, ${shadow}` : shadow;
|
||||
};
|
||||
|
||||
/**
|
||||
* Text Decoration
|
||||
*/
|
||||
|
||||
const resolveTextDecoration = (resolvedStyle, style) => {
|
||||
const { textDecorationColor, textDecorationLine, textDecorationStyle } = style;
|
||||
const color = normalizeColor(textDecorationColor);
|
||||
|
||||
if (textDecorationLine) {
|
||||
// use 'text-decoration' for browsers that support CSS2 text-decoration (e.g., IE, Edge)
|
||||
resolvedStyle.textDecoration = textDecorationLine;
|
||||
|
||||
if (textDecorationColor) {
|
||||
resolvedStyle.textDecorationColor = color;
|
||||
}
|
||||
if (textDecorationStyle) {
|
||||
resolvedStyle.textDecorationStyle = textDecorationStyle;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Text Shadow
|
||||
*/
|
||||
|
||||
const resolveTextShadow = (resolvedStyle, style) => {
|
||||
const { textShadowColor, textShadowOffset, textShadowRadius } = style;
|
||||
const { height, width } = textShadowOffset || defaultOffset;
|
||||
const radius = textShadowRadius || 0;
|
||||
const offsetX = normalizeValue(null, width);
|
||||
const offsetY = normalizeValue(null, height);
|
||||
const blurRadius = normalizeValue(null, radius);
|
||||
const color = normalizeColor(textShadowColor);
|
||||
|
||||
if (color && (height !== 0 || width !== 0 || radius !== 0)) {
|
||||
resolvedStyle.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform
|
||||
*/
|
||||
@@ -125,7 +62,7 @@ const resolveTextShadow = (resolvedStyle, style) => {
|
||||
// { translateX: 20 } => 'translateX(20px)'
|
||||
const mapTransform = transform => {
|
||||
const type = Object.keys(transform)[0];
|
||||
const value = normalizeValue(type, transform[type]);
|
||||
const value = normalizeValueWithProperty(transform[type], type);
|
||||
return `${type}(${value})`;
|
||||
};
|
||||
|
||||
@@ -149,174 +86,143 @@ const resolveTransform = (resolvedStyle, style) => {
|
||||
* Reducer
|
||||
*/
|
||||
|
||||
const createReducer = (style, styleProps) => {
|
||||
let hasResolvedShadow = false;
|
||||
let hasResolvedTextDecoration = false;
|
||||
let hasResolvedTextShadow = false;
|
||||
|
||||
return (resolvedStyle, prop) => {
|
||||
let value = normalizeValue(prop, style[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 ? normalizeValue(null, 0) : value;
|
||||
}
|
||||
|
||||
// Normalize color values
|
||||
if (colorProps[prop]) {
|
||||
value = normalizeColor(value);
|
||||
}
|
||||
|
||||
// Ignore everything else with a null value
|
||||
if (value == null) {
|
||||
return resolvedStyle;
|
||||
}
|
||||
|
||||
switch (prop) {
|
||||
// Ignore some React Native styles
|
||||
case 'aspectRatio':
|
||||
case 'elevation':
|
||||
case 'overlayColor':
|
||||
case 'resizeMode':
|
||||
case 'tintColor': {
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: remove once this issue is fixed
|
||||
// https://github.com/rofrischmann/inline-style-prefixer/issues/159
|
||||
case 'backgroundClip': {
|
||||
if (value === 'text') {
|
||||
resolvedStyle.backgroundClip = value;
|
||||
resolvedStyle.WebkitBackgroundClip = value;
|
||||
}
|
||||
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': {
|
||||
if (value > 0) {
|
||||
resolvedStyle.flexGrow = value;
|
||||
resolvedStyle.flexShrink = 1;
|
||||
resolvedStyle.flexBasis = '0%';
|
||||
} else if (value === 0) {
|
||||
resolvedStyle.flexGrow = 0;
|
||||
resolvedStyle.flexShrink = 0;
|
||||
resolvedStyle.flexBasis = '0%';
|
||||
} else if (value === -1) {
|
||||
resolvedStyle.flexGrow = 0;
|
||||
resolvedStyle.flexShrink = 1;
|
||||
resolvedStyle.flexBasis = 'auto';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'fontFamily': {
|
||||
if (value.indexOf('System') > -1) {
|
||||
const stack = value.split(/\s*,\s*/);
|
||||
stack[stack.indexOf('System')] = systemFontStack;
|
||||
resolvedStyle.fontFamily = stack.join(', ');
|
||||
} else if (value === 'monospace') {
|
||||
resolvedStyle.fontFamily = monospaceFontStack;
|
||||
} else {
|
||||
resolvedStyle.fontFamily = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'fontVariant': {
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
resolvedStyle.fontVariant = value.join(' ');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'shadowColor':
|
||||
case 'shadowOffset':
|
||||
case 'shadowOpacity':
|
||||
case 'shadowRadius': {
|
||||
if (!hasResolvedShadow) {
|
||||
resolveShadow(resolvedStyle, style);
|
||||
}
|
||||
hasResolvedShadow = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'textAlignVertical': {
|
||||
resolvedStyle.verticalAlign = value === 'center' ? 'middle' : value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'textDecorationColor':
|
||||
case 'textDecorationLine':
|
||||
case 'textDecorationStyle': {
|
||||
if (!hasResolvedTextDecoration) {
|
||||
resolveTextDecoration(resolvedStyle, style);
|
||||
}
|
||||
hasResolvedTextDecoration = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'textShadowColor':
|
||||
case 'textShadowOffset':
|
||||
case 'textShadowRadius': {
|
||||
if (!hasResolvedTextShadow) {
|
||||
resolveTextShadow(resolvedStyle, style);
|
||||
}
|
||||
hasResolvedTextShadow = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'transform':
|
||||
case 'transformMatrix': {
|
||||
resolveTransform(resolvedStyle, style);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
const longFormProperties = styleShortFormProperties[prop];
|
||||
if (longFormProperties) {
|
||||
longFormProperties.forEach((longForm, i) => {
|
||||
// The value of any longform property in the original styles takes
|
||||
// precedence over the shortform's value.
|
||||
if (styleProps.indexOf(longForm) === -1) {
|
||||
resolvedStyle[longForm] = value;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolvedStyle[prop] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedStyle;
|
||||
};
|
||||
};
|
||||
|
||||
const createReactDOMStyle = style => {
|
||||
if (!style) {
|
||||
return emptyObject;
|
||||
}
|
||||
const styleProps = Object.keys(style);
|
||||
const sortedStyleProps = styleProps.sort();
|
||||
const reducer = createReducer(style, styleProps);
|
||||
const resolvedStyle = sortedStyleProps.reduce(reducer, {});
|
||||
|
||||
const resolvedStyle = {};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Ignore everything else with a null value
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (prop) {
|
||||
// Ignore some React Native styles
|
||||
case 'aspectRatio':
|
||||
case 'elevation':
|
||||
case 'overlayColor':
|
||||
case 'resizeMode':
|
||||
case 'tintColor': {
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: remove once this issue is fixed
|
||||
// https://github.com/rofrischmann/inline-style-prefixer/issues/159
|
||||
case 'backgroundClip': {
|
||||
if (value === 'text') {
|
||||
resolvedStyle.backgroundClip = value;
|
||||
resolvedStyle.WebkitBackgroundClip = value;
|
||||
}
|
||||
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': {
|
||||
if (value > 0) {
|
||||
resolvedStyle.flexGrow = value;
|
||||
resolvedStyle.flexShrink = 1;
|
||||
resolvedStyle.flexBasis = '0%';
|
||||
} else if (value === 0) {
|
||||
resolvedStyle.flexGrow = 0;
|
||||
resolvedStyle.flexShrink = 0;
|
||||
resolvedStyle.flexBasis = '0%';
|
||||
} else if (value === -1) {
|
||||
resolvedStyle.flexGrow = 0;
|
||||
resolvedStyle.flexShrink = 1;
|
||||
resolvedStyle.flexBasis = 'auto';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'font': {
|
||||
resolvedStyle[prop] = value.replace('System', systemFontStack);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'fontFamily': {
|
||||
if (value.indexOf('System') > -1) {
|
||||
const stack = value.split(/,\s*/);
|
||||
stack[stack.indexOf('System')] = systemFontStack;
|
||||
resolvedStyle[prop] = stack.join(', ');
|
||||
} else if (value === 'monospace') {
|
||||
resolvedStyle[prop] = monospaceFontStack;
|
||||
} else {
|
||||
resolvedStyle[prop] = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'fontVariant': {
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
resolvedStyle.fontVariant = value.join(' ');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'textAlignVertical': {
|
||||
resolvedStyle.verticalAlign = value === 'center' ? 'middle' : value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'textDecorationLine': {
|
||||
// use 'text-decoration' for browsers that only support CSS2
|
||||
// text-decoration (e.g., IE, Edge)
|
||||
resolvedStyle.textDecoration = value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'transform':
|
||||
case 'transformMatrix': {
|
||||
resolveTransform(resolvedStyle, style);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
const longFormProperties = styleShortFormProperties[prop];
|
||||
if (longFormProperties) {
|
||||
longFormProperties.forEach((longForm, i) => {
|
||||
// The value of any longform property in the original styles takes
|
||||
// precedence over the shortform's value.
|
||||
if (typeof style[longForm] === 'undefined') {
|
||||
resolvedStyle[longForm] = value;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolvedStyle[prop] = Array.isArray(value) ? value.join(', ') : value;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return resolvedStyle;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* 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 hyphenateStyleName from 'hyphenate-style-name';
|
||||
import mapKeyValue from '../../modules/mapKeyValue';
|
||||
import normalizeValue from './normalizeValue';
|
||||
import prefixStyles from '../../modules/prefixStyles';
|
||||
|
||||
const createDeclarationString = (prop, val) => {
|
||||
const name = hyphenateStyleName(prop);
|
||||
const value = normalizeValue(prop, val);
|
||||
if (Array.isArray(val)) {
|
||||
return val.map(v => `${name}:${v}`).join(';');
|
||||
}
|
||||
return `${name}:${value}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates valid CSS rule body from a JS object
|
||||
*
|
||||
* createRuleBlock({ width: 20, color: 'blue' });
|
||||
* // => 'color:blue;width:20px'
|
||||
*/
|
||||
const createRuleBlock = style =>
|
||||
mapKeyValue(prefixStyles(style), createDeclarationString)
|
||||
.sort()
|
||||
.join(';');
|
||||
|
||||
export default createRuleBlock;
|
||||
0
packages/react-native-web/src/exports/StyleSheet/createStyleResolver.js
vendored
Normal file
0
packages/react-native-web/src/exports/StyleSheet/createStyleResolver.js
vendored
Normal file
@@ -19,12 +19,8 @@
|
||||
*/
|
||||
|
||||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
import hash from '../../vendor/hash';
|
||||
|
||||
const focusVisibleAttributeName =
|
||||
'data-rn-' +
|
||||
(process.env.NODE_ENV !== 'production' ? 'focusvisible-' : '') +
|
||||
hash('focusvisible');
|
||||
const focusVisibleAttributeName = 'data-focusvisible-polyfill';
|
||||
|
||||
const rule = `:focus:not([${focusVisibleAttributeName}]){outline: none;}`;
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* 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 unitlessNumbers from '../../modules/unitlessNumbers';
|
||||
|
||||
const normalizeValue = (property: string, value) => {
|
||||
if (!unitlessNumbers[property] && typeof value === 'number') {
|
||||
value = `${value}px`;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
export default normalizeValue;
|
||||
33
packages/react-native-web/src/exports/StyleSheet/normalizeValueWithProperty.js
vendored
Normal file
33
packages/react-native-web/src/exports/StyleSheet/normalizeValueWithProperty.js
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) Nicolas Gallagher.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
import unitlessNumbers from '../../modules/unitlessNumbers';
|
||||
import normalizeColor from '../../modules/normalizeColor';
|
||||
|
||||
const colorProps = {
|
||||
backgroundColor: true,
|
||||
borderColor: true,
|
||||
borderTopColor: true,
|
||||
borderRightColor: true,
|
||||
borderBottomColor: true,
|
||||
borderLeftColor: true,
|
||||
color: true,
|
||||
shadowColor: true,
|
||||
textDecorationColor: true,
|
||||
textShadowColor: true
|
||||
};
|
||||
|
||||
export default function normalizeValueWithProperty(value: any, property?: ?string) {
|
||||
if ((property == null || !unitlessNumbers[property]) && typeof value === 'number') {
|
||||
value = `${value}px`;
|
||||
} else if (property != null && colorProps[property]) {
|
||||
value = normalizeColor(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -8,16 +8,16 @@
|
||||
*/
|
||||
|
||||
import normalizeColor from '../../modules/normalizeColor';
|
||||
import normalizeValue from './normalizeValue';
|
||||
import normalizeValueWithProperty from './normalizeValueWithProperty';
|
||||
|
||||
const defaultOffset = { height: 0, width: 0 };
|
||||
|
||||
const resolveShadowValue = (style: Object) => {
|
||||
const { shadowColor, shadowOffset, shadowOpacity, shadowRadius } = style;
|
||||
const { height, width } = shadowOffset || defaultOffset;
|
||||
const offsetX = normalizeValue(null, width);
|
||||
const offsetY = normalizeValue(null, height);
|
||||
const blurRadius = normalizeValue(null, shadowRadius || 0);
|
||||
const offsetX = normalizeValueWithProperty(width);
|
||||
const offsetY = normalizeValueWithProperty(height);
|
||||
const blurRadius = normalizeValueWithProperty(shadowRadius || 0);
|
||||
const color = normalizeColor(shadowColor || 'black', shadowOpacity);
|
||||
if (color) {
|
||||
return `${offsetX} ${offsetY} ${blurRadius} ${color}`;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
exports[`components/Text prop "onPress" 1`] = `
|
||||
<div
|
||||
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-cursor-1loqt21 rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-1b43r93 rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
|
||||
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"
|
||||
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-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-1b43r93 rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
|
||||
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"
|
||||
dir="auto"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`components/Text prop "selectable" 2`] = `
|
||||
<div
|
||||
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-1b43r93 rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-textDecoration-bauka4 rn-userSelect-lrvibr rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
|
||||
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"
|
||||
dir="auto"
|
||||
/>
|
||||
`;
|
||||
|
||||
@@ -9,19 +9,27 @@
|
||||
|
||||
import { arrayOf, number, object, oneOf, oneOfType, string } from 'prop-types';
|
||||
|
||||
const animationDirectionEnum = ['alternate', 'alternate-reverse', 'normal', 'reverse'];
|
||||
const animationFillModeEnum = ['none', 'forwards', 'backwards', 'both'];
|
||||
const animationPlayStateEnum = ['paused', 'running'];
|
||||
|
||||
const AnimationPropTypes = {
|
||||
animationDelay: string,
|
||||
animationDirection: oneOf(['alternate', 'alternate-reverse', 'normal', 'reverse']),
|
||||
animationDuration: string,
|
||||
animationFillMode: oneOf(['none', 'forwards', 'backwards', 'both']),
|
||||
animationIterationCount: oneOfType([number, oneOf(['infinite'])]),
|
||||
animationName: oneOfType([string, arrayOf(oneOfType([string, object]))]),
|
||||
animationPlayState: oneOf(['paused', 'running']),
|
||||
animationTimingFunction: string,
|
||||
transitionDelay: string,
|
||||
transitionDuration: string,
|
||||
transitionProperty: string,
|
||||
transitionTimingFunction: string
|
||||
animationDelay: oneOfType([string, arrayOf(string)]),
|
||||
animationDirection: oneOfType([oneOf(animationDirectionEnum), arrayOf(animationDirectionEnum)]),
|
||||
animationDuration: oneOfType([string, arrayOf(string)]),
|
||||
animationFillMode: oneOfType([oneOf(animationFillModeEnum), arrayOf(animationFillModeEnum)]),
|
||||
animationIterationCount: oneOfType([
|
||||
number,
|
||||
oneOf(['infinite']),
|
||||
arrayOf(oneOfType([number, oneOf(['infinite'])]))
|
||||
]),
|
||||
animationKeyframes: oneOfType([string, object, arrayOf(oneOfType([string, object]))]),
|
||||
animationPlayState: oneOfType([oneOf(animationPlayStateEnum), arrayOf(animationPlayStateEnum)]),
|
||||
animationTimingFunction: oneOfType([string, arrayOf(string)]),
|
||||
transitionDelay: oneOfType([string, arrayOf(string)]),
|
||||
transitionDuration: oneOfType([string, arrayOf(string)]),
|
||||
transitionProperty: oneOfType([string, arrayOf(string)]),
|
||||
transitionTimingFunction: oneOfType([string, arrayOf(string)])
|
||||
};
|
||||
|
||||
export default AnimationPropTypes;
|
||||
|
||||
@@ -4,8 +4,8 @@ exports[`modules/createDOMProps includes "rel" values for "a" elements (to secur
|
||||
|
||||
exports[`modules/createDOMProps includes cursor style for "button" role 1`] = `"rn-cursor-1loqt21"`;
|
||||
|
||||
exports[`modules/createDOMProps includes reset styles for "a" elements 1`] = `"rn-backgroundColor-1niwhzg rn-color-homxoj rn-textDecoration-bauka4"`;
|
||||
exports[`modules/createDOMProps includes reset styles for "a" elements 1`] = `"rn-backgroundColor-1niwhzg rn-color-homxoj rn-textDecorationLine-13wfysu"`;
|
||||
|
||||
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-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-textAlign-1ttztb7"`;
|
||||
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 reset styles for "ul" elements 1`] = `"rn-listStyle-1ebb2ja"`;
|
||||
|
||||
@@ -347,8 +347,8 @@ const stylePropTypes = [
|
||||
},
|
||||
{
|
||||
label: 'web',
|
||||
name: 'animationName',
|
||||
typeInfo: 'string | Array<Object>'
|
||||
name: 'animationKeyframes',
|
||||
typeInfo: 'Array<Object|string>'
|
||||
},
|
||||
{
|
||||
label: 'web',
|
||||
|
||||
@@ -31,7 +31,7 @@ const createConfig = ({ modules }) => ({
|
||||
'@babel/plugin-transform-flow-strip-types',
|
||||
['babel-plugin-transform-react-remove-prop-types', { mode: 'wrap' }],
|
||||
['@babel/plugin-proposal-class-properties', { loose: true }],
|
||||
['@babel/plugin-proposal-object-rest-spread', { useBuiltIns: true }],
|
||||
['@babel/plugin-proposal-object-rest-spread', { useBuiltIns: true }]
|
||||
].concat(modules ? ['babel-plugin-add-module-exports'] : [])
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user