Compare commits

...

28 Commits

Author SHA1 Message Date
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
Nicolas Gallagher
64d2d34367 0.0.87 2017-04-23 13:39:27 -07:00
Nicolas Gallagher
d83cd45b6f [fix] Clipboard browser support
Safari 10.3 supports copying (but apparently not from inputs)
2017-04-23 13:38:51 -07:00
Nicolas Gallagher
227971d22c [add] support for CSS grid properties (experimental)
Allow people to experiment with using CSS grid in react-native. (No
support for shorthand properties.)
2017-04-22 10:34:43 -07:00
Nicolas Gallagher
4822cf4620 [add] support for 'clip' and 'textIndent' styles 2017-04-22 10:06:27 -07:00
Nathan Broadbent
91e4528eac Allow filter property in CSS 2017-04-22 22:56:07 +07:00
Nicolas Gallagher
1ee64d8285 0.0.86 2017-04-21 19:04:23 -07:00
Nicolas Gallagher
66a4c13bf3 [fix] AppState.isSupported -> AppState.isAvailable
React Native exposes AppState support via 'isAvailable'.
2017-04-21 18:47:29 -07:00
Nicolas Gallagher
9012e98ba7 [fix] support 'mailto:' URLs in 'Linking' 2017-04-21 18:29:29 -07:00
Nicolas Gallagher
046e01dfa9 [fix] add AsyncStorage callbacks and tests
Add support for the callback interface and add test coverage.

Fix #399
Close #400
2017-04-21 18:13:14 -07:00
Nicolas Gallagher
6e71e1e058 [fix] attempt to avoid need for 'Array.from' polyfill
Fix #409
2017-04-20 18:04:09 -07:00
Nicolas Gallagher
d5a9f3e779 Add ES module export
Preparation for publishing an ES module build.
Move 'modality' into 'createDOMElement' to ensure it is always initialized.
2017-04-20 17:16:05 -07:00
Nicolas Gallagher
f16f5f21ce [add] WebkitMaskImage style prop
Undocumented supported. Commonly used in border-radius hacks.

Close #326
2017-04-20 15:12:07 -07:00
Nicolas Gallagher
0bb7e67e63 0.0.85 2017-04-20 15:07:56 -07:00
Nicolas Gallagher
c6b54930b6 [add] StatusBar stub component
Fix #425
2017-04-20 15:07:34 -07:00
Nicolas Gallagher
599f1fcaf5 Filter unsupported TextInput props
Fix #385
2017-04-20 14:58:48 -07:00
Nicolas Gallagher
3f7a4e455f [add] support 'outlineColor' style prop
Fix #435
2017-04-20 14:50:14 -07:00
Nicolas Gallagher
1f3e9cc6ee [change] ScrollView as new surface
Fix #405
2017-04-20 13:36:00 -07:00
Nicolas Gallagher
17ed63129f Add a note about accessibilityRole compat 2017-04-20 10:39:34 -07:00
Nicolas Gallagher
769334d04e Update benchmark results 2017-04-20 10:04:33 -07:00
34 changed files with 780 additions and 321 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

@@ -85,6 +85,7 @@ When `false`, the text is not selectable.
+ `textAlign`
+ `textAlignVertical`
+ `textDecorationLine`
+ `textIndent`
+ `textOverflow`
+ `textRendering`
+ `textShadowColor`

View File

@@ -186,16 +186,30 @@ Controls whether the View can be the target of touch events. The enhanced
+ `borderRightWidth`
+ `borderTopWidth`
+ `bottom`
+ `boxShadow`
+ `boxShadow`
+ `boxSizing`
+ `clip`
+ `cursor`
+ `display`
+ `filter`
+ `flex` (number)
+ `flexBasis`
+ `flexDirection`
+ `flexGrow`
+ `flexShrink`
+ `flexWrap`
+ `gridAutoColumns`
+ `gridAutoFlow`
+ `gridAutoRows`
+ `gridColumnEnd`
+ `gridColumnGap`
+ `gridColumnStart`
+ `gridRowEnd`
+ `gridRowGap`
+ `gridRowStart`
+ `gridTemplateColumns`
+ `gridTemplateRows`
+ `gridTemplateAreas`
+ `height`
+ `justifyContent`
+ `left`
@@ -213,6 +227,7 @@ Controls whether the View can be the target of touch events. The enhanced
+ `opacity`
+ `order`
+ `outline`
+ `outlineColor`
+ `overflow`
+ `overflowX`
+ `overflowY`
@@ -227,6 +242,10 @@ Controls whether the View can be the target of touch events. The enhanced
+ `perspectiveOrigin`
+ `position`
+ `right`
+ `shadowColor`
+ `shadowOffset`
+ `shadowOpacity`
+ `shadowRadius`
+ `top`
+ `transform`
+ `transformOrigin`

View File

@@ -39,7 +39,10 @@ using `aria-label`.
In some cases, we also want to alert the end user of the type of selected
component (i.e., that it is a “button”). To provide more context to screen
readers, you should specify the `accessibilityRole` property.
readers, you should specify the `accessibilityRole` property. (Note that React
Native for Web provides a compatibility mapping of equivalent
`accessibilityTraits` and `accessibilityComponentType` values to
`accessibilityRole`).
The `accessibilityRole` prop is used to infer an [analogous HTML
element][html-aria-url] to use in addition to the resulting ARIA `role`, where

View File

@@ -1,5 +1,8 @@
# Getting started
It is recommended that your application provide a `Promise` and `Array.from`
polyfill.
## Webpack and Babel
[Webpack](webpack.js.org) is a popular build tool for web apps. Below is an
@@ -58,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'.
@@ -73,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

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

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-web",
"version": "0.0.84",
"version": "0.0.89",
"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

@@ -22,18 +22,19 @@ Typical render timings*: mean ± two standard deviations
| Implementation | Deep tree (ms) | Wide tree (ms) | Tweets (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `87.68` `±13.29` | `171.96` `±14.91` | |
| `react-native-web/stylesheet@0.0.81` | `90.59` `±12.03` | `190.37` `±19.65` | |
| `react-native-web@0.0.81` | `105.20` `±17.86` | `226.54` 29.50` | `12.12` 6.94ms` |
| `css-modules` | `87.67` `±15.22` | `170.85` `±16.87` | |
| `react-native-web/stylesheet@0.0.84` | `90.02` `±13.16` | `186.66` `±19.23` | |
| `react-native-web@0.0.84` | `102.72` `±19.26` | `222.35` 18.95` | `12.81` 5.45ms` |
Other libraries
| Implementation | Deep tree (ms) | Wide tree (ms) |
| :--- | ---: | ---: |
| `aphrodite@1.2.0` | `101.25` `±18.78` | `224.59` 22.28` |
| `glamor@3.0.0-1` | `143.39` `±23.05` | `275.21` 21.10` |
| `react-jss@5.4.1` | `142.27` `±16.62` | `318.62` `±26.19` |
| `reactxp@0.34.3` | `221.36` `±23.35` | `472.61` 40.86` |
| `styled-components@2.0.0-7` | `301.92` `±39.43` | `647.80` 102.1` |
| `styletron@2.5.1` | `88.48` `±12.00` | `171.89` 13.28` |
| `aphrodite@1.2.0` | `101.32` `±20.33` | `220.33` 31.41` |
| `glamor@3.0.0-1` | `129.00` `±14.92` | `264.57` `±28.54` |
| `react-jss@5.4.1` | `137.33` `±21.55` | `314.91` 29.03` |
| `reactxp@0.34.3` | `223.82` `±32.77` | `461.56` 34.43` |
| `styled-components@2.0.0-11` | `277.53` `±28.83` | `627.91` `±43.13` |
*MacBook Pro (13-inch, Early 2011); 2.7 GHz Intel Core i7; 16 GB 1600 MHz DDR3. Google Chrome 56.

View File

@@ -6,7 +6,9 @@ import React from 'react';
export const styletron = new Styletron();
const View = ({ style, ...other }) => <div {...other} className={classnames(viewStyle, ...style)} />;
const View = ({ style, ...other }) => (
<div {...other} className={classnames(viewStyle, ...style)} />
);
const viewStyle = injectStylePrefixed(styletron, {
alignItems: 'stretch',

View File

@@ -13,10 +13,10 @@ const AppStates = {
const listeners = [];
class AppState {
static isSupported = ExecutionEnvironment.canUseDOM && document.visibilityState;
static isAvailable = ExecutionEnvironment.canUseDOM && document.visibilityState;
static get currentState() {
if (!AppState.isSupported) {
if (!AppState.isAvailable) {
return AppState.ACTIVE;
}
@@ -31,7 +31,7 @@ class AppState {
}
static addEventListener(type: string, handler: Function) {
if (AppState.isSupported) {
if (AppState.isAvailable) {
invariant(
EVENT_TYPES.indexOf(type) !== -1,
'Trying to subscribe to unknown event: "%s"',
@@ -44,7 +44,7 @@ class AppState {
}
static removeEventListener(type: string, handler: Function) {
if (AppState.isSupported) {
if (AppState.isAvailable) {
invariant(
EVENT_TYPES.indexOf(type) !== -1,
'Trying to remove listener for unknown event: "%s"',

View File

@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/AsyncStorage mergeLocalStorageItem should have same behavior as react-native 1`] = `
exports[`apis/AsyncStorage mergeItem calls callback after setting item 1`] = `
Object {
"age": 31,
"name": "Chris",
@@ -11,3 +11,63 @@ Object {
},
}
`;
exports[`apis/AsyncStorage mergeItem promise after setting item 1`] = `
Object {
"age": 31,
"name": "Chris",
"traits": Object {
"eyes": "blue",
"hair": "brown",
"shoe_size": 10,
},
}
`;
exports[`apis/AsyncStorage multiMerge calls callback after setting items 1`] = `
Object {
"age": 31,
"name": "Chris",
"traits": Object {
"eyes": "blue",
"hair": "brown",
"shoe_size": 10,
},
}
`;
exports[`apis/AsyncStorage multiMerge calls callback after setting items 2`] = `
Object {
"age": 31,
"name": "Amy",
"traits": Object {
"eyes": "blue",
"hair": "black",
"shoe_size": 10,
},
}
`;
exports[`apis/AsyncStorage multiMerge promise after setting items 1`] = `
Object {
"age": 31,
"name": "Chris",
"traits": Object {
"eyes": "blue",
"hair": "brown",
"shoe_size": 10,
},
}
`;
exports[`apis/AsyncStorage multiMerge promise after setting items 2`] = `
Object {
"age": 31,
"name": "Amy",
"traits": Object {
"eyes": "blue",
"hair": "black",
"shoe_size": 10,
},
}
`;

View File

@@ -1,77 +1,275 @@
/* eslint-env jasmine, jest */
import AsyncStorage from '..';
const waterfall = (fns, cb) => {
const _waterfall = (...args) => {
const fn = (fns || []).shift();
if (typeof fn === 'function') {
fn(...args, (err, ...nextArgs) => {
if (err) {
return cb(err);
} else {
return _waterfall(...nextArgs);
}
});
} else {
cb(null, ...args);
}
};
_waterfall();
};
const originalLocalStorage = window.localStorage;
const obj = {};
let obj = {};
const mockLocalStorage = {
length: 0,
clear() {
obj = {};
mockLocalStorage.length = 0;
},
getItem(key) {
return obj[key];
},
key(index) {
return Object.keys(obj)[index];
},
removeItem(key) {
delete obj[key];
mockLocalStorage.length -= 1;
},
setItem(key, value) {
obj[key] = value;
mockLocalStorage.length += 1;
}
};
const originalLocalStorage = window.localStorage;
const uid123Object = {
name: 'Chris',
age: 30,
traits: { hair: 'brown', eyes: 'green' }
};
const uid123Delta = {
age: 31,
traits: { eyes: 'blue', shoe_size: 10 }
};
const uid124Object = {
name: 'Amy',
age: 28,
traits: { hair: 'black', eyes: 'brown' }
};
describe('apis/AsyncStorage', () => {
describe('mergeLocalStorageItem', () => {
test('should have same behavior as react-native', done => {
window.localStorage = mockLocalStorage;
// https://facebook.github.io/react-native/docs/asyncstorage.html
const UID123_object = {
name: 'Chris',
age: 30,
traits: { hair: 'brown', eyes: 'brown' }
};
const UID123_delta = {
age: 31,
traits: { eyes: 'blue', shoe_size: 10 }
};
beforeEach(() => {
mockLocalStorage.setItem('UID123', JSON.stringify(uid123Object));
mockLocalStorage.setItem('UID124', JSON.stringify(uid124Object));
window.localStorage = mockLocalStorage;
});
waterfall(
[
cb => {
AsyncStorage.setItem('UID123', JSON.stringify(UID123_object))
.then(() => cb(null))
.catch(cb);
},
cb => {
AsyncStorage.mergeItem('UID123', JSON.stringify(UID123_delta))
.then(() => cb(null))
.catch(cb);
},
cb => {
AsyncStorage.getItem('UID123')
.then(result => {
cb(null, JSON.parse(result));
})
.catch(cb);
}
],
afterEach(() => {
mockLocalStorage.clear();
window.localStorage = originalLocalStorage;
});
describe('clear', () => {
const assertResult = () => {
expect(mockLocalStorage.length).toEqual(0);
};
test('promise of erased keys', () => {
expect(mockLocalStorage.length).toEqual(2);
return AsyncStorage.clear().then(() => {
assertResult();
});
});
test('calls callback after erasing keys', done => {
expect(mockLocalStorage.length).toEqual(2);
AsyncStorage.clear(err => {
expect(err).toEqual(null);
assertResult();
done();
});
});
});
describe('getAllKeys', () => {
const assertResult = result => {
expect(result).toEqual(['UID123', 'UID124']);
};
test('promise of keys', () => {
return AsyncStorage.getAllKeys().then(result => {
assertResult(result);
});
});
test('calls callback with keys', done => {
AsyncStorage.getAllKeys((err, result) => {
expect(err).toEqual(null);
assertResult(result);
done();
});
});
});
describe('getItem', () => {
const assertResult = result => {
expect(result).toEqual(JSON.stringify(uid123Object));
};
test('promise of item', () => {
return AsyncStorage.getItem('UID123').then(result => {
assertResult(result);
});
});
test('calls callback with item', done => {
AsyncStorage.getItem('UID123', (err, result) => {
expect(err).toEqual(null);
assertResult(result);
done();
});
});
});
describe('multiGet', () => {
const assertResult = result => {
expect(result).toEqual([
['UID123', JSON.stringify(uid123Object)],
['UID124', JSON.stringify(uid124Object)]
]);
};
test('promise of items', () => {
return AsyncStorage.multiGet(['UID123', 'UID124']).then(result => {
assertResult(result);
});
});
test('calls callback with items', done => {
AsyncStorage.multiGet(['UID123', 'UID124'], (err, result) => {
expect(err).toEqual(null);
assertResult(result);
done();
});
});
});
describe('setItem', () => {
const assertResult = () => {
expect(mockLocalStorage.getItem('UID123')).toEqual(JSON.stringify(uid123Object));
};
test('promise after setting item', () => {
return AsyncStorage.setItem('UID123', JSON.stringify(uid123Object)).then(() => {
assertResult();
});
});
test('calls callback after setting item', done => {
AsyncStorage.setItem('UID123', JSON.stringify(uid123Object), (err, result) => {
expect(err).toEqual(null);
assertResult();
done();
});
});
});
describe('multiSet', () => {
const assertResult = result => {
expect(mockLocalStorage.getItem('UID123')).toEqual(JSON.stringify(uid123Object));
expect(mockLocalStorage.getItem('UID124')).toEqual(JSON.stringify(uid124Object));
};
test('promise after setting items', () => {
return AsyncStorage.multiSet([
['UID123', JSON.stringify(uid123Object)],
['UID124', JSON.stringify(uid124Object)]
]).then(() => {
assertResult();
});
});
test('calls callback after setting items', done => {
AsyncStorage.multiSet(
[['UID123', JSON.stringify(uid123Object)], ['UID124', JSON.stringify(uid124Object)]],
(err, result) => {
expect(err).toEqual(null);
expect(result).toMatchSnapshot();
window.localStorage = originalLocalStorage;
assertResult();
done();
}
);
});
});
describe('mergeItem', () => {
const assertResult = () => {
expect(JSON.parse(mockLocalStorage.getItem('UID123'))).toMatchSnapshot();
};
test('promise after setting item', () => {
return AsyncStorage.mergeItem('UID123', JSON.stringify(uid123Delta)).then(() => {
assertResult();
});
});
test('calls callback after setting item', done => {
AsyncStorage.mergeItem('UID123', JSON.stringify(uid123Delta), (err, result) => {
expect(err).toEqual(null);
assertResult();
done();
});
});
});
describe('multiMerge', () => {
const assertResult = result => {
expect(JSON.parse(mockLocalStorage.getItem('UID123'))).toMatchSnapshot();
expect(JSON.parse(mockLocalStorage.getItem('UID124'))).toMatchSnapshot();
};
test('promise after setting items', () => {
return AsyncStorage.multiMerge([
['UID123', JSON.stringify(uid123Delta)],
['UID124', JSON.stringify(uid123Delta)]
]).then(() => {
assertResult();
});
});
test('calls callback after setting items', done => {
AsyncStorage.multiMerge(
[['UID123', JSON.stringify(uid123Delta)], ['UID124', JSON.stringify(uid123Delta)]],
(err, result) => {
expect(err).toEqual(null);
assertResult();
done();
}
);
});
});
describe('removeItem', () => {
const assertResult = () => {
expect(mockLocalStorage.getItem('UID123')).toBeUndefined();
};
test('promise after setting item', () => {
return AsyncStorage.removeItem('UID123').then(() => {
assertResult();
});
});
test('calls callback after setting item', done => {
AsyncStorage.removeItem('UID123', (err, result) => {
expect(err).toEqual(null);
assertResult();
done();
});
});
});
describe('multiRemove', () => {
const assertResult = result => {
expect(mockLocalStorage.getItem('UID123')).toBeUndefined();
expect(mockLocalStorage.getItem('UID124')).toBeUndefined();
};
test('promise after setting items', () => {
return AsyncStorage.multiRemove(['UID123', 'UID124']).then(() => {
assertResult();
});
});
test('calls callback after setting items', done => {
AsyncStorage.multiRemove(['UID123', 'UID124'], (err, result) => {
expect(err).toEqual(null);
assertResult();
done();
});
});
});
});

View File

@@ -13,66 +13,69 @@ const mergeLocalStorageItem = (key, value) => {
window.localStorage.setItem(key, nextValue);
};
const createPromise = (getValue, callback) => {
return new Promise((resolve, reject) => {
try {
const value = getValue();
if (callback) {
callback(null, value);
}
resolve(value);
} catch (err) {
if (callback) {
callback(err);
}
reject(err);
}
});
};
const createPromiseAll = (promises, callback, processResult) => {
return Promise.all(promises).then(
result => {
const value = processResult ? processResult(result) : null;
callback && callback(null, value);
return Promise.resolve(value);
},
errors => {
callback && callback(errors);
return Promise.reject(errors);
}
);
};
class AsyncStorage {
/**
* Erases *all* AsyncStorage for the domain.
*/
static clear() {
return new Promise((resolve, reject) => {
try {
window.localStorage.clear();
resolve(null);
} catch (err) {
reject(err);
}
});
static clear(callback) {
return createPromise(() => {
window.localStorage.clear();
}, callback);
}
/**
* Gets *all* keys known to the app, for all callers, libraries, etc.
*/
static getAllKeys() {
return new Promise((resolve, reject) => {
try {
const numberOfKeys = window.localStorage.length;
const keys = [];
for (let i = 0; i < numberOfKeys; i += 1) {
const key = window.localStorage.key(i);
keys.push(key);
}
resolve(keys);
} catch (err) {
reject(err);
static getAllKeys(callback) {
return createPromise(() => {
const numberOfKeys = window.localStorage.length;
const keys = [];
for (let i = 0; i < numberOfKeys; i += 1) {
const key = window.localStorage.key(i);
keys.push(key);
}
});
return keys;
}, callback);
}
/**
* Fetches `key` value.
*/
static getItem(key: string) {
return new Promise((resolve, reject) => {
try {
const value = window.localStorage.getItem(key);
resolve(value);
} catch (err) {
reject(err);
}
});
}
/**
* Merges existing value with input value, assuming they are stringified JSON.
*/
static mergeItem(key: string, value: string) {
return new Promise((resolve, reject) => {
try {
mergeLocalStorageItem(key, value);
resolve(null);
} catch (err) {
reject(err);
}
});
static getItem(key: string, callback) {
return createPromise(() => {
return window.localStorage.getItem(key);
}, callback);
}
/**
@@ -81,13 +84,37 @@ class AsyncStorage {
*
* multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
*/
static multiGet(keys: Array<string>) {
static multiGet(keys: Array<string>, callback) {
const promises = keys.map(key => AsyncStorage.getItem(key));
const processResult = result => result.map((value, i) => [keys[i], value]);
return createPromiseAll(promises, callback, processResult);
}
return Promise.all(promises).then(
result => Promise.resolve(result.map((value, i) => [keys[i], value])),
error => Promise.reject(error)
);
/**
* Sets `value` for `key`.
*/
static setItem(key: string, value: string, callback) {
return createPromise(() => {
window.localStorage.setItem(key, value);
}, callback);
}
/**
* Takes an array of key-value array pairs.
* multiSet([['k1', 'val1'], ['k2', 'val2']])
*/
static multiSet(keyValuePairs: Array<Array<string>>, callback) {
const promises = keyValuePairs.map(item => AsyncStorage.setItem(item[0], item[1]));
return createPromiseAll(promises, callback);
}
/**
* Merges existing value with input value, assuming they are stringified JSON.
*/
static mergeItem(key: string, value: string, callback) {
return createPromise(() => {
mergeLocalStorageItem(key, value);
}, callback);
}
/**
@@ -96,57 +123,26 @@ class AsyncStorage {
*
* multiMerge([['k1', 'val1'], ['k2', 'val2']])
*/
static multiMerge(keyValuePairs: Array<Array<string>>) {
static multiMerge(keyValuePairs: Array<Array<string>>, callback) {
const promises = keyValuePairs.map(item => AsyncStorage.mergeItem(item[0], item[1]));
return Promise.all(promises).then(() => Promise.resolve(null), error => Promise.reject(error));
}
/**
* Delete all the keys in the `keys` array.
*/
static multiRemove(keys: Array<string>) {
const promises = keys.map(key => AsyncStorage.removeItem(key));
return Promise.all(promises).then(() => Promise.resolve(null), error => Promise.reject(error));
}
/**
* Takes an array of key-value array pairs.
* multiSet([['k1', 'val1'], ['k2', 'val2']])
*/
static multiSet(keyValuePairs: Array<Array<string>>) {
const promises = keyValuePairs.map(item => AsyncStorage.setItem(item[0], item[1]));
return Promise.all(promises).then(() => Promise.resolve(null), error => Promise.reject(error));
return createPromiseAll(promises, callback);
}
/**
* Removes a `key`
*/
static removeItem(key: string) {
return new Promise((resolve, reject) => {
try {
window.localStorage.removeItem(key);
resolve(null);
} catch (err) {
reject(err);
}
});
static removeItem(key: string, callback) {
return createPromise(() => {
return window.localStorage.removeItem(key);
}, callback);
}
/**
* Sets `value` for `key`.
* Delete all the keys in the `keys` array.
*/
static setItem(key: string, value: string) {
return new Promise((resolve, reject) => {
try {
window.localStorage.setItem(key, value);
resolve(null);
} catch (err) {
reject(err);
}
});
static multiRemove(keys: Array<string>, callback) {
const promises = keys.map(key => AsyncStorage.removeItem(key));
return createPromiseAll(promises, callback);
}
}

View File

@@ -1,19 +1,43 @@
/* global window */
class Clipboard {
static isSupported() {
return (
typeof document.queryCommandSupported === 'function' && document.queryCommandSupported('copy')
);
}
static getString() {
return Promise.resolve('');
}
static setString(text) {
let success = false;
const textField = document.createElement('textarea');
textField.innerText = text;
document.body.appendChild(textField);
textField.select();
// add the text to a hidden node
const node = document.createElement('span');
node.textContent = text;
node.style.position = 'absolute';
node.style.opacity = '0';
document.body.appendChild(node);
// select the text
const selection = window.getSelection();
selection.removeAllRanges();
const range = document.createRange();
range.selectNodeContents(node);
selection.addRange(range);
// attempt to copy
try {
document.execCommand('copy');
success = true;
} catch (e) {}
document.body.removeChild(textField);
// remove selection and node
selection.removeAllRanges();
document.body.removeChild(node);
return success;
}
}

View File

@@ -28,6 +28,7 @@ const Linking = {
* https://mathiasbynens.github.io/rel-noopener/
*/
const iframeOpen = url => {
const noOpener = url.indexOf('mailto:') !== 0;
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
@@ -36,7 +37,7 @@ const iframeOpen = url => {
const script = iframeDoc.createElement('script');
script.text = `
window.parent = null; window.top = null; window.frameElement = null;
var child = window.open("${url}"); child.opener = null;
var child = window.open("${url}"); ${noOpener && 'child.opener = null'};
`;
iframeDoc.body.appendChild(script);
document.body.removeChild(iframe);

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

@@ -11,6 +11,8 @@ import prefixInlineStyles from './prefixInlineStyles';
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
import StyleManager from './StyleManager';
const vendorPrefixPattern = /^(webkit|moz|ms)/;
const createCacheKey = id => {
const prefix = I18nManager.isRTL ? 'rtl' : 'ltr';
return `${prefix}-${id}`;
@@ -81,36 +83,58 @@ class StyleRegistry {
/**
* 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, { classList: domClassList, style: domStyle }) {
// Convert the DOM classList back into a React Native form
// Preserves unrecognized class names.
const { classList: rnClassList, style: rnStyle } = domClassList.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: {} }
);
// 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);
// DOM style may include vendor prefixes and properties set by other libraries.
// Preserve it but transform back into React DOM style.
const rdomStyle = Object.keys(domStyle).reduce((acc, styleName) => {
const value = domStyle[styleName];
if (value !== '') {
const reactStyleName = vendorPrefixPattern.test(styleName)
? styleName.charAt(0).toUpperCase() + styleName.slice(1)
: styleName;
acc[reactStyleName] = value;
}
return acc;
}, {});
// Create next DOM style props from current and next RN styles
const { classList: rdomClassListNext, style: rdomStyleNext } = this.resolve([
rnStyle,
rnStyleNext
]);
// 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] = '';
}
});
// Next inline styles take priority over current inline styles
Object.assign(style, rdomStyleNext);
// Add the current class names not managed by React Native
const className = classListToString(rdomClassListNext.concat(rnClassList));
// Resolve the two React Native styles.
const { classList, style = {} } = this.resolve([previousReactNativeStyle, reactNativeStyle]);
// 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;
});
classList.push(preservedClassNames);
const className = classListToString(classList);
return { className, style };
}

View File

@@ -58,19 +58,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

@@ -192,35 +192,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

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

@@ -143,11 +143,7 @@ const ScrollView = createReactClass({
children={this.props.children}
collapsable={false}
ref={this._setInnerViewRef}
style={[
styles.contentContainer,
horizontal && styles.contentContainerHorizontal,
contentContainerStyle
]}
style={[horizontal && styles.contentContainerHorizontal, contentContainerStyle]}
/>
);
@@ -232,16 +228,17 @@ const styles = StyleSheet.create({
flex: 1,
overflowX: 'hidden',
overflowY: 'auto',
WebkitOverflowScrolling: 'touch'
WebkitOverflowScrolling: 'touch',
// Enable hardware compositing in modern browsers.
// Creates a new layer with its own backing surface that can significantly
// improve scroll performance.
transform: [{ translateZ: 0 }]
},
baseHorizontal: {
flexDirection: 'row',
overflowX: 'auto',
overflowY: 'hidden'
},
contentContainer: {
transform: [{ translateZ: 0 }]
},
contentContainerHorizontal: {
flexDirection: 'row'
}

View File

@@ -0,0 +1,14 @@
import { Component } from 'react';
class StatusBar extends Component {
static setBackgroundColor() {}
static setBarStyle() {}
static setHidden() {}
static setNetworkActivityIndicatorVisible() {}
static setTranslucent() {}
render() {
return null;
}
}
module.exports = StatusBar;

View File

@@ -25,6 +25,7 @@ const TextOnlyStylePropTypes = {
textShadowRadius: number,
writingDirection: WritingDirectionPropType,
/* @platform web */
textIndent: numberOrString,
textOverflow: string,
textRendering: oneOf(['auto', 'geometricPrecision', 'optimizeLegibility', 'optimizeSpeed']),
textTransform: oneOf(['capitalize', 'lowercase', 'none', 'uppercase']),

View File

@@ -147,6 +147,7 @@ class TextInput extends Component {
style,
/* eslint-disable */
blurOnSubmit,
caretHidden,
clearButtonMode,
clearTextOnFocus,
dataDetectorTypes,
@@ -163,6 +164,8 @@ class TextInput extends Component {
selection,
selectionColor,
selectTextOnFocus,
textBreakStrategy,
underlineColorAndroid,
/* eslint-enable */
...otherProps
} = this.props;

View File

@@ -18,7 +18,7 @@ module.exports = {
* @platform unsupported
*/
elevation: number,
/*
/**
* @platform web
*/
backgroundAttachment: string,
@@ -29,8 +29,11 @@ module.exports = {
backgroundRepeat: string,
backgroundSize: string,
boxShadow: string,
clip: string,
cursor: string,
filter: string,
outline: string,
outlineColor: ColorPropType,
perspective: oneOfType([number, string]),
perspectiveOrigin: string,
transitionDelay: string,
@@ -39,5 +42,6 @@ module.exports = {
transitionTimingFunction: string,
userSelect: string,
willChange: string,
WebkitMaskImage: string,
WebkitOverflowScrolling: oneOf(['auto', 'touch'])
};

View File

@@ -1,53 +1,57 @@
import createDOMElement from './modules/createDOMElement';
import findNodeHandle from './modules/findNodeHandle';
import modality from './modules/modality';
import NativeModules from './modules/NativeModules';
import processColor from './modules/processColor';
import { render, unmountComponentAtNode } from 'react-dom';
import {
// top-level API
findNodeHandle,
render,
unmountComponentAtNode,
// APIs
import Animated from './apis/Animated';
import AppRegistry from './apis/AppRegistry';
import AppState from './apis/AppState';
import AsyncStorage from './apis/AsyncStorage';
import BackAndroid from './apis/BackAndroid';
import Clipboard from './apis/Clipboard';
import Dimensions from './apis/Dimensions';
import Easing from 'animated/lib/Easing';
import I18nManager from './apis/I18nManager';
import InteractionManager from './apis/InteractionManager';
import Linking from './apis/Linking';
import NetInfo from './apis/NetInfo';
import PanResponder from './apis/PanResponder';
import PixelRatio from './apis/PixelRatio';
import Platform from './apis/Platform';
import StyleSheet from './apis/StyleSheet';
import UIManager from './apis/UIManager';
import Vibration from './apis/Vibration';
// modules
createDOMElement,
NativeModules,
processColor,
// components
import ActivityIndicator from './components/ActivityIndicator';
import Button from './components/Button';
import Image from './components/Image';
import ListView from './components/ListView';
import ProgressBar from './components/ProgressBar';
import ScrollView from './components/ScrollView';
import Switch from './components/Switch';
import Text from './components/Text';
import TextInput from './components/TextInput';
import Touchable from './components/Touchable/Touchable';
import TouchableHighlight from './components/Touchable/TouchableHighlight';
import TouchableOpacity from './components/Touchable/TouchableOpacity';
import TouchableWithoutFeedback from './components/Touchable/TouchableWithoutFeedback';
import View from './components/View';
// APIs
Animated,
AppRegistry,
AppState,
AsyncStorage,
BackAndroid,
Clipboard,
Dimensions,
Easing,
I18nManager,
InteractionManager,
Linking,
NetInfo,
PanResponder,
PixelRatio,
Platform,
StyleSheet,
UIManager,
Vibration,
// propTypes
import ColorPropType from './propTypes/ColorPropType';
import EdgeInsetsPropType from './propTypes/EdgeInsetsPropType';
import PointPropType from './propTypes/PointPropType';
import ViewPropTypes from './components/View/ViewPropTypes';
// components
ActivityIndicator,
Button,
Image,
ListView,
ProgressBar,
ScrollView,
StatusBar,
Switch,
Text,
TextInput,
Touchable,
TouchableHighlight,
TouchableOpacity,
TouchableWithoutFeedback,
View,
modality();
// propTypes
ColorPropType,
EdgeInsetsPropType,
PointPropType,
ViewPropTypes
} from './module';
const ReactNative = {
// top-level API
@@ -87,6 +91,7 @@ const ReactNative = {
ListView,
ProgressBar,
ScrollView,
StatusBar,
Switch,
Text,
TextInput,

50
src/module.js Normal file
View File

@@ -0,0 +1,50 @@
export { default as createDOMElement } from './modules/createDOMElement';
export { default as findNodeHandle } from './modules/findNodeHandle';
export { default as NativeModules } from './modules/NativeModules';
export { default as processColor } from './modules/processColor';
export { render, unmountComponentAtNode } from 'react-dom';
// APIs
export { default as Animated } from './apis/Animated';
export { default as AppRegistry } from './apis/AppRegistry';
export { default as AppState } from './apis/AppState';
export { default as AsyncStorage } from './apis/AsyncStorage';
export { default as BackAndroid } from './apis/BackAndroid';
export { default as Clipboard } from './apis/Clipboard';
export { default as Dimensions } from './apis/Dimensions';
export { default as Easing } from 'animated/lib/Easing';
export { default as I18nManager } from './apis/I18nManager';
export { default as InteractionManager } from './apis/InteractionManager';
export { default as Linking } from './apis/Linking';
export { default as NetInfo } from './apis/NetInfo';
export { default as PanResponder } from './apis/PanResponder';
export { default as PixelRatio } from './apis/PixelRatio';
export { default as Platform } from './apis/Platform';
export { default as StyleSheet } from './apis/StyleSheet';
export { default as UIManager } from './apis/UIManager';
export { default as Vibration } from './apis/Vibration';
// components
export { default as ActivityIndicator } from './components/ActivityIndicator';
export { default as Button } from './components/Button';
export { default as Image } from './components/Image';
export { default as ListView } from './components/ListView';
export { default as ProgressBar } from './components/ProgressBar';
export { default as ScrollView } from './components/ScrollView';
export { default as StatusBar } from './components/StatusBar';
export { default as Switch } from './components/Switch';
export { default as Text } from './components/Text';
export { default as TextInput } from './components/TextInput';
export { default as Touchable } from './components/Touchable/Touchable';
export { default as TouchableHighlight } from './components/Touchable/TouchableHighlight';
export { default as TouchableOpacity } from './components/Touchable/TouchableOpacity';
export {
default as TouchableWithoutFeedback
} from './components/Touchable/TouchableWithoutFeedback';
export { default as View } from './components/View';
// propTypes
export { default as ColorPropType } from './propTypes/ColorPropType';
export { default as EdgeInsetsPropType } from './propTypes/EdgeInsetsPropType';
export { default as PointPropType } from './propTypes/PointPropType';
export { default as ViewPropTypes } from './components/View/ViewPropTypes';

View File

@@ -6,6 +6,7 @@ const accessibilityComponentTypeToRole = {
const accessibilityTraitsToRole = {
adjustable: 'slider',
button: 'button',
header: 'heading',
image: 'img',
link: 'link',
none: 'presentation',

View File

@@ -67,12 +67,15 @@ 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 classList = [...node.classList];
const classList = Array.prototype.slice.call(node.classList);
const style = { ...node.style };
const domStyleProps = { classList, style };
// Next DOM state
const domProps = createDOMProps(nativeProps, style =>
StyleRegistry.resolveStateful(style, classList)
StyleRegistry.resolveStateful(style, domStyleProps)
);
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

@@ -2,9 +2,12 @@ import '../injectResponderEventPlugin';
import AccessibilityUtil from '../AccessibilityUtil';
import createDOMProps from '../createDOMProps';
import modality from '../modality';
import normalizeNativeEvent from '../normalizeNativeEvent';
import React from 'react';
modality();
const eventHandlerNames = {
onClick: true,
onClickCapture: true,

View File

@@ -14,7 +14,6 @@ const LayoutPropTypes = {
]),
alignItems: oneOf(['baseline', 'center', 'flex-end', 'flex-start', 'stretch']),
alignSelf: oneOf(['auto', 'baseline', 'center', 'flex-end', 'flex-start', 'stretch']),
aspectRatio: number,
backfaceVisibility: hiddenOrVisible,
borderWidth: numberOrString,
borderBottomWidth: numberOrString,
@@ -61,7 +60,26 @@ const LayoutPropTypes = {
top: numberOrString,
visibility: hiddenOrVisible,
width: numberOrString,
zIndex: number
zIndex: number,
/**
* @platform unsupported
*/
aspectRatio: number,
/**
* @platform web
*/
gridAutoColumns: string,
gridAutoFlow: string,
gridAutoRows: string,
gridColumnEnd: string,
gridColumnGap: string,
gridColumnStart: string,
gridRowEnd: string,
gridRowGap: string,
gridRowStart: string,
gridTemplateColumns: string,
gridTemplateRows: string,
gridTemplateAreas: string
};
module.exports = LayoutPropTypes;

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"