mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-18 04:42:45 +00:00
Reorganize 'createReactDOMStyle'
1. Rename 'expandStyle' to 'createReactDOMStyle' 2. Move use of 'i18nStyle' out of 'createReactDOMStyle' to decouple the two transformations. 3. Move the style property resolvers into 'createReactDOMStyle'
This commit is contained in:
@@ -6,6 +6,7 @@ import createReactDOMStyle from './createReactDOMStyle';
|
||||
import flattenArray from '../../modules/flattenArray';
|
||||
import flattenStyle from './flattenStyle';
|
||||
import I18nManager from '../I18nManager';
|
||||
import i18nStyle from './i18nStyle';
|
||||
import mapKeyValue from '../../modules/mapKeyValue';
|
||||
import { prefixInlineStyles } from '../../modules/prefixStyles';
|
||||
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
|
||||
@@ -34,7 +35,7 @@ class StyleRegistry {
|
||||
register(flatStyle) {
|
||||
const id = ReactNativePropRegistry.register(flatStyle);
|
||||
const key = createCacheKey(id);
|
||||
const style = createReactDOMStyle(flatStyle);
|
||||
const style = createReactDOMStyle(i18nStyle(flatStyle));
|
||||
const classList = mapKeyValue(style, (prop, value) => {
|
||||
if (value != null) {
|
||||
return this.styleManager.setDeclaration(prop, value);
|
||||
@@ -130,7 +131,7 @@ class StyleRegistry {
|
||||
* Resolves a React Native style object
|
||||
*/
|
||||
_resolveStyle(reactNativeStyle) {
|
||||
const domStyle = createReactDOMStyle(flattenStyle(reactNativeStyle));
|
||||
const domStyle = createReactDOMStyle(i18nStyle(flattenStyle(reactNativeStyle)));
|
||||
|
||||
const props = Object.keys(domStyle).reduce(
|
||||
(props, styleProp) => {
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`apis/StyleSheet/createReactDOMStyle converts ReactNative style to ReactDOM style 1`] = `
|
||||
exports[`apis/StyleSheet/createReactDOMStyle shortform -> longform 1`] = `
|
||||
Object {
|
||||
"borderBottomColor": "white",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "1px",
|
||||
"borderLeftWidth": "1px",
|
||||
"borderRightWidth": "1px",
|
||||
"borderTopWidth": "1px",
|
||||
"borderWidthLeft": "2px",
|
||||
"borderWidthRight": "3px",
|
||||
"boxShadow": "1px 1px 1px 1px #000, 1px 2px 0px red",
|
||||
"display": "flex",
|
||||
"flexShrink": 0,
|
||||
"marginBottom": "0px",
|
||||
"marginTop": "0px",
|
||||
"opacity": 0,
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"marginBottom": "25px",
|
||||
"marginLeft": "10px",
|
||||
"marginRight": "10px",
|
||||
"marginTop": "50px",
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`apis/StyleSheet/expandStyle shortform -> longform 1`] = `
|
||||
Object {
|
||||
"borderBottomColor": "white",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "1px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"marginBottom": "25px",
|
||||
"marginLeft": "10px",
|
||||
"marginRight": "10px",
|
||||
"marginTop": "50px",
|
||||
}
|
||||
`;
|
||||
@@ -16,13 +16,148 @@ const reactNativeStyle = {
|
||||
};
|
||||
|
||||
describe('apis/StyleSheet/createReactDOMStyle', () => {
|
||||
test('converts ReactNative style to ReactDOM style', () => {
|
||||
expect(createReactDOMStyle(reactNativeStyle)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('noop on DOM styles', () => {
|
||||
const firstStyle = createReactDOMStyle(reactNativeStyle);
|
||||
const secondStyle = createReactDOMStyle(firstStyle);
|
||||
expect(firstStyle).toEqual(secondStyle);
|
||||
});
|
||||
|
||||
test('flex', () => {
|
||||
expect(createReactDOMStyle({ display: 'flex' })).toEqual({
|
||||
display: 'flex',
|
||||
flexShrink: 0
|
||||
});
|
||||
|
||||
expect(createReactDOMStyle({ display: 'flex', flex: 1 })).toEqual({
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
flexBasis: 'auto'
|
||||
});
|
||||
|
||||
expect(createReactDOMStyle({ display: 'flex', flex: 10 })).toEqual({
|
||||
display: 'flex',
|
||||
flexGrow: 10,
|
||||
flexShrink: 1,
|
||||
flexBasis: 'auto'
|
||||
});
|
||||
|
||||
expect(createReactDOMStyle({ display: 'flex', flexShrink: 1 })).toEqual({
|
||||
display: 'flex',
|
||||
flexShrink: 1
|
||||
});
|
||||
|
||||
expect(createReactDOMStyle({ display: 'flex', flex: 1, flexShrink: 2 })).toEqual({
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
flexShrink: 2,
|
||||
flexBasis: 'auto'
|
||||
});
|
||||
});
|
||||
|
||||
test('shortform -> longform', () => {
|
||||
const style = {
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
borderBottomColor: 'white',
|
||||
borderBottomWidth: 1,
|
||||
borderWidth: 0,
|
||||
marginTop: 50,
|
||||
marginVertical: 25,
|
||||
margin: 10
|
||||
};
|
||||
|
||||
expect(createReactDOMStyle(style)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('shadow styles', () => {
|
||||
test('shadowColor only', () => {
|
||||
const style = { shadowColor: 'red' };
|
||||
const resolved = createReactDOMStyle(style);
|
||||
|
||||
expect(resolved).toEqual({
|
||||
boxShadow: '0px 0px 0px red'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowColor and shadowOpacity only', () => {
|
||||
expect(createReactDOMStyle({ shadowColor: 'red', shadowOpacity: 0.5 })).toEqual({
|
||||
boxShadow: '0px 0px 0px rgba(255,0,0,0.5)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowOffset only', () => {
|
||||
expect(createReactDOMStyle({ shadowOffset: { width: 1, height: 2 } })).toEqual({});
|
||||
});
|
||||
|
||||
test('shadowRadius only', () => {
|
||||
expect(createReactDOMStyle({ shadowRadius: 5 })).toEqual({});
|
||||
});
|
||||
|
||||
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({
|
||||
textAlignVertical: 'center'
|
||||
})
|
||||
).toEqual({
|
||||
verticalAlign: 'middle'
|
||||
});
|
||||
});
|
||||
|
||||
test('textShadowOffset', () => {
|
||||
expect(
|
||||
createReactDOMStyle({
|
||||
textShadowColor: 'red',
|
||||
textShadowOffset: { width: 1, height: 2 },
|
||||
textShadowRadius: 5
|
||||
})
|
||||
).toEqual({
|
||||
textShadow: '1px 2px 5px red'
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
// passthrough if transform value is ever a string
|
||||
test('string', () => {
|
||||
const transform = 'perspective(50px) scaleX(20) translateX(20px) rotate(20deg)';
|
||||
const style = { transform };
|
||||
const resolved = createReactDOMStyle(style);
|
||||
|
||||
expect(resolved).toEqual({ transform });
|
||||
});
|
||||
|
||||
test('array', () => {
|
||||
const style = {
|
||||
transform: [{ perspective: 50 }, { scaleX: 20 }, { translateX: 20 }, { rotate: '20deg' }]
|
||||
};
|
||||
const resolved = createReactDOMStyle(style);
|
||||
|
||||
expect(resolved).toEqual({
|
||||
transform: 'perspective(50px) scaleX(20) translateX(20px) rotate(20deg)'
|
||||
});
|
||||
});
|
||||
|
||||
test('transformMatrix', () => {
|
||||
const style = { transformMatrix: [1, 2, 3, 4, 5, 6] };
|
||||
const resolved = createReactDOMStyle(style);
|
||||
|
||||
expect(resolved).toEqual({
|
||||
transform: 'matrix3d(1,2,3,4,5,6)'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import expandStyle from '../expandStyle';
|
||||
|
||||
describe('apis/StyleSheet/expandStyle', () => {
|
||||
test('shortform -> longform', () => {
|
||||
const style = {
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
borderBottomColor: 'white',
|
||||
borderBottomWidth: 1,
|
||||
borderWidth: 0,
|
||||
marginTop: 50,
|
||||
marginVertical: 25,
|
||||
margin: 10
|
||||
};
|
||||
|
||||
expect(expandStyle(style)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('textAlignVertical', () => {
|
||||
const initial = {
|
||||
textAlignVertical: 'center'
|
||||
};
|
||||
|
||||
const expected = {
|
||||
verticalAlign: 'middle'
|
||||
};
|
||||
|
||||
expect(expandStyle(initial)).toEqual(expected);
|
||||
});
|
||||
|
||||
test('flex', () => {
|
||||
const value = 10;
|
||||
|
||||
const initial = {
|
||||
flex: value
|
||||
};
|
||||
|
||||
const expected = {
|
||||
flexGrow: value,
|
||||
flexShrink: 1,
|
||||
flexBasis: 'auto'
|
||||
};
|
||||
|
||||
expect(expandStyle(initial)).toEqual(expected);
|
||||
});
|
||||
|
||||
test('flex', () => {
|
||||
expect(expandStyle({ display: 'flex' })).toEqual({
|
||||
display: 'flex',
|
||||
flexShrink: 0
|
||||
});
|
||||
|
||||
expect(expandStyle({ display: 'flex', flex: 1 })).toEqual({
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
flexBasis: 'auto'
|
||||
});
|
||||
|
||||
expect(expandStyle({ display: 'flex', flex: 10 })).toEqual({
|
||||
display: 'flex',
|
||||
flexGrow: 10,
|
||||
flexShrink: 1,
|
||||
flexBasis: 'auto'
|
||||
});
|
||||
|
||||
expect(expandStyle({ display: 'flex', flexShrink: 1 })).toEqual({
|
||||
display: 'flex',
|
||||
flexShrink: 1
|
||||
});
|
||||
|
||||
expect(expandStyle({ display: 'flex', flex: 1, flexShrink: 2 })).toEqual({
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
flexShrink: 2,
|
||||
flexBasis: 'auto'
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import resolveBoxShadow from '../resolveBoxShadow';
|
||||
|
||||
describe('apis/StyleSheet/resolveBoxShadow', () => {
|
||||
test('shadowColor only', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = { shadowColor: 'red' };
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
boxShadow: '0px 0px 0px red'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowColor and shadowOpacity only', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = { shadowColor: 'red', shadowOpacity: 0.5 };
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
boxShadow: '0px 0px 0px rgba(255,0,0,0.5)'
|
||||
});
|
||||
});
|
||||
|
||||
test('shadowOffset only', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = { shadowOffset: { width: 1, height: 2 } };
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({});
|
||||
});
|
||||
|
||||
test('shadowRadius only', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = { shadowRadius: 5 };
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({});
|
||||
});
|
||||
|
||||
test('shadowOffset, shadowRadius, shadowColor', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = {
|
||||
shadowColor: 'rgba(50,60,70,0.5)',
|
||||
shadowOffset: { width: 1, height: 2 },
|
||||
shadowOpacity: 0.5,
|
||||
shadowRadius: 3
|
||||
};
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
boxShadow: '1px 2px 3px rgba(50,60,70,0.25)'
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import resolveTextShadow from '../resolveTextShadow';
|
||||
|
||||
describe('apis/StyleSheet/resolveTextShadow', () => {
|
||||
test('textShadowOffset', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = {
|
||||
textShadowColor: 'red',
|
||||
textShadowOffset: { width: 1, height: 2 },
|
||||
textShadowRadius: 5
|
||||
};
|
||||
resolveTextShadow(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
textShadow: '1px 2px 5px red'
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import resolveTransform from '../resolveTransform';
|
||||
|
||||
describe('apis/StyleSheet/resolveTransform', () => {
|
||||
// passthrough if transform value is ever a string
|
||||
test('transform string', () => {
|
||||
const resolvedStyle = {};
|
||||
const transform = 'perspective(50px) scaleX(20) translateX(20px) rotate(20deg)';
|
||||
|
||||
const style = { transform };
|
||||
resolveTransform(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({ transform });
|
||||
});
|
||||
|
||||
test('transform array', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = {
|
||||
transform: [{ perspective: 50 }, { scaleX: 20 }, { translateX: 20 }, { rotate: '20deg' }]
|
||||
};
|
||||
resolveTransform(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
transform: 'perspective(50px) scaleX(20) translateX(20px) rotate(20deg)'
|
||||
});
|
||||
});
|
||||
|
||||
test('transformMatrix', () => {
|
||||
const resolvedStyle = {};
|
||||
const style = { transformMatrix: [1, 2, 3, 4, 5, 6] };
|
||||
resolveTransform(resolvedStyle, style);
|
||||
|
||||
expect(resolvedStyle).toEqual({
|
||||
transform: 'matrix3d(1,2,3,4,5,6)'
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,226 @@
|
||||
import expandStyle from './expandStyle';
|
||||
import i18nStyle from './i18nStyle';
|
||||
/**
|
||||
* The browser implements the CSS cascade, where the order of properties is a
|
||||
* factor in determining which styles to paint. React Native is different in
|
||||
* giving precedence to the more specific styles. For example, the value of
|
||||
* `paddingTop` takes precedence over that of `padding`.
|
||||
*
|
||||
* This module creates mutally exclusive style declarations by expanding all of
|
||||
* React Native's supported shortform properties (e.g. `padding`) to their
|
||||
* longfrom equivalents.
|
||||
*/
|
||||
|
||||
const createReactDOMStyle = flattenedReactNativeStyle =>
|
||||
expandStyle(i18nStyle(flattenedReactNativeStyle));
|
||||
import normalizeValue from './normalizeValue';
|
||||
import processColor from '../../modules/processColor';
|
||||
|
||||
const emptyObject = {};
|
||||
const styleShortFormProperties = {
|
||||
borderColor: ['borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor'],
|
||||
borderRadius: [
|
||||
'borderTopLeftRadius',
|
||||
'borderTopRightRadius',
|
||||
'borderBottomRightRadius',
|
||||
'borderBottomLeftRadius'
|
||||
],
|
||||
borderStyle: ['borderTopStyle', 'borderRightStyle', 'borderBottomStyle', 'borderLeftStyle'],
|
||||
borderWidth: ['borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth'],
|
||||
margin: ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'],
|
||||
marginHorizontal: ['marginRight', 'marginLeft'],
|
||||
marginVertical: ['marginTop', 'marginBottom'],
|
||||
overflow: ['overflowX', 'overflowY'],
|
||||
padding: ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
|
||||
paddingHorizontal: ['paddingRight', 'paddingLeft'],
|
||||
paddingVertical: ['paddingTop', 'paddingBottom'],
|
||||
textDecorationLine: ['textDecoration'],
|
||||
writingDirection: ['direction']
|
||||
};
|
||||
|
||||
const colorProps = {
|
||||
backgroundColor: true,
|
||||
borderColor: true,
|
||||
borderTopColor: true,
|
||||
borderRightColor: true,
|
||||
borderBottomColor: true,
|
||||
borderLeftColor: true,
|
||||
color: true
|
||||
};
|
||||
|
||||
const alphaSortProps = propsArray =>
|
||||
propsArray.sort((a, b) => {
|
||||
if (a < b) {
|
||||
return -1;
|
||||
}
|
||||
if (a > b) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
const defaultOffset = { height: 0, width: 0 };
|
||||
|
||||
/**
|
||||
* Shadow
|
||||
*/
|
||||
|
||||
// TODO: add inset and spread support
|
||||
const resolveShadow = (resolvedStyle, style) => {
|
||||
const { height, width } = style.shadowOffset || defaultOffset;
|
||||
const offsetX = normalizeValue(null, width);
|
||||
const offsetY = normalizeValue(null, height);
|
||||
const blurRadius = normalizeValue(null, style.shadowRadius || 0);
|
||||
const color = processColor(style.shadowColor, style.shadowOpacity);
|
||||
|
||||
if (color) {
|
||||
const boxShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
|
||||
resolvedStyle.boxShadow = style.boxShadow ? `${style.boxShadow}, ${boxShadow}` : boxShadow;
|
||||
} else if (style.boxShadow) {
|
||||
resolvedStyle.boxShadow = style.boxShadow;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Text Shadow
|
||||
*/
|
||||
|
||||
const resolveTextShadow = (resolvedStyle, style) => {
|
||||
const { height, width } = style.textShadowOffset || defaultOffset;
|
||||
const offsetX = normalizeValue(null, width);
|
||||
const offsetY = normalizeValue(null, height);
|
||||
const blurRadius = normalizeValue(null, style.textShadowRadius || 0);
|
||||
const color = processColor(style.textShadowColor);
|
||||
|
||||
if (color) {
|
||||
resolvedStyle.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform
|
||||
*/
|
||||
|
||||
// { scale: 2 } => 'scale(2)'
|
||||
// { translateX: 20 } => 'translateX(20px)'
|
||||
const mapTransform = transform => {
|
||||
const type = Object.keys(transform)[0];
|
||||
const value = normalizeValue(type, transform[type]);
|
||||
return `${type}(${value})`;
|
||||
};
|
||||
|
||||
// [1,2,3,4,5,6] => 'matrix3d(1,2,3,4,5,6)'
|
||||
const convertTransformMatrix = transformMatrix => {
|
||||
const matrix = transformMatrix.join(',');
|
||||
return `matrix3d(${matrix})`;
|
||||
};
|
||||
|
||||
const resolveTransform = (resolvedStyle, style) => {
|
||||
let transform = style.transform;
|
||||
if (Array.isArray(style.transform)) {
|
||||
transform = style.transform.map(mapTransform).join(' ');
|
||||
} else if (style.transformMatrix) {
|
||||
transform = convertTransformMatrix(style.transformMatrix);
|
||||
}
|
||||
resolvedStyle.transform = transform;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reducer
|
||||
*/
|
||||
|
||||
const createReducer = (style, styleProps) => {
|
||||
let hasResolvedShadow = false;
|
||||
let hasResolvedTextShadow = false;
|
||||
|
||||
return (resolvedStyle, prop) => {
|
||||
const value = normalizeValue(prop, style[prop]);
|
||||
if (value == null) {
|
||||
return resolvedStyle;
|
||||
}
|
||||
|
||||
switch (prop) {
|
||||
case 'display': {
|
||||
resolvedStyle.display = value;
|
||||
// default of 'flexShrink:0' has lowest precedence
|
||||
if (style.display === 'flex' && style.flex == null && style.flexShrink == null) {
|
||||
resolvedStyle.flexShrink = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// ignore React Native styles
|
||||
case 'aspectRatio':
|
||||
case 'elevation':
|
||||
case 'overlayColor':
|
||||
case 'resizeMode':
|
||||
case 'tintColor': {
|
||||
break;
|
||||
}
|
||||
case 'flex': {
|
||||
resolvedStyle.flexGrow = value;
|
||||
resolvedStyle.flexShrink = 1;
|
||||
resolvedStyle.flexBasis = 'auto';
|
||||
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 'textShadowColor':
|
||||
case 'textShadowOffset':
|
||||
case 'textShadowRadius': {
|
||||
if (!hasResolvedTextShadow) {
|
||||
resolveTextShadow(resolvedStyle, style);
|
||||
}
|
||||
hasResolvedTextShadow = true;
|
||||
break;
|
||||
}
|
||||
case 'transform':
|
||||
case 'transformMatrix': {
|
||||
resolveTransform(resolvedStyle, style);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// normalize color values
|
||||
let finalValue = value;
|
||||
if (colorProps[prop]) {
|
||||
finalValue = processColor(value);
|
||||
}
|
||||
|
||||
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] = finalValue;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolvedStyle[prop] = finalValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedStyle;
|
||||
};
|
||||
};
|
||||
|
||||
const createReactDOMStyle = style => {
|
||||
if (!style) {
|
||||
return emptyObject;
|
||||
}
|
||||
const styleProps = Object.keys(style);
|
||||
const sortedStyleProps = alphaSortProps(styleProps);
|
||||
const reducer = createReducer(style, styleProps);
|
||||
const resolvedStyle = sortedStyleProps.reduce(reducer, {});
|
||||
return resolvedStyle;
|
||||
};
|
||||
|
||||
module.exports = createReactDOMStyle;
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
/**
|
||||
* The browser implements the CSS cascade, where the order of properties is a
|
||||
* factor in determining which styles to paint. React Native is different in
|
||||
* giving precedence to the more specific styles. For example, the value of
|
||||
* `paddingTop` takes precedence over that of `padding`.
|
||||
*
|
||||
* This module creates mutally exclusive style declarations by expanding all of
|
||||
* React Native's supported shortform properties (e.g. `padding`) to their
|
||||
* longfrom equivalents.
|
||||
*/
|
||||
|
||||
import normalizeValue from './normalizeValue';
|
||||
import processColor from '../../modules/processColor';
|
||||
import resolveBoxShadow from './resolveBoxShadow';
|
||||
import resolveTextShadow from './resolveTextShadow';
|
||||
import resolveTransform from './resolveTransform';
|
||||
|
||||
const emptyObject = {};
|
||||
const styleShortFormProperties = {
|
||||
borderColor: ['borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor'],
|
||||
borderRadius: [
|
||||
'borderTopLeftRadius',
|
||||
'borderTopRightRadius',
|
||||
'borderBottomRightRadius',
|
||||
'borderBottomLeftRadius'
|
||||
],
|
||||
borderStyle: ['borderTopStyle', 'borderRightStyle', 'borderBottomStyle', 'borderLeftStyle'],
|
||||
borderWidth: ['borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth'],
|
||||
margin: ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'],
|
||||
marginHorizontal: ['marginRight', 'marginLeft'],
|
||||
marginVertical: ['marginTop', 'marginBottom'],
|
||||
overflow: ['overflowX', 'overflowY'],
|
||||
padding: ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
|
||||
paddingHorizontal: ['paddingRight', 'paddingLeft'],
|
||||
paddingVertical: ['paddingTop', 'paddingBottom'],
|
||||
textDecorationLine: ['textDecoration'],
|
||||
writingDirection: ['direction']
|
||||
};
|
||||
|
||||
const colorProps = {
|
||||
backgroundColor: true,
|
||||
borderColor: true,
|
||||
borderTopColor: true,
|
||||
borderRightColor: true,
|
||||
borderBottomColor: true,
|
||||
borderLeftColor: true,
|
||||
color: true
|
||||
};
|
||||
|
||||
const alphaSortProps = propsArray =>
|
||||
propsArray.sort((a, b) => {
|
||||
if (a < b) {
|
||||
return -1;
|
||||
}
|
||||
if (a > b) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
const createReducer = (style, styleProps) => {
|
||||
let hasResolvedBoxShadow = false;
|
||||
let hasResolvedTextShadow = false;
|
||||
|
||||
return (resolvedStyle, prop) => {
|
||||
const value = normalizeValue(prop, style[prop]);
|
||||
if (value == null) {
|
||||
return resolvedStyle;
|
||||
}
|
||||
|
||||
switch (prop) {
|
||||
case 'display': {
|
||||
resolvedStyle.display = value;
|
||||
// default of 'flexShrink:0' has lowest precedence
|
||||
if (style.display === 'flex' && style.flex == null && style.flexShrink == null) {
|
||||
resolvedStyle.flexShrink = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// ignore React Native styles
|
||||
case 'aspectRatio':
|
||||
case 'elevation':
|
||||
case 'overlayColor':
|
||||
case 'resizeMode':
|
||||
case 'tintColor': {
|
||||
break;
|
||||
}
|
||||
case 'flex': {
|
||||
resolvedStyle.flexGrow = value;
|
||||
resolvedStyle.flexShrink = 1;
|
||||
resolvedStyle.flexBasis = 'auto';
|
||||
break;
|
||||
}
|
||||
case 'shadowColor':
|
||||
case 'shadowOffset':
|
||||
case 'shadowOpacity':
|
||||
case 'shadowRadius': {
|
||||
if (!hasResolvedBoxShadow) {
|
||||
resolveBoxShadow(resolvedStyle, style);
|
||||
}
|
||||
hasResolvedBoxShadow = true;
|
||||
break;
|
||||
}
|
||||
case 'textAlignVertical': {
|
||||
resolvedStyle.verticalAlign = value === 'center' ? 'middle' : value;
|
||||
break;
|
||||
}
|
||||
case 'textShadowColor':
|
||||
case 'textShadowOffset':
|
||||
case 'textShadowRadius': {
|
||||
if (!hasResolvedTextShadow) {
|
||||
resolveTextShadow(resolvedStyle, style);
|
||||
}
|
||||
hasResolvedTextShadow = true;
|
||||
break;
|
||||
}
|
||||
case 'transform': {
|
||||
resolveTransform(resolvedStyle, style);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// normalize color values
|
||||
let finalValue = value;
|
||||
if (colorProps[prop]) {
|
||||
finalValue = processColor(value);
|
||||
}
|
||||
|
||||
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] = finalValue;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolvedStyle[prop] = finalValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedStyle;
|
||||
};
|
||||
};
|
||||
|
||||
const expandStyle = style => {
|
||||
if (!style) {
|
||||
return emptyObject;
|
||||
}
|
||||
const styleProps = Object.keys(style);
|
||||
const sortedStyleProps = alphaSortProps(styleProps);
|
||||
const reducer = createReducer(style, styleProps);
|
||||
const resolvedStyle = sortedStyleProps.reduce(reducer, {});
|
||||
return resolvedStyle;
|
||||
};
|
||||
|
||||
module.exports = expandStyle;
|
||||
@@ -1,22 +0,0 @@
|
||||
import normalizeValue from './normalizeValue';
|
||||
import processColor from '../../modules/processColor';
|
||||
|
||||
const defaultOffset = { height: 0, width: 0 };
|
||||
|
||||
// TODO: add inset and spread support
|
||||
const resolveBoxShadow = (resolvedStyle, style) => {
|
||||
const { height, width } = style.shadowOffset || defaultOffset;
|
||||
const offsetX = normalizeValue(null, width);
|
||||
const offsetY = normalizeValue(null, height);
|
||||
const blurRadius = normalizeValue(null, style.shadowRadius || 0);
|
||||
const color = processColor(style.shadowColor, style.shadowOpacity);
|
||||
|
||||
if (color) {
|
||||
const boxShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
|
||||
resolvedStyle.boxShadow = style.boxShadow ? `${style.boxShadow}, ${boxShadow}` : boxShadow;
|
||||
} else if (style.boxShadow) {
|
||||
resolvedStyle.boxShadow = style.boxShadow;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = resolveBoxShadow;
|
||||
@@ -1,18 +0,0 @@
|
||||
import normalizeValue from './normalizeValue';
|
||||
import processColor from '../../modules/processColor';
|
||||
|
||||
const defaultOffset = { height: 0, width: 0 };
|
||||
|
||||
const resolveTextShadow = (resolvedStyle, style) => {
|
||||
const { height, width } = style.textShadowOffset || defaultOffset;
|
||||
const offsetX = normalizeValue(null, width);
|
||||
const offsetY = normalizeValue(null, height);
|
||||
const blurRadius = normalizeValue(null, style.textShadowRadius || 0);
|
||||
const color = processColor(style.textShadowColor);
|
||||
|
||||
if (color) {
|
||||
resolvedStyle.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = resolveTextShadow;
|
||||
@@ -1,27 +0,0 @@
|
||||
import normalizeValue from './normalizeValue';
|
||||
|
||||
// { scale: 2 } => 'scale(2)'
|
||||
// { translateX: 20 } => 'translateX(20px)'
|
||||
const mapTransform = transform => {
|
||||
const type = Object.keys(transform)[0];
|
||||
const value = normalizeValue(type, transform[type]);
|
||||
return `${type}(${value})`;
|
||||
};
|
||||
|
||||
// [1,2,3,4,5,6] => 'matrix3d(1,2,3,4,5,6)'
|
||||
const convertTransformMatrix = transformMatrix => {
|
||||
const matrix = transformMatrix.join(',');
|
||||
return `matrix3d(${matrix})`;
|
||||
};
|
||||
|
||||
const resolveTransform = (resolvedStyle, style) => {
|
||||
let transform = style.transform;
|
||||
if (Array.isArray(style.transform)) {
|
||||
transform = style.transform.map(mapTransform).join(' ');
|
||||
} else if (style.transformMatrix) {
|
||||
transform = convertTransformMatrix(style.transformMatrix);
|
||||
}
|
||||
resolvedStyle.transform = transform;
|
||||
};
|
||||
|
||||
module.exports = resolveTransform;
|
||||
Reference in New Issue
Block a user