mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-30 09:24:09 +08:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64d2d34367 | ||
|
|
d83cd45b6f | ||
|
|
227971d22c | ||
|
|
4822cf4620 | ||
|
|
91e4528eac | ||
|
|
1ee64d8285 | ||
|
|
66a4c13bf3 | ||
|
|
9012e98ba7 | ||
|
|
046e01dfa9 | ||
|
|
6e71e1e058 | ||
|
|
d5a9f3e779 | ||
|
|
f16f5f21ce | ||
|
|
0bb7e67e63 | ||
|
|
c6b54930b6 | ||
|
|
599f1fcaf5 | ||
|
|
3f7a4e455f | ||
|
|
1f3e9cc6ee | ||
|
|
17ed63129f | ||
|
|
769334d04e |
@@ -85,6 +85,7 @@ When `false`, the text is not selectable.
|
||||
+ `textAlign`
|
||||
+ `textAlignVertical`
|
||||
+ `textDecorationLine`
|
||||
+ `textIndent` ‡
|
||||
+ `textOverflow` ‡
|
||||
+ `textRendering` ‡
|
||||
+ `textShadowColor`
|
||||
|
||||
@@ -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` ‡
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,7 +27,7 @@ module.exports = {
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': path.join(__dirname, '../../src')
|
||||
'react-native': path.join(__dirname, '../../src/module')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-web",
|
||||
"version": "0.0.84",
|
||||
"version": "0.0.87",
|
||||
"description": "React Native for Web",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -49,7 +49,7 @@ module.exports = {
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': path.join(__dirname, '../src')
|
||||
'react-native': path.join(__dirname, '../src/module')
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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"',
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
14
src/components/StatusBar/index.js
Normal file
14
src/components/StatusBar/index.js
Normal 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;
|
||||
@@ -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']),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'])
|
||||
};
|
||||
|
||||
97
src/index.js
97
src/index.js
@@ -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
50
src/module.js
Normal 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';
|
||||
@@ -6,6 +6,7 @@ const accessibilityComponentTypeToRole = {
|
||||
const accessibilityTraitsToRole = {
|
||||
adjustable: 'slider',
|
||||
button: 'button',
|
||||
header: 'heading',
|
||||
image: 'img',
|
||||
link: 'link',
|
||||
none: 'presentation',
|
||||
|
||||
@@ -69,7 +69,7 @@ const NativeMethodsMixin = {
|
||||
setNativeProps(nativeProps: Object) {
|
||||
// DOM state
|
||||
const node = findNodeHandle(this);
|
||||
const classList = [...node.classList];
|
||||
const classList = Array.prototype.slice.call(node.classList);
|
||||
|
||||
const domProps = createDOMProps(nativeProps, style =>
|
||||
StyleRegistry.resolveStateful(style, classList)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user