Compare commits

..

21 Commits

Author SHA1 Message Date
Nicolas Gallagher
03769f7d45 0.0.93 2017-04-29 19:53:45 -07:00
Nicolas Gallagher
eb43a8f3e7 [fix] setNativeProps with RTL layout
Ensure that 'setNativeProps' doesn't try to i18n flip styles that have
already been flipped. This is hacked into the current design.
Registering both RTL and LTR styles is not implemented yet either.
2017-04-29 19:49:59 -07:00
Nicolas Gallagher
cdf13b880d 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'
2017-04-29 19:03:48 -07:00
Nicolas Gallagher
47fad1ef58 [fix] setNativeProps 2017-04-29 18:58:15 -07:00
Nicolas Gallagher
5f69c8e8b8 0.0.92 2017-04-29 12:19:16 -07:00
Nicolas Gallagher
21550db5f2 [fix] stop propagation of ScrollView 'onScroll' event
Fix #440
2017-04-29 12:15:19 -07:00
Nicolas Gallagher
1cae5d55a1 [fix] setNativeProps DOM style copying
The 'style' object of an HTML node is a 'CSSStyleDeclaration'. Use the
'CSSStyleDeclaration' API to copy the inline styles, rather than
treating it like a plain object. This avoids errors that were resulting
from indices and property names being used a key-value pairs in the
resulting style copy.

Fix #460
Ref #454
2017-04-29 11:09:43 -07:00
Nicolas Gallagher
11d23f850a 0.0.91 2017-04-28 15:40:12 -07:00
Nicolas Gallagher
d994a25017 0.0.90 2017-04-28 15:17:44 -07:00
Nicolas Gallagher
756df70154 [fix] check 'transform' style is array before mapping 2017-04-28 15:15:57 -07:00
Nicolas Gallagher
f0b06419f9 Move prefixStyles module 2017-04-27 16:27:45 -07:00
Nicolas Gallagher
60ff75705e [fix] remove stray 'length' property from style object
Fix #454
2017-04-27 15:10:03 -07:00
Nicolas Gallagher
5e8ad67296 0.0.89 2017-04-26 15:12:19 -07:00
Nathan Broadbent
ba24a882be Link to another starter kit example 2017-04-26 15:11:16 -07:00
Nicolas Gallagher
c7686209cd Update prettier 2017-04-26 15:07:44 -07:00
Nicolas Gallagher
f1b281ae32 Update debounce dependency 2017-04-26 15:06:41 -07:00
Nicolas Gallagher
b676fbd5e0 [fix] propTypes removal in production builds
Updates the relevant babel plugin, which now replaces component
propTypes with an empty object, avoiding the majority of potential
runtime errors related to this transform.

Fix #423
2017-04-26 11:05:33 -07:00
Nicolas Gallagher
51aef6c791 0.0.88 2017-04-24 13:28:38 -07:00
Nathan Leung
ae9a9cde5f Fix example webpack config in documentation 2017-04-24 13:21:46 -07:00
Nicolas Gallagher
beb907b180 Rename some variables in StyleRegistry 2017-04-24 13:21:27 -07:00
Nicolas Gallagher
a3362e1f38 [fix] setNativeProps inline styles
Inline styles are preserved when using 'setNativeProps'. Adds unit tests
for the resolution logic required by 'setNativeProps'/'resolveStateful'
in a DOM context.

Fix #439
2017-04-23 21:24:27 -07:00
30 changed files with 582 additions and 566 deletions

View File

@@ -144,6 +144,7 @@ AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-ro
* [react-native-web-player](https://github.com/dabbott/react-native-web-player)
* [reactxp](https://github.com/microsoft/reactxp)
* [react-web](https://github.com/taobaofed/react-web)
* [react-native-web-webpack](https://github.com/ndbroadbent/react-native-web-webpack)
## License

View File

@@ -61,7 +61,7 @@ module.exports = {
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
},
],
resolve: {
// Maps the 'react-native' import to 'react-native-web'.
@@ -76,6 +76,10 @@ module.exports = {
}
```
A more complex example setup for web apps can be found in various starter kits
(e.g., create-react-app and
[react-native-web-webpack](https://github.com/ndbroadbent/react-native-web-webpack))
Please refer to the Webpack documentation for more information.
## Jest

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-web",
"version": "0.0.87",
"version": "0.0.93",
"description": "React Native for Web",
"main": "dist/index.js",
"files": [
@@ -27,7 +27,7 @@
"array-find-index": "^1.0.2",
"babel-runtime": "^6.23.0",
"create-react-class": "^15.5.2",
"debounce": "^1.0.0",
"debounce": "1.0.2",
"deep-assign": "^2.0.0",
"fbjs": "^0.8.8",
"hyphenate-style-name": "^1.0.2",
@@ -42,19 +42,19 @@
"babel-core": "^6.24.1",
"babel-eslint": "^7.2.2",
"babel-loader": "^6.4.1",
"babel-plugin-transform-react-remove-prop-types": "^0.4.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.2",
"babel-preset-react-native": "^1.9.1",
"del-cli": "^0.2.1",
"enzyme": "^2.8.2",
"enzyme-to-json": "^1.5.1",
"eslint": "^3.19.0",
"eslint-config-prettier": "^1.6.0",
"eslint-config-prettier": "^1.7.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^6.10.3",
"file-loader": "^0.11.1",
"jest": "^19.0.2",
"node-libs-browser": "^0.5.3",
"prettier": "^1.1.0",
"prettier": "^1.2.2",
"react": "~15.4.1",
"react-addons-test-utils": "~15.4.1",
"react-dom": "~15.4.1",

View File

@@ -49,7 +49,7 @@ module.exports = {
],
resolve: {
alias: {
'react-native': path.join(__dirname, '../src/module')
'react-native': path.join(__dirname, '../src')
}
}
};

View File

@@ -299,7 +299,8 @@ var PanResponder = {
PanResponder._initializeGestureState(gestureState);
}
} else if (
e.nativeEvent.originalEvent && e.nativeEvent.originalEvent.type === 'mousedown'
e.nativeEvent.originalEvent &&
e.nativeEvent.originalEvent.type === 'mousedown'
) {
PanResponder._initializeGestureState(gestureState);
}

View File

@@ -6,8 +6,9 @@ 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 './prefixInlineStyles';
import { prefixInlineStyles } from '../../modules/prefixStyles';
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
import StyleManager from './StyleManager';
@@ -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);
@@ -48,7 +49,7 @@ class StyleRegistry {
/**
* Resolves a React Native style object to DOM attributes
*/
resolve(reactNativeStyle) {
resolve(reactNativeStyle, options) {
if (!reactNativeStyle) {
return undefined;
}
@@ -56,12 +57,12 @@ class StyleRegistry {
// fast and cachable
if (typeof reactNativeStyle === 'number') {
const key = createCacheKey(reactNativeStyle);
return this._resolveStyleIfNeeded(key, reactNativeStyle);
return this._resolveStyleIfNeeded(reactNativeStyle, { key, ...options });
}
// resolve a plain RN style object
if (!Array.isArray(reactNativeStyle)) {
return this._resolveStyle(reactNativeStyle);
return this._resolveStyle(reactNativeStyle, options);
}
// flatten the style array
@@ -76,49 +77,64 @@ class StyleRegistry {
}
}
const key = isArrayOfNumbers ? createCacheKey(flatArray.join('-')) : null;
return this._resolveStyleIfNeeded(key, flatArray);
return this._resolveStyleIfNeeded(flatArray, { key, ...options });
}
/**
* Resolves a React Native style object to DOM attributes, accounting for
* the existing styles applied to the DOM node
* the existing styles applied to the DOM node.
*
* To determine the next style, some of the existing DOM state must be
* converted back into React Native styles.
*/
resolveStateful(reactNativeStyle, domClassList) {
const previousReactNativeStyle = {};
const preservedClassNames = [];
resolveStateful(rnStyleNext, domStyleProps, options) {
const { classList: rdomClassList, style: rdomStyle } = domStyleProps;
// Convert the existing classList to a React Native style and preserve any
// unrecognized classNames.
domClassList.forEach(className => {
const { prop, value } = this.styleManager.getDeclaration(className);
if (prop) {
previousReactNativeStyle[prop] = value;
} else {
preservedClassNames.push(className);
// Convert the DOM classList back into a React Native form
// Preserves unrecognized class names.
const { classList: rnClassList, style: rnStyle } = rdomClassList.reduce(
(styleProps, className) => {
const { prop, value } = this.styleManager.getDeclaration(className);
if (prop) {
styleProps.style[prop] = value;
} else {
styleProps.classList.push(className);
}
return styleProps;
},
{ classList: [], style: {} }
);
// Create next DOM style props from current and next RN styles
const { classList: rdomClassListNext, style: rdomStyleNext } = this.resolve(
[rnStyle, rnStyleNext],
options
);
// Next class names take priority over current inline styles
const style = { ...rdomStyle };
rdomClassListNext.forEach(className => {
const { prop } = this.styleManager.getDeclaration(className);
if (style[prop]) {
style[prop] = '';
}
});
// Resolve the two React Native styles.
const { classList, style = {} } = this.resolve([previousReactNativeStyle, reactNativeStyle]);
// Next inline styles take priority over current inline styles
Object.assign(style, rdomStyleNext);
// Because this is used in stateful operations we need to remove any
// existing inline styles that would override the classNames.
classList.forEach(className => {
const { prop } = this.styleManager.getDeclaration(className);
style[prop] = null;
});
// Add the current class names not managed by React Native
const className = classListToString(rdomClassListNext.concat(rnClassList));
classList.push(preservedClassNames);
const className = classListToString(classList);
return { className, style };
}
/**
* Resolves a React Native style object
*/
_resolveStyle(reactNativeStyle) {
const domStyle = createReactDOMStyle(flattenStyle(reactNativeStyle));
_resolveStyle(reactNativeStyle, options) {
const flatStyle = flattenStyle(reactNativeStyle);
const domStyle = createReactDOMStyle(options.i18n === false ? flatStyle : i18nStyle(flatStyle));
const props = Object.keys(domStyle).reduce(
(props, styleProp) => {
@@ -150,15 +166,15 @@ class StyleRegistry {
/**
* Caching layer over 'resolveStyle'
*/
_resolveStyleIfNeeded(key, style) {
_resolveStyleIfNeeded(style, { key, ...rest }) {
if (key) {
if (!this.cache[key]) {
// slow: convert style object to props and cache
this.cache[key] = this._resolveStyle(style);
this.cache[key] = this._resolveStyle(style, rest);
}
return this.cache[key];
}
return this._resolveStyle(style);
return this._resolveStyle(style, rest);
}
}

View File

@@ -1,5 +1,6 @@
/* eslint-env jasmine, jest */
import I18nManager from '../../I18nManager';
import StyleRegistry from '../StyleRegistry';
let styleRegistry;
@@ -46,6 +47,16 @@ describe('apis/StyleSheet/StyleRegistry', () => {
testResolve(a, b, c);
});
test('with register before RTL, resolves to className', () => {
const a = styleRegistry.register({ left: '12.34%' });
const b = styleRegistry.register({ textAlign: 'left' });
const c = styleRegistry.register({ marginLeft: 10 });
I18nManager.forceRTL(true);
const resolved = styleRegistry.resolve([a, b, c]);
I18nManager.forceRTL(false);
expect(resolved).toMatchSnapshot();
});
test('with register, resolves to mixed', () => {
const a = styleA;
const b = styleRegistry.register(styleB);
@@ -58,19 +69,37 @@ describe('apis/StyleSheet/StyleRegistry', () => {
});
});
test('resolveStateful', () => {
// generate a classList to act as pre-existing DOM state
const mockStyle = styleRegistry.register({
borderWidth: 0,
borderColor: 'red',
width: 100
describe('resolveStateful', () => {
test('preserves unrelated class names', () => {
const domStyleProps = { classList: ['unknown-class-1', 'unknown-class-2'], style: {} };
const domStyleNextProps = styleRegistry.resolveStateful({}, domStyleProps);
expect(domStyleNextProps).toMatchSnapshot();
});
const { classList: domClassList } = styleRegistry.resolve(mockStyle);
domClassList.unshift('external-className');
expect(domClassList).toMatchSnapshot();
// test result
const result = styleRegistry.resolveStateful({ borderWidth: 1, opacity: 1 }, domClassList);
expect(result).toMatchSnapshot();
test('preserves unrelated inline styles', () => {
const domStyleProps = { classList: [], style: { fontSize: '20px' } };
const domStyleNextProps = styleRegistry.resolveStateful({ opacity: 1 }, domStyleProps);
expect(domStyleNextProps).toMatchSnapshot();
});
test('next class names have priority over current inline styles', () => {
const domStyleProps = { classList: [], style: { opacity: 0.5 } };
const nextStyle = styleRegistry.register({ opacity: 1 });
const domStyleNextProps = styleRegistry.resolveStateful(nextStyle, domStyleProps);
expect(domStyleNextProps).toMatchSnapshot();
});
test('next inline styles have priority over current inline styles', () => {
// note: this also checks for correctly uppercasing the first letter of DOM vendor prefixes
const domStyleProps = {
classList: [],
style: { opacity: 0.5, WebkitTransform: 'scale(1)', transform: 'scale(1)' }
};
const domStyleNextProps = styleRegistry.resolveStateful(
{ opacity: 1, transform: [{ scale: 2 }] },
domStyleProps
);
expect(domStyleNextProps).toMatchSnapshot();
});
});
});

View File

@@ -1,5 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/StyleRegistry resolve with register before RTL, resolves to className 1`] = `
Object {
"classList": Array [],
"className": "",
"style": Object {
"marginRight": "10px",
"right": "12.34%",
"textAlign": "right",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to className 1`] = `
Object {
"classList": Array [
@@ -192,35 +204,39 @@ Object {
}
`;
exports[`apis/StyleSheet/StyleRegistry resolveStateful 1`] = `
Array [
"external-className",
"rn-borderTopColor-1gxhl28",
"rn-borderRightColor-knoah9",
"rn-borderBottomColor-1ani3fp",
"rn-borderLeftColor-ribj9x",
"rn-borderTopWidth-13yce4e",
"rn-borderRightWidth-fnigne",
"rn-borderBottomWidth-ndvcnb",
"rn-borderLeftWidth-gxnn5r",
"rn-width-b8lwoo",
]
`;
exports[`apis/StyleSheet/StyleRegistry resolveStateful 2`] = `
exports[`apis/StyleSheet/StyleRegistry resolveStateful next class names have priority over current inline styles 1`] = `
Object {
"className": "rn-borderBottomColor-1ani3fp rn-borderBottomWidth-ndvcnb rn-borderLeftColor-ribj9x rn-borderLeftWidth-gxnn5r rn-borderRightColor-knoah9 rn-borderRightWidth-fnigne rn-borderTopColor-1gxhl28 rn-borderTopWidth-13yce4e rn-width-b8lwoo external-className",
"className": "rn-opacity-6dt33c",
"style": Object {
"borderBottomColor": null,
"borderBottomWidth": null,
"borderLeftColor": null,
"borderLeftWidth": null,
"borderRightColor": null,
"borderRightWidth": null,
"borderTopColor": null,
"borderTopWidth": null,
"opacity": 1,
"width": null,
"opacity": "",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolveStateful next inline styles have priority over current inline styles 1`] = `
Object {
"className": "",
"style": Object {
"WebkitTransform": "scale(2)",
"opacity": 1,
"transform": "scale(2)",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolveStateful preserves unrelated class names 1`] = `
Object {
"className": "unknown-class-1 unknown-class-2",
"style": Object {},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolveStateful preserves unrelated inline styles 1`] = `
Object {
"className": "",
"style": Object {
"fontSize": "20px",
"opacity": 1,
},
}
`;

View File

@@ -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",
}
`;

View File

@@ -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",
}
`;

View File

@@ -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)'
});
});
});
});

View File

@@ -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'
});
});
});

View File

@@ -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)'
});
});
});

View File

@@ -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'
});
});
});

View File

@@ -1,27 +0,0 @@
/* eslint-env jasmine, jest */
import resolveTransform from '../resolveTransform';
describe('apis/StyleSheet/resolveTransform', () => {
test('transform', () => {
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)'
});
});
});

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,7 +1,7 @@
import hyphenateStyleName from 'hyphenate-style-name';
import mapKeyValue from '../../modules/mapKeyValue';
import normalizeValue from './normalizeValue';
import prefixAll from 'inline-style-prefixer/static';
import prefixStyles from '../../modules/prefixStyles';
const createDeclarationString = (prop, val) => {
const name = hyphenateStyleName(prop);
@@ -19,6 +19,6 @@ const createDeclarationString = (prop, val) => {
* // => 'color:blue;width:20px'
*/
const generateCss = style =>
mapKeyValue(prefixAll(style), createDeclarationString).sort().join(';');
mapKeyValue(prefixStyles(style), createDeclarationString).sort().join(';');
module.exports = generateCss;

View File

@@ -71,15 +71,17 @@ const i18nStyle = originalStyle => {
continue;
}
const value = style[prop];
if (PROPERTIES_TO_SWAP[prop]) {
const newProp = flipProperty(prop);
nextStyle[newProp] = style[prop];
nextStyle[newProp] = value;
} else if (PROPERTIES_SWAP_LEFT_RIGHT[prop]) {
nextStyle[prop] = swapLeftRight(style[prop]);
nextStyle[prop] = swapLeftRight(value);
} else if (prop === 'textShadowOffset') {
nextStyle[prop] = style[prop];
nextStyle[prop].width = additiveInverse(style[prop].width);
} else if (prop === 'transform') {
nextStyle[prop] = value;
nextStyle[prop].width = additiveInverse(value.width);
} else if (prop === 'transform' && Array.isArray(value)) {
nextStyle[prop] = style[prop].map(flipTransform);
} else {
nextStyle[prop] = style[prop];

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) => {
if (Array.isArray(style.transform)) {
const transform = style.transform.map(mapTransform).join(' ');
resolvedStyle.transform = transform;
} else if (style.transformMatrix) {
const transform = convertTransformMatrix(style.transformMatrix);
resolvedStyle.transform = transform;
}
};
module.exports = resolveTransform;

View File

@@ -206,7 +206,8 @@ class ListView extends Component {
totalIndex++;
if (
renderSeparator && (rowIdx !== rowIDs.length - 1 || sectionIdx === allRowIDs.length - 1)
renderSeparator &&
(rowIdx !== rowIDs.length - 1 || sectionIdx === allRowIDs.length - 1)
) {
const adjacentRowHighlighted =
this.state.highlightedRow.sectionID === sectionID &&

View File

@@ -87,6 +87,7 @@ export default class ScrollViewBase extends Component {
_handleScroll = e => {
e.persist();
e.stopPropagation();
const { scrollEventThrottle } = this.props;
// A scroll happened, so the scroll bumps the debounce.
this._debouncedOnScrollEnd(e);

View File

@@ -8,9 +8,13 @@
import createDOMProps from '../createDOMProps';
import findNodeHandle from '../findNodeHandle';
import i18nStyle from '../../apis/StyleSheet/i18nStyle';
import StyleRegistry from '../../apis/StyleSheet/registry';
import UIManager from '../../apis/UIManager';
const hyphenPattern = /-([a-z])/g;
const toCamelCase = str => str.replace(hyphenPattern, m => m[1].toUpperCase());
const NativeMethodsMixin = {
/**
* Removes focus from an input or view. This is the opposite of `focus()`.
@@ -67,12 +71,26 @@ const NativeMethodsMixin = {
* the initial styles from the DOM node and merge them with incoming props.
*/
setNativeProps(nativeProps: Object) {
// DOM state
// Copy of existing DOM state
const node = findNodeHandle(this);
const nodeStyle = node.style;
const classList = Array.prototype.slice.call(node.classList);
const style = {};
// DOM style is a CSSStyleDeclaration
// https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
for (let i = 0; i < node.style.length; i += 1) {
const property = nodeStyle.item(i);
if (property) {
// DOM style uses hyphenated prop names and may include vendor prefixes
// Transform back into React DOM style.
style[toCamelCase(property)] = nodeStyle.getPropertyValue(property);
}
}
const domStyleProps = { classList, style };
const domProps = createDOMProps(nativeProps, style =>
StyleRegistry.resolveStateful(style, classList)
// Next DOM state
const domProps = createDOMProps(i18nStyle(nativeProps), style =>
StyleRegistry.resolveStateful(style, domStyleProps, { i18n: false })
);
UIManager.updateView(node, domProps, this);
}

View File

@@ -75,7 +75,10 @@ const applyLayout = Component => {
if (!this._isMounted) return;
if (
layout.x !== x || layout.y !== y || layout.width !== width || layout.height !== height
layout.x !== x ||
layout.y !== y ||
layout.width !== width ||
layout.height !== height
) {
this._layoutState = { x, y, width, height };
const nativeEvent = { layout: this._layoutState };

View File

@@ -1,9 +1,9 @@
/* eslint-env jasmine, jest */
import prefixInlineStyles from '../prefixInlineStyles';
import { prefixInlineStyles } from '..';
describe('apis/StyleSheet/prefixInlineStyles', () => {
test('handles array values', () => {
describe('modules/prefixStyles', () => {
test('handles array values for inline styles', () => {
const style = {
display: ['-webkit-flex', 'flex']
};

View File

@@ -1,6 +1,8 @@
import prefixAll from 'inline-style-prefixer/static';
const prefixInlineStyles = style => {
export default prefixAll;
export const prefixInlineStyles = style => {
const prefixedStyles = prefixAll(style);
// React@15 removed undocumented support for fallback values in
@@ -14,5 +16,3 @@ const prefixInlineStyles = style => {
return prefixedStyles;
};
module.exports = prefixInlineStyles;

View File

@@ -158,7 +158,9 @@ function dangerousStyleValue(name, value, component) {
var isNonNumeric = isNaN(value);
if (
isNonNumeric || value === 0 || (unitlessNumbers.hasOwnProperty(name) && unitlessNumbers[name])
isNonNumeric ||
value === 0 ||
(unitlessNumbers.hasOwnProperty(name) && unitlessNumbers[name])
) {
return '' + value; // cast to string
}

View File

@@ -931,9 +931,9 @@ babel-plugin-transform-react-jsx@^6.3.13, babel-plugin-transform-react-jsx@^6.5.
babel-plugin-syntax-jsx "^6.8.0"
babel-runtime "^6.0.0"
babel-plugin-transform-react-remove-prop-types@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.0.tgz#f63840e7953563d661be8c647b094d74d7363f17"
babel-plugin-transform-react-remove-prop-types@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.2.tgz#97e3cab4d05938df91e16c0473737ef6c575a2e6"
babel-plugin-transform-regenerator@6.16.1:
version "6.16.1"
@@ -1917,19 +1917,13 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
date-now@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-1.0.1.tgz#bb7d086438debe4182a485fb3df3fbfb99d6153c"
date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
debounce:
version "1.0.0"
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.0.0.tgz#0948af513d2e4ce407916f8506a423d3f9cf72d8"
dependencies:
date-now "1.0.1"
debounce@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.0.2.tgz#503cc674d8d7f737099664fb75ddbd36b9626dc6"
debug@2.6.1:
version "2.6.1"
@@ -2330,9 +2324,9 @@ escope@^3.6.0:
esrecurse "^4.1.0"
estraverse "^4.1.1"
eslint-config-prettier@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-1.6.0.tgz#56e53a8eb461c06eced20cec40d765c185100fd5"
eslint-config-prettier@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-1.7.0.tgz#cda3ce22df1e852daa9370f1f3446e8b8a02ce44"
dependencies:
get-stdin "^5.0.1"
@@ -5012,9 +5006,9 @@ preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
prettier@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.1.0.tgz#9d6ad005703efefa66b6999b8916bfc6afeaf9f8"
prettier@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.2.2.tgz#22d17c1132faaaea1f1d4faea31f19f7a1959f3e"
dependencies:
ast-types "0.9.8"
babel-code-frame "6.22.0"