mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
306
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00079d2167 | ||
|
|
31da1be640 | ||
|
|
816d919989 | ||
|
|
3365a6c4cf | ||
|
|
0b48e2d71a | ||
|
|
37386a8223 | ||
|
|
697e96bc10 | ||
|
|
b76ccca30a | ||
|
|
4facc5fc9c | ||
|
|
720874fb10 | ||
|
|
d31d0141e4 | ||
|
|
3051f9b888 | ||
|
|
e2e610dbe9 | ||
|
|
2b0e8dd303 | ||
|
|
8ed0bafad2 | ||
|
|
758ec76cb6 | ||
|
|
05b17a811e | ||
|
|
5786a05e9e | ||
|
|
5153ce0a03 | ||
|
|
c9f5d229db | ||
|
|
12492d8d94 | ||
|
|
eeb0adca43 | ||
|
|
28fb229266 | ||
|
|
4daca092d2 | ||
|
|
5ebe0ed717 | ||
|
|
0d636a017d | ||
|
|
4d817245f7 | ||
|
|
e2d9ddce4c | ||
|
|
48af214216 | ||
|
|
2fe1ac2a83 | ||
|
|
2424a215e0 | ||
|
|
f5d8e4d6e0 | ||
|
|
68f1a223fb | ||
|
|
51ccdfa36a | ||
|
|
e163df3567 | ||
|
|
a303a42e28 | ||
|
|
2afeb8fbe6 | ||
|
|
56059e92df | ||
|
|
5d20b9065d | ||
|
|
fd4e75644f | ||
|
|
6e2e02ab08 | ||
|
|
f309540f69 | ||
|
|
b489c9aa43 | ||
|
|
5b5cadeb30 | ||
|
|
59b974eafb | ||
|
|
50d5f86bf8 | ||
|
|
c1a0a72275 | ||
|
|
94caefa9c2 | ||
|
|
b82ac9bf07 | ||
|
|
e21fb91786 | ||
|
|
0f5190e9f3 | ||
|
|
331e4a96e9 | ||
|
|
2d66e10ec4 | ||
|
|
3aec8ee72d | ||
|
|
205a2c0bfc | ||
|
|
2ddf3d942d | ||
|
|
a91f44d7fd | ||
|
|
6e9ceaa348 | ||
|
|
6cd0709bc0 | ||
|
|
3cef3010e6 | ||
|
|
1d452f3e4d | ||
|
|
c933087567 | ||
|
|
9edad17a15 | ||
|
|
ffa7dbcbf1 | ||
|
|
a8211e32d2 | ||
|
|
deba13f698 | ||
|
|
3309f6da80 | ||
|
|
554f23276b | ||
|
|
aefdf82cdc | ||
|
|
327f84b967 | ||
|
|
48548fedbf | ||
|
|
0d981c06de | ||
|
|
8463625bd9 | ||
|
|
a6ff11f1d2 | ||
|
|
d4eac3ddca | ||
|
|
d8f43105ed | ||
|
|
f165bbaf4e | ||
|
|
222594cb44 | ||
|
|
274769fd8c | ||
|
|
9f81d6726c | ||
|
|
01deb0b03d | ||
|
|
257bbb5255 | ||
|
|
bb883bfb61 | ||
|
|
eab7b00cee | ||
|
|
47e1d1aef8 | ||
|
|
18452940f0 | ||
|
|
afdeefc864 | ||
|
|
6a4b83c16f | ||
|
|
c63992437b | ||
|
|
44ce9194a2 | ||
|
|
3e21f39a77 | ||
|
|
e9f69bc2e4 | ||
|
|
e3ed572c01 | ||
|
|
e2a2472af4 | ||
|
|
5222be0edf | ||
|
|
0b5e229ed2 | ||
|
|
129d1548a8 | ||
|
|
bc56eb1439 | ||
|
|
5a918227f2 | ||
|
|
95d1fd142e | ||
|
|
3e79838a31 | ||
|
|
ee8b50c955 | ||
|
|
4a93d23af2 | ||
|
|
d858d0c4e0 | ||
|
|
f41663a2df | ||
|
|
1fb26d4319 | ||
|
|
b807c51bbe | ||
|
|
53fb5b6cee | ||
|
|
407eb4ce85 | ||
|
|
41dd6fe6ea | ||
|
|
b10a1549c6 | ||
|
|
c627600ef3 | ||
|
|
f2b3057a7c | ||
|
|
90b80e375c | ||
|
|
cf22111cee | ||
|
|
8a272bc873 | ||
|
|
d8a13ec3d5 | ||
|
|
4bfeeaa90d | ||
|
|
60c8abcc70 | ||
|
|
4b16e4d550 | ||
|
|
fc9150c674 | ||
|
|
de13b59dd2 | ||
|
|
37636fc59a | ||
|
|
0f32bea634 | ||
|
|
809a2dc1d6 | ||
|
|
8416691719 | ||
|
|
d6de865236 | ||
|
|
e073fdc4c7 | ||
|
|
dfa43f0ce5 | ||
|
|
95b9dd3a88 | ||
|
|
e0ea046092 | ||
|
|
87ce2d635b | ||
|
|
f1b22e87f1 | ||
|
|
127f7095dc | ||
|
|
068f396c9b | ||
|
|
b812b0ee2e | ||
|
|
e964e741ef | ||
|
|
5164aa290b | ||
|
|
7d19ff3dcb | ||
|
|
f53c95c743 | ||
|
|
7fcd85cc53 | ||
|
|
7a806f6d89 | ||
|
|
379444515b | ||
|
|
f502e17cc7 | ||
|
|
757d6d204a | ||
|
|
1d852624fd | ||
|
|
62b7920b9d | ||
|
|
3cff9be3d1 | ||
|
|
3b83853713 | ||
|
|
842ce51099 | ||
|
|
9649e7cc8d | ||
|
|
58a403d3c8 | ||
|
|
18e6094cab | ||
|
|
33e62f71f4 | ||
|
|
b7253dc604 | ||
|
|
01911e5e06 | ||
|
|
09bb761b25 | ||
|
|
2eb401a496 | ||
|
|
7996c32211 | ||
|
|
2c5290946b | ||
|
|
d178e27939 | ||
|
|
f9abb5aae2 | ||
|
|
0e5422f36c | ||
|
|
c3d194d1f8 | ||
|
|
f69e33e6a8 | ||
|
|
aca76c75dd | ||
|
|
f1bd1cbc94 | ||
|
|
53222f0dda | ||
|
|
5f0317291b | ||
|
|
ea5276ed24 | ||
|
|
c8373d2ad6 | ||
|
|
bf7e2a85d0 | ||
|
|
d10e4dbf0f | ||
|
|
a1612a7dd2 | ||
|
|
000ab1139f | ||
|
|
81dd9c27ea | ||
|
|
4499f28c1d | ||
|
|
ef5cec4f08 | ||
|
|
7c87952e43 | ||
|
|
1b06e41c83 | ||
|
|
accf6f12e4 | ||
|
|
e01f90784f | ||
|
|
710d29efae | ||
|
|
9e4af68d91 | ||
|
|
10ffe170c2 | ||
|
|
3fa3963465 | ||
|
|
aefb3d3628 | ||
|
|
a803e3217e | ||
|
|
03dccfbf71 | ||
|
|
064dafa618 | ||
|
|
9c8ba9502c | ||
|
|
19a8eff668 | ||
|
|
9d19829742 | ||
|
|
6fea7c2846 | ||
|
|
fea2db42fd | ||
|
|
b34892eb80 | ||
|
|
e06af51cf9 | ||
|
|
a671c58189 | ||
|
|
93bbc6482d | ||
|
|
bb7aa500d7 | ||
|
|
cec5360f1b | ||
|
|
49b55804b1 | ||
|
|
8dd1256c25 | ||
|
|
280347d66a | ||
|
|
214c47ed76 | ||
|
|
75b220d6ce | ||
|
|
4e33b801ca | ||
|
|
1e5f22dc9c | ||
|
|
35aa9f3b97 | ||
|
|
4d3c3e625e | ||
|
|
961c1eb429 | ||
|
|
4f904b5d68 | ||
|
|
98ab7581a5 | ||
|
|
85cb35c514 | ||
|
|
9c73e2ff7a | ||
|
|
9c5fe3612d | ||
|
|
151ddd9e42 | ||
|
|
6e2f07fb81 | ||
|
|
9105b22925 | ||
|
|
e54e452026 | ||
|
|
bf7497a418 | ||
|
|
270e09d46e | ||
|
|
725053acfe | ||
|
|
6213950334 | ||
|
|
95dc38929c | ||
|
|
0d91b93aba | ||
|
|
72f7a1be6f | ||
|
|
5db42643cf | ||
|
|
e3afc4efd8 | ||
|
|
f2d65ea85b | ||
|
|
f3dd07531c | ||
|
|
4a262e0f2b | ||
|
|
c28d33f3a2 | ||
|
|
c4389c006f | ||
|
|
b56717ede9 | ||
|
|
3c78aa6d25 | ||
|
|
61c648d564 | ||
|
|
82a774e92b | ||
|
|
fc23d0c40d | ||
|
|
4a5f12aa29 | ||
|
|
c43e93d1b4 | ||
|
|
2771fc4c52 | ||
|
|
77a0cf27f2 | ||
|
|
5ec60effea | ||
|
|
326a66ba07 | ||
|
|
4b82673484 | ||
|
|
ab9a87c33a | ||
|
|
fca16fbe40 | ||
|
|
fa4c570d33 | ||
|
|
81ad713f5f | ||
|
|
36f76e5893 | ||
|
|
f74efc13ed | ||
|
|
b362072c83 | ||
|
|
2cb634bb0b | ||
|
|
9f94dd457a | ||
|
|
72694a722e | ||
|
|
09236ccbe7 | ||
|
|
28b9a6f69e | ||
|
|
902ffbbfb8 | ||
|
|
5006eca4a8 | ||
|
|
70feab9d50 | ||
|
|
915540d237 | ||
|
|
64c00dcd7a | ||
|
|
ba433be886 | ||
|
|
94e2e51876 | ||
|
|
548549ea9f | ||
|
|
19ddbac113 | ||
|
|
04fa09afef | ||
|
|
5d4140c513 | ||
|
|
7a23651ee1 | ||
|
|
d30ada61f0 | ||
|
|
9799c215cb | ||
|
|
3e8604d5d8 | ||
|
|
2e9d156fad | ||
|
|
c3e75393ab | ||
|
|
74f8055692 | ||
|
|
d1a82c4af1 | ||
|
|
a43987309d | ||
|
|
52f599a080 | ||
|
|
34c2157b66 | ||
|
|
ebd046ae21 | ||
|
|
a8cb47e011 | ||
|
|
9936a2406d | ||
|
|
691a1dafd1 | ||
|
|
0c61b49f0a | ||
|
|
b34a85f4da | ||
|
|
a65bbe14d3 | ||
|
|
be2cabc3f8 | ||
|
|
eb06659711 | ||
|
|
6357931b61 | ||
|
|
875ab073ea | ||
|
|
0d153562be | ||
|
|
31b63ade13 | ||
|
|
509e9c96e2 | ||
|
|
e1ce646275 | ||
|
|
e5f7200213 | ||
|
|
9f07b9a2b6 | ||
|
|
d5943b0e47 | ||
|
|
90dd7a13f0 | ||
|
|
d1b14ef062 | ||
|
|
47315af069 | ||
|
|
01151f8c7a | ||
|
|
4beef5bec3 | ||
|
|
9c4b9973f9 | ||
|
|
f732e46581 | ||
|
|
2cbcfcea88 |
+9
-8
@@ -9,11 +9,10 @@
|
||||
|
||||
# Ignore react-tools where there are overlaps, but don't ignore anything that
|
||||
# react-native relies on
|
||||
.*/node_modules/react-tools/src/vendor/core/ExecutionEnvironment.js
|
||||
.*/node_modules/react-tools/src/browser/eventPlugins/ResponderEventPlugin.js
|
||||
.*/node_modules/react-tools/src/browser/ui/React.js
|
||||
.*/node_modules/react-tools/src/core/ReactInstanceHandles.js
|
||||
.*/node_modules/react-tools/src/event/EventPropagators.js
|
||||
.*/node_modules/react-tools/src/React.js
|
||||
.*/node_modules/react-tools/src/renderers/shared/event/EventPropagators.js
|
||||
.*/node_modules/react-tools/src/renderers/shared/event/eventPlugins/ResponderEventPlugin.js
|
||||
.*/node_modules/react-tools/src/shared/vendor/core/ExecutionEnvironment.js
|
||||
|
||||
# Ignore commoner tests
|
||||
.*/node_modules/commoner/test/.*
|
||||
@@ -36,13 +35,15 @@ Examples/UIExplorer/ImageMocks.js
|
||||
[options]
|
||||
module.system=haste
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FixMe
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-3]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-3]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
|
||||
[version]
|
||||
0.13.1
|
||||
0.14.0
|
||||
|
||||
+5
-4
@@ -10,19 +10,20 @@ cache:
|
||||
before_install:
|
||||
- brew update
|
||||
install:
|
||||
- brew reinstall flow xctool nvm
|
||||
- brew reinstall xctool nvm
|
||||
- rm -Rf `node -p "require('os').tmpDir()"`/jest_preprocess_cache
|
||||
- npm install -g flow-bin@`node -p "require('fs').readFileSync('.flowconfig', 'utf8').split('[version]')[1].trim()"`
|
||||
- mkdir -p .nvm
|
||||
- cp $(brew --prefix nvm)/nvm-exec .nvm/
|
||||
- export NVM_DIR=.nvm
|
||||
- source $(brew --prefix nvm)/nvm.sh
|
||||
- nvm install iojs-v2
|
||||
- nvm use iojs-v2
|
||||
- npm config set spin=false
|
||||
- npm install
|
||||
|
||||
script:
|
||||
- |
|
||||
nvm use iojs-v2
|
||||
|
||||
if [ "$TEST_TYPE" = objc ]
|
||||
then
|
||||
|
||||
@@ -37,7 +38,7 @@ script:
|
||||
then
|
||||
|
||||
cd website
|
||||
npm install
|
||||
$(which npm) install
|
||||
./setup.sh
|
||||
if [ "$TRAVIS_PULL_REQUEST" = false ] && [ "$TRAVIS_BRANCH" = master ]; then
|
||||
# Automatically publish the website
|
||||
|
||||
+39
-41
@@ -24,7 +24,7 @@ var {
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var AnimationExperimental = require('AnimationExperimental');
|
||||
var Animated = require('Animated');
|
||||
var GameBoard = require('GameBoard');
|
||||
var TouchableBounce = require('TouchableBounce');
|
||||
|
||||
@@ -53,58 +53,58 @@ class Board extends React.Component {
|
||||
}
|
||||
|
||||
class Tile extends React.Component {
|
||||
static _getPosition(index): number {
|
||||
return BOARD_PADDING + (index * (CELL_SIZE + CELL_MARGIN * 2) + CELL_MARGIN);
|
||||
}
|
||||
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
|
||||
var tile = this.props.tile;
|
||||
|
||||
this.state = {
|
||||
opacity: new Animated.Value(0),
|
||||
top: new Animated.Value(Tile._getPosition(tile.toRow())),
|
||||
left: new Animated.Value(Tile._getPosition(tile.toColumn())),
|
||||
};
|
||||
}
|
||||
|
||||
calculateOffset(): {top: number; left: number; opacity: number} {
|
||||
var tile = this.props.tile;
|
||||
|
||||
var pos = (i) => {
|
||||
return BOARD_PADDING + (i * (CELL_SIZE + CELL_MARGIN * 2) + CELL_MARGIN);
|
||||
};
|
||||
|
||||
var animationPosition = (i) => {
|
||||
return pos(i) + (CELL_SIZE / 2);
|
||||
};
|
||||
|
||||
var offset = {
|
||||
top: pos(tile.toRow()),
|
||||
left: pos(tile.toColumn()),
|
||||
opacity: 1,
|
||||
top: this.state.top,
|
||||
left: this.state.left,
|
||||
opacity: this.state.opacity,
|
||||
};
|
||||
|
||||
if (tile.isNew()) {
|
||||
offset.opacity = 0;
|
||||
} else {
|
||||
var point = {
|
||||
x: animationPosition(tile.toColumn()),
|
||||
y: animationPosition(tile.toRow()),
|
||||
};
|
||||
AnimationExperimental.startAnimation({
|
||||
node: this.refs['this'],
|
||||
Animated.timing(this.state.opacity, {
|
||||
duration: 100,
|
||||
easing: 'easeInOutQuad',
|
||||
property: 'position',
|
||||
toValue: point,
|
||||
});
|
||||
toValue: 1,
|
||||
}).start();
|
||||
} else {
|
||||
Animated.parallel([
|
||||
Animated.timing(offset.top, {
|
||||
duration: 100,
|
||||
toValue: Tile._getPosition(tile.toRow()),
|
||||
}),
|
||||
Animated.timing(offset.left, {
|
||||
duration: 100,
|
||||
toValue: Tile._getPosition(tile.toColumn()),
|
||||
}),
|
||||
]).start();
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
AnimationExperimental.startAnimation({
|
||||
node: this.refs['this'],
|
||||
duration: 100,
|
||||
easing: 'easeInOutQuad',
|
||||
property: 'opacity',
|
||||
toValue: 1,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
var tile = this.props.tile;
|
||||
|
||||
var tileStyles = [
|
||||
styles.tile,
|
||||
styles['tile' + tile.value],
|
||||
this.calculateOffset()
|
||||
this.calculateOffset(),
|
||||
];
|
||||
|
||||
var textStyles = [
|
||||
@@ -115,9 +115,9 @@ class Tile extends React.Component {
|
||||
];
|
||||
|
||||
return (
|
||||
<View ref="this" style={tileStyles}>
|
||||
<Animated.View style={tileStyles}>
|
||||
<Text style={textStyles}>{tile.value}</Text>
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -136,10 +136,8 @@ class GameEndOverlay extends React.Component {
|
||||
return (
|
||||
<View style={styles.overlay}>
|
||||
<Text style={styles.overlayMessage}>{message}</Text>
|
||||
<TouchableBounce onPress={this.props.onRestart}>
|
||||
<View style={styles.tryAgain}>
|
||||
<Text style={styles.tryAgainText}>Try Again?</Text>
|
||||
</View>
|
||||
<TouchableBounce onPress={this.props.onRestart} style={styles.tryAgain}>
|
||||
<Text style={styles.tryAgainText}>Try Again?</Text>
|
||||
</TouchableBounce>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -262,6 +262,7 @@ var SearchScreen = React.createClass({
|
||||
) {
|
||||
return (
|
||||
<MovieCell
|
||||
key={movie.id}
|
||||
onSelect={() => this.selectMovie(movie)}
|
||||
onHighlight={() => highlightRowFunc(sectionID, rowID)}
|
||||
onUnhighlight={() => highlightRowFunc(null, null)}
|
||||
@@ -307,7 +308,7 @@ var NoMovies = React.createClass({
|
||||
render: function() {
|
||||
var text = '';
|
||||
if (this.props.filter) {
|
||||
text = `No results for “${this.props.filter}”`;
|
||||
text = `No results for "${this.props.filter}"`;
|
||||
} else if (!this.props.isLoading) {
|
||||
// If we're looking at the latest movies, aren't currently loading, and
|
||||
// still have no results, show a message
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
|
||||
# Ignore react-tools where there are overlaps, but don't ignore anything that
|
||||
# react-native relies on
|
||||
.*/node_modules/react-tools/src/vendor/core/ExecutionEnvironment.js
|
||||
.*/node_modules/react-tools/src/browser/eventPlugins/ResponderEventPlugin.js
|
||||
.*/node_modules/react-tools/src/browser/ui/React.js
|
||||
.*/node_modules/react-tools/src/core/ReactInstanceHandles.js
|
||||
.*/node_modules/react-tools/src/event/EventPropagators.js
|
||||
.*/node_modules/react-tools/src/React.js
|
||||
.*/node_modules/react-tools/src/renderers/shared/event/EventPropagators.js
|
||||
.*/node_modules/react-tools/src/renderers/shared/event/eventPlugins/ResponderEventPlugin.js
|
||||
.*/node_modules/react-tools/src/shared/vendor/core/ExecutionEnvironment.js
|
||||
|
||||
|
||||
# Ignore commoner tests
|
||||
.*/node_modules/commoner/test/.*
|
||||
@@ -32,13 +32,15 @@ node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
[options]
|
||||
module.system=haste
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FixMe
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-3]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-3]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-4]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
|
||||
[version]
|
||||
0.13.1
|
||||
0.14.0
|
||||
|
||||
+25
-26
@@ -106,27 +106,27 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = main.jsbundle; path = iOS/main.jsbundle; sourceTree = "<group>"; };
|
||||
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = "<group>"; };
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
|
||||
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../../Libraries/Image/RCTImage.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = "<group>"; };
|
||||
00E356EE1AD99517003FC87E /* SampleAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
00E356F21AD99517003FC87E /* SampleAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleAppTests.m; sourceTree = "<group>"; };
|
||||
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
|
||||
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = "<group>"; };
|
||||
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
|
||||
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = "<group>"; };
|
||||
13B07F961A680F5B00A75B9A /* SampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = iOS/AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = iOS/AppDelegate.m; sourceTree = "<group>"; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = iOS/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = iOS/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = iOS/main.m; sourceTree = "<group>"; };
|
||||
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../React/React.xcodeproj; sourceTree = "<group>"; };
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = "<group>"; };
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = "<group>"; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../../React/React.xcodeproj; sourceTree = "<group>"; };
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = "<group>"; };
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../../Libraries/Text/RCTText.xcodeproj; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -233,15 +233,15 @@
|
||||
13B07FAE1A68108700A75B9A /* SampleApp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
);
|
||||
name = SampleApp;
|
||||
path = SampleApp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
146834001AC3E56700842450 /* Products */ = {
|
||||
@@ -289,8 +289,8 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07FAE1A68108700A75B9A /* SampleApp */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
00E356EF1AD99517003FC87E /* SampleAppTests */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
@@ -548,7 +548,6 @@
|
||||
13B07FB21A68108700A75B9A /* Base */,
|
||||
);
|
||||
name = LaunchScreen.xib;
|
||||
path = iOS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
@@ -598,9 +597,9 @@
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
);
|
||||
INFOPLIST_FILE = "iOS/Info.plist";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/SampleApp/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = SampleApp;
|
||||
@@ -614,9 +613,9 @@
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
);
|
||||
INFOPLIST_FILE = "iOS/Info.plist";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/SampleApp/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = SampleApp;
|
||||
@@ -660,7 +659,7 @@
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
@@ -700,7 +699,7 @@
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
"$(SRCROOT)/../../../React/**",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
+1
-25
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0620"
|
||||
LastUpgradeVersion = "0630"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -20,20 +20,6 @@
|
||||
ReferencedContainer = "container:SampleApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
|
||||
BuildableName = "SampleAppTests.xctest"
|
||||
BlueprintName = "SampleAppTests"
|
||||
ReferencedContainer = "container:SampleApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
@@ -42,16 +28,6 @@
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
|
||||
BuildableName = "SampleAppTests.xctest"
|
||||
BlueprintName = "SampleAppTests"
|
||||
ReferencedContainer = "container:SampleApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6751" systemVersion="14C1510" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6736"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
TouchableWithoutFeedback,
|
||||
} = React;
|
||||
var ToastAndroid = require('ToastAndroid');
|
||||
var AccessibilityInfo = require('AccessibilityInfo');
|
||||
|
||||
var UIExplorerBlock = require('./UIExplorerBlock');
|
||||
var UIExplorerPage = require('./UIExplorerPage');
|
||||
|
||||
var AccessibilityAndroidExample = React.createClass({
|
||||
|
||||
statics: {
|
||||
title: 'Accessibility',
|
||||
description: 'Examples of using Accessibility API.',
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
count: 0,
|
||||
talkbackEnabled: false,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
AccessibilityInfo.addEventListener(
|
||||
'change',
|
||||
this._handleTouchExplorationChange
|
||||
);
|
||||
AccessibilityInfo.fetch().done((enabled) => {
|
||||
this.setState({
|
||||
count: this.state.count,
|
||||
talkbackEnabled: enabled}); }
|
||||
);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
AccessibilityInfo.removeEventListener(
|
||||
'change',
|
||||
this._handleTouchExplorationChange
|
||||
);
|
||||
},
|
||||
|
||||
_handleTouchExplorationChange: function(isEnabled) {
|
||||
this.setState({
|
||||
count: this.state.count,
|
||||
talkbackEnabled: isEnabled,
|
||||
});
|
||||
},
|
||||
|
||||
_showAccessibilityToast: function() {
|
||||
var text = 'TouchExploration is ' + (this.state.talkbackEnabled ? 'enabled' : 'disabled');
|
||||
ToastAndroid.show(text, ToastAndroid.SHORT);
|
||||
},
|
||||
|
||||
_addOne: function() {
|
||||
this.setState({
|
||||
count: ++this.state.count,
|
||||
talkbackEnabled: this.state.talkbackEnabled,
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<UIExplorerPage title={'Accessibility'}>
|
||||
|
||||
<UIExplorerBlock title="Nonaccessible view with TextViews">
|
||||
<View>
|
||||
<Text style={{color: 'green',}}>
|
||||
This is
|
||||
</Text>
|
||||
<Text style={{color: 'blue'}}>
|
||||
nontouchable normal view.
|
||||
</Text>
|
||||
</View>
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Accessible view with TextViews wihout label">
|
||||
<View accessible={true}>
|
||||
<Text style={{color: 'green',}}>
|
||||
This is
|
||||
</Text>
|
||||
<Text style={{color: 'blue'}}>
|
||||
nontouchable accessible view without label.
|
||||
</Text>
|
||||
</View>
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Accessible view with TextViews with label">
|
||||
<View accessible={true}
|
||||
accessibilityLabel="I have label, so I read it instead of embedded text.">
|
||||
<Text style={{color: 'green',}}>
|
||||
This is
|
||||
</Text>
|
||||
<Text style={{color: 'blue'}}>
|
||||
nontouchable accessible view with label.
|
||||
</Text>
|
||||
</View>
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Touchable with component type = button">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={() => ToastAndroid.show('Toasts work by default', ToastAndroid.SHORT)}>
|
||||
<View style={styles.embedded} accessibilityComponentType="button">
|
||||
<Text>Click me</Text>
|
||||
<Text>Or not</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="LiveRegion">
|
||||
<TouchableWithoutFeedback onPress={this._addOne}>
|
||||
<View style={styles.embedded} accessibilityComponentType="button">
|
||||
<Text>Click me</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
<Text accessibilityLiveRegion="polite">
|
||||
Clicked {this.state.count} times
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Check if TouchExploration is enabled">
|
||||
<TouchableWithoutFeedback onPress={this._showAccessibilityToast}>
|
||||
<View style={styles.embedded}>
|
||||
<Text>
|
||||
Click to check TouchExploration
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</UIExplorerBlock>
|
||||
|
||||
</UIExplorerPage>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
embedded: {
|
||||
backgroundColor: 'yellow',
|
||||
padding:10,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = AccessibilityAndroidExample;
|
||||
@@ -38,9 +38,11 @@ class Circle extends React.Component {
|
||||
super();
|
||||
this._onLongPress = this._onLongPress.bind(this);
|
||||
this._toggleIsActive = this._toggleIsActive.bind(this);
|
||||
this.state = {isActive: false};
|
||||
this.state.pan = new Animated.ValueXY(); // Vectors reduce boilerplate. (step1: uncomment)
|
||||
this.state.pop = new Animated.Value(0); // Initial value. (step2a: uncomment)
|
||||
this.state = {
|
||||
isActive: false,
|
||||
pan: new Animated.ValueXY(), // Vectors reduce boilerplate. (step1: uncomment)
|
||||
pop: new Animated.Value(0), // Initial value. (step2a: uncomment)
|
||||
};
|
||||
}
|
||||
|
||||
_onLongPress(): void {
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
Image,
|
||||
StyleSheet,
|
||||
View,
|
||||
ScrollView
|
||||
} = React;
|
||||
|
||||
var AssetScaledImageExample = React.createClass({
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
asset: this.props.asset
|
||||
};
|
||||
},
|
||||
|
||||
render() {
|
||||
var image = this.state.asset.node.image;
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.row}>
|
||||
<Image source={image} style={styles.imageWide}/>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Image source={image} style={styles.imageThumb}/>
|
||||
<Image source={image} style={styles.imageThumb}/>
|
||||
<Image source={image} style={styles.imageThumb}/>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Image source={image} style={styles.imageT1}/>
|
||||
<Image source={image} style={styles.imageT2}/>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
row: {
|
||||
padding: 5,
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignSelf: 'center',
|
||||
},
|
||||
textColumn: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
},
|
||||
imageWide: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'black',
|
||||
width: 320,
|
||||
height: 240,
|
||||
margin: 5,
|
||||
},
|
||||
imageThumb: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'black',
|
||||
width: 100,
|
||||
height: 100,
|
||||
margin: 5,
|
||||
},
|
||||
imageT1: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'black',
|
||||
width: 212,
|
||||
height: 320,
|
||||
margin: 5,
|
||||
},
|
||||
imageT2: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'black',
|
||||
width: 100,
|
||||
height: 320,
|
||||
margin: 5,
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = '<AssetScaledImageExample>';
|
||||
exports.description = 'Example component that displays the automatic scaling capabilities of the <Image /> tag';
|
||||
module.exports = AssetScaledImageExample;
|
||||
@@ -29,18 +29,23 @@ var COLORS = ['red', 'orange', 'yellow', 'green', 'blue'];
|
||||
|
||||
var BasicStorageExample = React.createClass({
|
||||
componentDidMount() {
|
||||
AsyncStorage.getItem(STORAGE_KEY)
|
||||
.then((value) => {
|
||||
if (value !== null){
|
||||
this.setState({selectedValue: value});
|
||||
this._appendMessage('Recovered selection from disk: ' + value);
|
||||
} else {
|
||||
this._appendMessage('Initialized with no selection on disk.');
|
||||
}
|
||||
})
|
||||
.catch((error) => this._appendMessage('AsyncStorage error: ' + error.message))
|
||||
.done();
|
||||
this._loadInitialState().done();
|
||||
},
|
||||
|
||||
async _loadInitialState() {
|
||||
try {
|
||||
var value = await AsyncStorage.getItem(STORAGE_KEY);
|
||||
if (value !== null){
|
||||
this.setState({selectedValue: value});
|
||||
this._appendMessage('Recovered selection from disk: ' + value);
|
||||
} else {
|
||||
this._appendMessage('Initialized with no selection on disk.');
|
||||
}
|
||||
} catch (error) {
|
||||
this._appendMessage('AsyncStorage error: ' + error.message);
|
||||
}
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
selectedValue: COLORS[0],
|
||||
@@ -80,19 +85,23 @@ var BasicStorageExample = React.createClass({
|
||||
);
|
||||
},
|
||||
|
||||
_onValueChange(selectedValue) {
|
||||
async _onValueChange(selectedValue) {
|
||||
this.setState({selectedValue});
|
||||
AsyncStorage.setItem(STORAGE_KEY, selectedValue)
|
||||
.then(() => this._appendMessage('Saved selection to disk: ' + selectedValue))
|
||||
.catch((error) => this._appendMessage('AsyncStorage error: ' + error.message))
|
||||
.done();
|
||||
try {
|
||||
await AsyncStorage.setItem(STORAGE_KEY, selectedValue);
|
||||
this._appendMessage('Saved selection to disk: ' + selectedValue);
|
||||
} catch (error) {
|
||||
this._appendMessage('AsyncStorage error: ' + error.message);
|
||||
}
|
||||
},
|
||||
|
||||
_removeStorage() {
|
||||
AsyncStorage.removeItem(STORAGE_KEY)
|
||||
.then(() => this._appendMessage('Selection removed from disk.'))
|
||||
.catch((error) => { this._appendMessage('AsyncStorage error: ' + error.message) })
|
||||
.done();
|
||||
async _removeStorage() {
|
||||
try {
|
||||
await AsyncStorage.removeItem(STORAGE_KEY);
|
||||
this._appendMessage('Selection removed from disk.');
|
||||
} catch (error) {
|
||||
this._appendMessage('AsyncStorage error: ' + error.message);
|
||||
}
|
||||
},
|
||||
|
||||
_appendMessage(message) {
|
||||
|
||||
@@ -24,9 +24,11 @@ var {
|
||||
SwitchIOS,
|
||||
Text,
|
||||
View,
|
||||
TouchableOpacity
|
||||
} = React;
|
||||
|
||||
var CameraRollView = require('./CameraRollView.ios');
|
||||
var AssetScaledImageExampleView = require('./AssetScaledImageExample');
|
||||
|
||||
var CAMERA_ROLL_VIEW = 'camera_roll_view';
|
||||
|
||||
@@ -54,7 +56,7 @@ var CameraRollExample = React.createClass({
|
||||
<Text>{'Group Type: ' + this.state.groupTypes}</Text>
|
||||
<CameraRollView
|
||||
ref={CAMERA_ROLL_VIEW}
|
||||
batchSize={5}
|
||||
batchSize={20}
|
||||
groupTypes={this.state.groupTypes}
|
||||
renderImage={this._renderImage}
|
||||
/>
|
||||
@@ -62,24 +64,35 @@ var CameraRollExample = React.createClass({
|
||||
);
|
||||
},
|
||||
|
||||
loadAsset(asset){
|
||||
this.props.navigator.push({
|
||||
title: 'Camera Roll Image',
|
||||
component: AssetScaledImageExampleView,
|
||||
backButtonTitle: 'Back',
|
||||
passProps: { asset: asset },
|
||||
});
|
||||
},
|
||||
|
||||
_renderImage(asset) {
|
||||
var imageSize = this.state.bigImages ? 150 : 75;
|
||||
var imageStyle = [styles.image, {width: imageSize, height: imageSize}];
|
||||
var location = asset.node.location.longitude ?
|
||||
JSON.stringify(asset.node.location) : 'Unknown location';
|
||||
return (
|
||||
<View key={asset} style={styles.row}>
|
||||
<Image
|
||||
source={asset.node.image}
|
||||
style={imageStyle}
|
||||
/>
|
||||
<View style={styles.info}>
|
||||
<Text style={styles.url}>{asset.node.image.uri}</Text>
|
||||
<Text>{location}</Text>
|
||||
<Text>{asset.node.group_name}</Text>
|
||||
<Text>{new Date(asset.node.timestamp).toString()}</Text>
|
||||
<TouchableOpacity onPress={ this.loadAsset.bind( this, asset ) }>
|
||||
<View key={asset} style={styles.row}>
|
||||
<Image
|
||||
source={asset.node.image}
|
||||
style={imageStyle}
|
||||
/>
|
||||
<View style={styles.info}>
|
||||
<Text style={styles.url}>{asset.node.image.uri}</Text>
|
||||
<Text>{location}</Text>
|
||||
<Text>{asset.node.group_name}</Text>
|
||||
<Text>{new Date(asset.node.timestamp).toString()}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -115,7 +128,7 @@ var styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = '<CameraRollView>';
|
||||
exports.title = 'Camera Roll';
|
||||
exports.description = 'Example component that uses CameraRoll to list user\'s photos';
|
||||
exports.examples = [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
CameraRoll,
|
||||
Image,
|
||||
NativeModules,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = React;
|
||||
var ImageEditingManager = NativeModules.ImageEditingManager;
|
||||
var RCTScrollViewConsts = NativeModules.UIManager.RCTScrollView.Constants;
|
||||
|
||||
var PAGE_SIZE = 20;
|
||||
|
||||
type ImageOffset = {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
type ImageSize = {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
type TransformData = {
|
||||
offset: ImageOffset;
|
||||
size: ImageSize;
|
||||
}
|
||||
|
||||
class SquareImageCropper extends React.Component {
|
||||
_isMounted: boolean;
|
||||
_transformData: TransformData;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._isMounted = true;
|
||||
this.state = {
|
||||
randomPhoto: null,
|
||||
measuredSize: null,
|
||||
croppedImageURI: null,
|
||||
cropError: null,
|
||||
};
|
||||
this._fetchRandomPhoto();
|
||||
}
|
||||
|
||||
_fetchRandomPhoto() {
|
||||
CameraRoll.getPhotos(
|
||||
{first: PAGE_SIZE},
|
||||
(data) => {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
}
|
||||
var edges = data.edges;
|
||||
var edge = edges[Math.floor(Math.random() * edges.length)];
|
||||
var randomPhoto = edge && edge.node && edge.node.image;
|
||||
if (randomPhoto) {
|
||||
this.setState({randomPhoto});
|
||||
}
|
||||
},
|
||||
(error) => undefined
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state.measuredSize) {
|
||||
return (
|
||||
<View
|
||||
style={styles.container}
|
||||
onLayout={(event) => {
|
||||
var measuredWidth = event.nativeEvent.layout.width;
|
||||
if (!measuredWidth) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
measuredSize: {width: measuredWidth, height: measuredWidth},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.state.croppedImageURI) {
|
||||
return this._renderImageCropper();
|
||||
}
|
||||
return this._renderCroppedImage();
|
||||
}
|
||||
|
||||
_renderImageCropper() {
|
||||
if (!this.state.randomPhoto) {
|
||||
return (
|
||||
<View style={styles.container} />
|
||||
);
|
||||
}
|
||||
var error = null;
|
||||
if (this.state.cropError) {
|
||||
error = (
|
||||
<Text>{this.state.cropError.message}</Text>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text>Drag the image within the square to crop:</Text>
|
||||
<ImageCropper
|
||||
image={this.state.randomPhoto}
|
||||
size={this.state.measuredSize}
|
||||
style={[styles.imageCropper, this.state.measuredSize]}
|
||||
onTransformDataChange={(data) => this._transformData = data}
|
||||
/>
|
||||
<TouchableHighlight
|
||||
style={styles.cropButtonTouchable}
|
||||
onPress={this._crop.bind(this)}>
|
||||
<View style={styles.cropButton}>
|
||||
<Text style={styles.cropButtonLabel}>
|
||||
Crop
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
{error}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_renderCroppedImage() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text>Here is the cropped image:</Text>
|
||||
<Image
|
||||
source={{uri: this.state.croppedImageURI}}
|
||||
style={[styles.imageCropper, this.state.measuredSize]}
|
||||
/>
|
||||
<TouchableHighlight
|
||||
style={styles.cropButtonTouchable}
|
||||
onPress={this._reset.bind(this)}>
|
||||
<View style={styles.cropButton}>
|
||||
<Text style={styles.cropButtonLabel}>
|
||||
Try again
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_crop() {
|
||||
ImageEditingManager.cropImage(
|
||||
this.state.randomPhoto.uri,
|
||||
this._transformData,
|
||||
(croppedImageURI) => this.setState({croppedImageURI}),
|
||||
(cropError) => this.setState({cropError})
|
||||
);
|
||||
}
|
||||
|
||||
_reset() {
|
||||
this.setState({
|
||||
randomPhoto: null,
|
||||
croppedImageURI: null,
|
||||
cropError: null,
|
||||
});
|
||||
this._fetchRandomPhoto();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ImageCropper extends React.Component {
|
||||
_scaledImageSize: ImageSize;
|
||||
_contentOffset: ImageOffset;
|
||||
|
||||
componentWillMount() {
|
||||
// Scale an image to the minimum size that is large enough to completely
|
||||
// fill the crop box.
|
||||
var widthRatio = this.props.image.width / this.props.size.width;
|
||||
var heightRatio = this.props.image.height / this.props.size.height;
|
||||
if (widthRatio < heightRatio) {
|
||||
this._scaledImageSize = {
|
||||
width: this.props.size.width,
|
||||
height: this.props.image.height / widthRatio,
|
||||
};
|
||||
} else {
|
||||
this._scaledImageSize = {
|
||||
width: this.props.image.width / heightRatio,
|
||||
height: this.props.size.height,
|
||||
};
|
||||
}
|
||||
this._contentOffset = {
|
||||
x: (this._scaledImageSize.width - this.props.size.width) / 2,
|
||||
y: (this._scaledImageSize.height - this.props.size.height) / 2,
|
||||
};
|
||||
this._updateTransformData(
|
||||
this._contentOffset,
|
||||
this._scaledImageSize,
|
||||
this.props.size
|
||||
);
|
||||
}
|
||||
|
||||
_onScroll(event) {
|
||||
this._updateTransformData(
|
||||
event.nativeEvent.contentOffset,
|
||||
event.nativeEvent.contentSize,
|
||||
event.nativeEvent.layoutMeasurement
|
||||
);
|
||||
}
|
||||
|
||||
_updateTransformData(offset, scaledImageSize, croppedImageSize) {
|
||||
var offsetRatioX = offset.x / scaledImageSize.width;
|
||||
var offsetRatioY = offset.y / scaledImageSize.height;
|
||||
var sizeRatioX = croppedImageSize.width / scaledImageSize.width;
|
||||
var sizeRatioY = croppedImageSize.height / scaledImageSize.height;
|
||||
|
||||
this.props.onTransformDataChange && this.props.onTransformDataChange({
|
||||
offset: {
|
||||
x: this.props.image.width * offsetRatioX,
|
||||
y: this.props.image.height * offsetRatioY,
|
||||
},
|
||||
size: {
|
||||
width: this.props.image.width * sizeRatioX,
|
||||
height: this.props.image.height * sizeRatioY,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
var decelerationRate =
|
||||
RCTScrollViewConsts && RCTScrollViewConsts.DecelerationRate ?
|
||||
RCTScrollViewConsts.DecelerationRate.Fast :
|
||||
0;
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
alwaysBounceVertical={true}
|
||||
automaticallyAdjustContentInsets={false}
|
||||
contentOffset={this._contentOffset}
|
||||
decelerationRate={decelerationRate}
|
||||
horizontal={true}
|
||||
maximumZoomScale={3.0}
|
||||
onMomentumScrollEnd={this._onScroll.bind(this)}
|
||||
onScrollEndDrag={this._onScroll.bind(this)}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={this.props.style}
|
||||
scrollEventThrottle={16}>
|
||||
<Image source={this.props.image} style={this._scaledImageSize} />
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exports.framework = 'React';
|
||||
exports.title = 'ImageEditingManager';
|
||||
exports.description = 'Cropping and scaling with ImageEditingManager';
|
||||
exports.examples = [{
|
||||
title: 'Image Cropping',
|
||||
render() {
|
||||
return <SquareImageCropper/>;
|
||||
}
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignSelf: 'stretch',
|
||||
},
|
||||
imageCropper: {
|
||||
alignSelf: 'center',
|
||||
marginTop: 12,
|
||||
},
|
||||
cropButtonTouchable: {
|
||||
alignSelf: 'center',
|
||||
marginTop: 12,
|
||||
},
|
||||
cropButton: {
|
||||
padding: 12,
|
||||
backgroundColor: 'blue',
|
||||
borderRadius: 4,
|
||||
},
|
||||
cropButtonLabel: {
|
||||
color: 'white',
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
@@ -21,10 +21,41 @@ var {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
ActivityIndicatorIOS
|
||||
} = React;
|
||||
|
||||
var ImageCapInsetsExample = require('./ImageCapInsetsExample');
|
||||
|
||||
var NetworkImageExample = React.createClass({
|
||||
watchID: (null: ?number),
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
error: false,
|
||||
loading: false,
|
||||
progress: 0
|
||||
};
|
||||
},
|
||||
render: function() {
|
||||
var loader = this.state.loading ?
|
||||
<View style={styles.progress}>
|
||||
<Text>{this.state.progress}%</Text>
|
||||
<ActivityIndicatorIOS style={{marginLeft:5}}/>
|
||||
</View> : null;
|
||||
return this.state.error ?
|
||||
<Text>{this.state.error}</Text> :
|
||||
<Image
|
||||
source={this.props.source}
|
||||
style={[styles.base, {overflow: 'visible'}]}
|
||||
onLoadStart={(e) => this.setState({loading: true})}
|
||||
onError={(e) => this.setState({error: e.nativeEvent.error, loading: false})}
|
||||
onProgress={(e) => this.setState({progress: Math.round(100 * e.nativeEvent.loaded / e.nativeEvent.total)})}
|
||||
onLoad={() => this.setState({loading: false, error: false})}>
|
||||
{loader}
|
||||
</Image>;
|
||||
}
|
||||
});
|
||||
|
||||
exports.displayName = (undefined: ?string);
|
||||
exports.framework = 'React';
|
||||
exports.title = '<Image>';
|
||||
@@ -59,6 +90,22 @@ exports.examples = [
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Error Handler',
|
||||
render: function() {
|
||||
return (
|
||||
<NetworkImageExample source={{uri: 'http://TYPO_ERROR_facebook.github.io/react/img/logo_og.png'}} />
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Image Download Progress',
|
||||
render: function() {
|
||||
return (
|
||||
<NetworkImageExample source={{uri: 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1'}}/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Border Color',
|
||||
render: function() {
|
||||
@@ -194,23 +241,46 @@ exports.examples = [
|
||||
'pixels to the tint color.',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
source={require('image!uie_thumb_normal')}
|
||||
style={[styles.icon, {tintColor: 'blue' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('image!uie_thumb_normal')}
|
||||
style={[styles.icon, styles.leftMargin, {tintColor: 'green' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('image!uie_thumb_normal')}
|
||||
style={[styles.icon, styles.leftMargin, {tintColor: 'red' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('image!uie_thumb_normal')}
|
||||
style={[styles.icon, styles.leftMargin, {tintColor: 'black' }]}
|
||||
/>
|
||||
<View>
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
source={require('image!uie_thumb_normal')}
|
||||
style={[styles.icon, {tintColor: '#5ac8fa' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('image!uie_thumb_normal')}
|
||||
style={[styles.icon, styles.leftMargin, {tintColor: '#4cd964' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('image!uie_thumb_normal')}
|
||||
style={[styles.icon, styles.leftMargin, {tintColor: '#ff2d55' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('image!uie_thumb_normal')}
|
||||
style={[styles.icon, styles.leftMargin, {tintColor: '#8e8e93' }]}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.sectionText}>
|
||||
It also works with downloaded images:
|
||||
</Text>
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, {tintColor: '#5ac8fa' }]}
|
||||
/>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, styles.leftMargin, {tintColor: '#4cd964' }]}
|
||||
/>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, styles.leftMargin, {tintColor: '#ff2d55' }]}
|
||||
/>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, styles.leftMargin, {tintColor: '#8e8e93' }]}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
@@ -277,12 +347,21 @@ var styles = StyleSheet.create({
|
||||
width: 38,
|
||||
height: 38,
|
||||
},
|
||||
progress: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
width: 100
|
||||
},
|
||||
leftMargin: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
background: {
|
||||
backgroundColor: '#222222'
|
||||
},
|
||||
sectionText: {
|
||||
marginVertical: 6,
|
||||
},
|
||||
nestedText: {
|
||||
marginLeft: 12,
|
||||
marginTop: 20,
|
||||
|
||||
@@ -49,8 +49,7 @@ var LayoutEventExample = React.createClass({
|
||||
() => {
|
||||
console.log('layout animation done.');
|
||||
this.addWrapText();
|
||||
},
|
||||
(error) => { throw new Error(JSON.stringify(error)); }
|
||||
}
|
||||
);
|
||||
this.setState({
|
||||
viewStyle: {
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
Image,
|
||||
ListView,
|
||||
TouchableHighlight,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var THUMB_URLS = [
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851549_767334479959628_274486868_n.png',
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851561_767334496626293_1958532586_n.png',
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851579_767334503292959_179092627_n.png',
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851589_767334513292958_1747022277_n.png',
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851563_767334559959620_1193692107_n.png',
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851593_767334566626286_1953955109_n.png',
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851591_767334523292957_797560749_n.png',
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851567_767334529959623_843148472_n.png',
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851548_767334489959627_794462220_n.png',
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851575_767334539959622_441598241_n.png',
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851573_767334549959621_534583464_n.png',
|
||||
'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851583_767334573292952_1519550680_n.png',
|
||||
];
|
||||
|
||||
var ListViewGridLayoutExample = React.createClass({
|
||||
|
||||
statics: {
|
||||
title: '<ListView> - Grid Layout',
|
||||
description: 'Flexbox grid layout.'
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
|
||||
return {
|
||||
dataSource: ds.cloneWithRows(this._genRows({})),
|
||||
};
|
||||
},
|
||||
|
||||
_pressData: ({}: {[key: number]: boolean}),
|
||||
|
||||
componentWillMount: function() {
|
||||
this._pressData = {};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
// ListView wraps ScrollView and so takes on its properties.
|
||||
// With that in mind you can use the ScrollView's contentContainerStyle prop to style the items.
|
||||
<ListView
|
||||
contentContainerStyle={styles.list}
|
||||
dataSource={this.state.dataSource}
|
||||
renderRow={this._renderRow}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
_renderRow: function(rowData: string, sectionID: number, rowID: number) {
|
||||
var rowHash = Math.abs(hashCode(rowData));
|
||||
var imgSource = {
|
||||
uri: THUMB_URLS[rowHash % THUMB_URLS.length],
|
||||
};
|
||||
return (
|
||||
<TouchableHighlight onPress={() => this._pressRow(rowID)} underlayColor="transparent">
|
||||
<View>
|
||||
<View style={styles.row}>
|
||||
<Image style={styles.thumb} source={imgSource} />
|
||||
<Text style={styles.text}>
|
||||
{rowData}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
},
|
||||
|
||||
_genRows: function(pressData: {[key: number]: boolean}): Array<string> {
|
||||
var dataBlob = [];
|
||||
for (var ii = 0; ii < 100; ii++) {
|
||||
var pressedText = pressData[ii] ? ' (X)' : '';
|
||||
dataBlob.push('Cell ' + ii + pressedText);
|
||||
}
|
||||
return dataBlob;
|
||||
},
|
||||
|
||||
_pressRow: function(rowID: number) {
|
||||
this._pressData[rowID] = !this._pressData[rowID];
|
||||
this.setState({dataSource: this.state.dataSource.cloneWithRows(
|
||||
this._genRows(this._pressData)
|
||||
)});
|
||||
},
|
||||
});
|
||||
|
||||
/* eslint no-bitwise: 0 */
|
||||
var hashCode = function(str) {
|
||||
var hash = 15;
|
||||
for (var ii = str.length - 1; ii >= 0; ii--) {
|
||||
hash = ((hash << 5) - hash) + str.charCodeAt(ii);
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
list: {
|
||||
justifyContent: 'space-around',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap'
|
||||
},
|
||||
row: {
|
||||
justifyContent: 'center',
|
||||
padding: 5,
|
||||
margin: 3,
|
||||
width: 100,
|
||||
height: 100,
|
||||
backgroundColor: '#F6F6F6',
|
||||
alignItems: 'center',
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
borderColor: '#CCC'
|
||||
},
|
||||
thumb: {
|
||||
width: 64,
|
||||
height: 64
|
||||
},
|
||||
text: {
|
||||
flex: 1,
|
||||
marginTop: 5,
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ListViewGridLayoutExample;
|
||||
@@ -48,19 +48,19 @@ var Thumb = React.createClass({
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<TouchableOpacity onPress={this._onPressThumb}>
|
||||
<View style={[styles.buttonContents, {flexDirection: this.state.dir}]}>
|
||||
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
|
||||
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
|
||||
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
|
||||
{this.state.dir === 'column' ?
|
||||
<Text>
|
||||
Oooo, look at this new text! So awesome it may just be crazy.
|
||||
Let me keep typing here so it wraps at least one line.
|
||||
</Text> :
|
||||
<Text />
|
||||
}
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
onPress={this._onPressThumb}
|
||||
style={[styles.buttonContents, {flexDirection: this.state.dir}]}>
|
||||
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
|
||||
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
|
||||
<Image style={styles.img} source={{uri: THUMB_URLS[this.state.thumbIndex]}} />
|
||||
{this.state.dir === 'column' ?
|
||||
<Text>
|
||||
Oooo, look at this new text! So awesome it may just be crazy.
|
||||
Let me keep typing here so it wraps at least one line.
|
||||
</Text> :
|
||||
<Text />
|
||||
}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
@@ -127,14 +127,12 @@ var ListViewPagingExample = React.createClass({
|
||||
<View><Text style={styles.text}>1 Like</Text></View> :
|
||||
null;
|
||||
return (
|
||||
<TouchableOpacity onPress={this._onPressHeader}>
|
||||
<View style={styles.header}>
|
||||
{headerLikeText}
|
||||
<View>
|
||||
<Text style={styles.text}>
|
||||
Table Header (click me)
|
||||
</Text>
|
||||
</View>
|
||||
<TouchableOpacity onPress={this._onPressHeader} style={styles.header}>
|
||||
{headerLikeText}
|
||||
<View>
|
||||
<Text style={styles.text}>
|
||||
Table Header (click me)
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
Modal,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
exports.displayName = (undefined: ?string);
|
||||
exports.framework = 'React';
|
||||
exports.title = '<Modal>';
|
||||
exports.description = 'Component for presenting modal views.';
|
||||
|
||||
var ModalExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
openModal: null,
|
||||
};
|
||||
},
|
||||
|
||||
_closeModal: function() {
|
||||
this.setState({openModal: null});
|
||||
},
|
||||
|
||||
_openAnimatedModal: function() {
|
||||
this.setState({openModal: 'animated'});
|
||||
},
|
||||
|
||||
_openNotAnimatedModal: function() {
|
||||
this.setState({openModal: 'not-animated'});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Modal animated={true} visible={this.state.openModal === 'animated'}>
|
||||
<View style={styles.container}>
|
||||
<Text>This modal was presented with animation.</Text>
|
||||
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._closeModal}>
|
||||
<Text>Close</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
<Modal visible={this.state.openModal === 'not-animated'}>
|
||||
<View style={styles.container}>
|
||||
<Text>This modal was presented immediately, without animation.</Text>
|
||||
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._closeModal}>
|
||||
<Text>Close</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._openAnimatedModal}>
|
||||
<Text>Present Animated</Text>
|
||||
</TouchableHighlight>
|
||||
|
||||
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._openNotAnimatedModal}>
|
||||
<Text>Present Without Animation</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Modal Presentation',
|
||||
description: 'Modals can be presented with or without animation',
|
||||
render: () => <ModalExample />,
|
||||
},
|
||||
];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#f5fcff',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
@@ -55,26 +55,24 @@ var BreadcrumbNavSample = React.createClass({
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => navigator.push(_getRandomRoute())}>
|
||||
<View>
|
||||
<Text style={styles.titleText}>{route.title}</Text>
|
||||
</View>
|
||||
<Text style={styles.titleText}>{route.title}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
},
|
||||
iconForRoute: function(route, navigator) {
|
||||
return (
|
||||
<TouchableOpacity onPress={() => {
|
||||
navigator.popToRoute(route);
|
||||
}}>
|
||||
<View style={styles.crumbIconPlaceholder} />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={() => { navigator.popToRoute(route); }}
|
||||
style={styles.crumbIconPlaceholder}
|
||||
/>
|
||||
);
|
||||
},
|
||||
separatorForRoute: function(route, navigator) {
|
||||
return (
|
||||
<TouchableOpacity onPress={navigator.pop}>
|
||||
<View style={styles.crumbSeparatorPlaceholder} />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={navigator.pop}
|
||||
style={styles.crumbSeparatorPlaceholder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -51,12 +51,11 @@ var NavigationBarRouteMapper = {
|
||||
var previousRoute = navState.routeStack[index - 1];
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => navigator.pop()}>
|
||||
<View style={styles.navBarLeftButton}>
|
||||
<Text style={[styles.navBarText, styles.navBarButtonText]}>
|
||||
{previousRoute.title}
|
||||
</Text>
|
||||
</View>
|
||||
onPress={() => navigator.pop()}
|
||||
style={styles.navBarLeftButton}>
|
||||
<Text style={[styles.navBarText, styles.navBarButtonText]}>
|
||||
{previousRoute.title}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
},
|
||||
@@ -64,12 +63,11 @@ var NavigationBarRouteMapper = {
|
||||
RightButton: function(route, navigator, index, navState) {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => navigator.push(newRandomRoute())}>
|
||||
<View style={styles.navBarRightButton}>
|
||||
<Text style={[styles.navBarText, styles.navBarButtonText]}>
|
||||
Next
|
||||
</Text>
|
||||
</View>
|
||||
onPress={() => navigator.push(newRandomRoute())}
|
||||
style={styles.navBarRightButton}>
|
||||
<Text style={[styles.navBarText, styles.navBarButtonText]}>
|
||||
Next
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -83,6 +83,26 @@ exports.examples = [{
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Status Bar Network Activity Indicator',
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<TouchableHighlight style={styles.wrapper}
|
||||
onPress={() => StatusBarIOS.setNetworkActivityIndicatorVisible(true)}>
|
||||
<View style={styles.button}>
|
||||
<Text>setNetworkActivityIndicatorVisible(true)</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight style={styles.wrapper}
|
||||
onPress={() => StatusBarIOS.setNetworkActivityIndicatorVisible(false)}>
|
||||
<View style={styles.button}>
|
||||
<Text>setNetworkActivityIndicatorVisible(false)</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
|
||||
@@ -39,11 +39,11 @@ var TabBarExample = React.createClass({
|
||||
};
|
||||
},
|
||||
|
||||
_renderContent: function(color: string, pageText: string) {
|
||||
_renderContent: function(color: string, pageText: string, num?: number) {
|
||||
return (
|
||||
<View style={[styles.tabContent, {backgroundColor: color}]}>
|
||||
<Text style={styles.tabText}>{pageText}</Text>
|
||||
<Text style={styles.tabText}>{this.state.presses} re-renders of the More tab</Text>
|
||||
<Text style={styles.tabText}>{num} re-renders of the {pageText}</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
@@ -51,8 +51,8 @@ var TabBarExample = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<TabBarIOS
|
||||
tintColor="black"
|
||||
barTintColor="#3abeff">
|
||||
tintColor="white"
|
||||
barTintColor="darkslateblue">
|
||||
<TabBarIOS.Item
|
||||
title="Blue Tab"
|
||||
selected={this.state.selectedTab === 'blueTab'}
|
||||
@@ -73,7 +73,7 @@ var TabBarExample = React.createClass({
|
||||
notifCount: this.state.notifCount + 1,
|
||||
});
|
||||
}}>
|
||||
{this._renderContent('#783E33', 'Red Tab')}
|
||||
{this._renderContent('#783E33', 'Red Tab', this.state.notifCount)}
|
||||
</TabBarIOS.Item>
|
||||
<TabBarIOS.Item
|
||||
systemIcon="more"
|
||||
@@ -84,7 +84,7 @@ var TabBarExample = React.createClass({
|
||||
presses: this.state.presses + 1
|
||||
});
|
||||
}}>
|
||||
{this._renderContent('#21551C', 'Green Tab')}
|
||||
{this._renderContent('#21551C', 'Green Tab', this.state.presses)}
|
||||
</TabBarIOS.Item>
|
||||
</TabBarIOS>
|
||||
);
|
||||
|
||||
@@ -269,7 +269,7 @@ exports.examples = [
|
||||
right right right right right right right right right right right right right
|
||||
</Text>
|
||||
<Text style={{textAlign: 'justify'}}>
|
||||
justify: this text component's contents are laid out with "textAlign: justify"
|
||||
justify: this text component{"'"}s contents are laid out with "textAlign: justify"
|
||||
and as you can see all of the lines except the last one span the
|
||||
available width of the parent container.
|
||||
</Text>
|
||||
@@ -369,6 +369,34 @@ exports.examples = [
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Text highlighting (tap the link to see highlight)',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text>Lorem ipsum dolor sit amet, <Text suppressHighlighting={false} style={{backgroundColor: 'white', textDecorationLine: 'underline', color: 'blue'}} onPress={() => null}>consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud</Text> exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'allowFontScaling attribute',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text>
|
||||
By default, text will respect Text Size accessibility setting on iOS.
|
||||
It means that all font sizes will be increased or descreased depending on the value of Text Size setting in
|
||||
{" "}<Text style={{fontWeight: 'bold'}}>Settings.app - Display & Brightness - Text Size</Text>
|
||||
</Text>
|
||||
<Text style={{marginTop: 10}}>
|
||||
You can disable scaling for your Text component by passing {"\""}allowFontScaling={"{"}false{"}\""} prop.
|
||||
</Text>
|
||||
<Text allowFontScaling={false} style={{marginTop: 20}}>
|
||||
This text will not scale.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
|
||||
@@ -33,7 +33,7 @@ var WithLabel = React.createClass({
|
||||
{this.props.children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
var TextEventsExample = React.createClass({
|
||||
@@ -41,13 +41,17 @@ var TextEventsExample = React.createClass({
|
||||
return {
|
||||
curText: '<No Event>',
|
||||
prevText: '<No Event>',
|
||||
prev2Text: '<No Event>',
|
||||
};
|
||||
},
|
||||
|
||||
updateText: function(text) {
|
||||
this.setState({
|
||||
curText: text,
|
||||
prevText: this.state.curText,
|
||||
this.setState((state) => {
|
||||
return {
|
||||
curText: text,
|
||||
prevText: state.curText,
|
||||
prev2Text: state.prevText,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
@@ -73,13 +77,43 @@ var TextEventsExample = React.createClass({
|
||||
/>
|
||||
<Text style={styles.eventLabel}>
|
||||
{this.state.curText}{'\n'}
|
||||
(prev: {this.state.prevText})
|
||||
(prev: {this.state.prevText}){'\n'}
|
||||
(prev2: {this.state.prev2Text})
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
class RewriteExample extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {text: ''};
|
||||
}
|
||||
render() {
|
||||
var limit = 20;
|
||||
var remainder = limit - this.state.text.length;
|
||||
var remainderColor = remainder > 5 ? 'blue' : 'red';
|
||||
return (
|
||||
<View style={styles.rewriteContainer}>
|
||||
<TextInput
|
||||
multiline={false}
|
||||
maxLength={limit}
|
||||
onChangeText={(text) => {
|
||||
text = text.replace(/ /g, '_');
|
||||
this.setState({text});
|
||||
}}
|
||||
style={styles.default}
|
||||
value={this.state.text}
|
||||
/>
|
||||
<Text style={[styles.remainder, {color: remainderColor}]}>
|
||||
{remainder}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
page: {
|
||||
paddingBottom: 300,
|
||||
@@ -125,12 +159,19 @@ var styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
},
|
||||
label: {
|
||||
width: 120,
|
||||
justifyContent: 'flex-end',
|
||||
flexDirection: 'row',
|
||||
width: 115,
|
||||
alignItems: 'flex-end',
|
||||
marginRight: 10,
|
||||
paddingTop: 2,
|
||||
},
|
||||
rewriteContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
remainder: {
|
||||
textAlign: 'right',
|
||||
width: 24,
|
||||
},
|
||||
});
|
||||
|
||||
exports.displayName = (undefined: ?string);
|
||||
@@ -143,6 +184,12 @@ exports.examples = [
|
||||
return <TextInput autoFocus={true} style={styles.default} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Live Re-Write (<sp> -> '_') + maxLength",
|
||||
render: function() {
|
||||
return <RewriteExample />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Auto-capitalize',
|
||||
render: function() {
|
||||
@@ -268,7 +315,7 @@ exports.examples = [
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="true">
|
||||
<TextInput password={true} style={styles.default} value="abc" />
|
||||
<TextInput password={true} style={styles.default} defaultValue="abc" />
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
@@ -276,7 +323,7 @@ exports.examples = [
|
||||
},
|
||||
{
|
||||
title: 'Event handling',
|
||||
render: function(): ReactElement { return <TextEventsExample /> },
|
||||
render: function(): ReactElement { return <TextEventsExample />; },
|
||||
},
|
||||
{
|
||||
title: 'Colored input text',
|
||||
@@ -285,11 +332,11 @@ exports.examples = [
|
||||
<View>
|
||||
<TextInput
|
||||
style={[styles.default, {color: 'blue'}]}
|
||||
value="Blue"
|
||||
defaultValue="Blue"
|
||||
/>
|
||||
<TextInput
|
||||
style={[styles.default, {color: 'green'}]}
|
||||
value="Green"
|
||||
defaultValue="Green"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
@@ -336,7 +383,7 @@ exports.examples = [
|
||||
<WithLabel label="clearTextOnFocus">
|
||||
<TextInput
|
||||
placeholder="text is cleared on focus"
|
||||
value="text is cleared on focus"
|
||||
defaultValue="text is cleared on focus"
|
||||
style={styles.default}
|
||||
clearTextOnFocus={true}
|
||||
/>
|
||||
@@ -344,7 +391,7 @@ exports.examples = [
|
||||
<WithLabel label="selectTextOnFocus">
|
||||
<TextInput
|
||||
placeholder="text is selected on focus"
|
||||
value="text is selected on focus"
|
||||
defaultValue="text is selected on focus"
|
||||
style={styles.default}
|
||||
selectTextOnFocus={true}
|
||||
/>
|
||||
@@ -364,7 +411,7 @@ exports.examples = [
|
||||
style={styles.multiline}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="mutliline text input with font styles and placeholder"
|
||||
placeholder="multiline text input with font styles and placeholder"
|
||||
multiline={true}
|
||||
clearTextOnFocus={true}
|
||||
autoCorrect={true}
|
||||
@@ -374,7 +421,7 @@ exports.examples = [
|
||||
style={[styles.multiline, styles.multilineWithFontStyles]}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="uneditable mutliline text input"
|
||||
placeholder="uneditable multiline text input"
|
||||
editable={false}
|
||||
multiline={true}
|
||||
style={styles.multiline}
|
||||
|
||||
@@ -1,61 +1,85 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* @providesModule TransformExample
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
var React = require('react-native');
|
||||
var {
|
||||
Animated,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
var UIExplorerBlock = require('./UIExplorerBlock');
|
||||
var UIExplorerPage = require('./UIExplorerPage');
|
||||
|
||||
var TransformExample = React.createClass({
|
||||
|
||||
mixins: [TimerMixin],
|
||||
|
||||
var Flip = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
interval: this.setInterval(this._update, 800),
|
||||
pulse: false,
|
||||
theta: new Animated.Value(45),
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
this._animate();
|
||||
},
|
||||
|
||||
_animate() {
|
||||
this.state.theta.setValue(0);
|
||||
Animated.timing(this.state.theta, {
|
||||
toValue: 360,
|
||||
duration: 5000,
|
||||
}).start(this._animate);
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<UIExplorerPage title="Transforms">
|
||||
<UIExplorerBlock title="foo bar">
|
||||
<View style={{height: 500}}>
|
||||
<View style={styles.box1} />
|
||||
<View style={styles.box2} />
|
||||
<View style={styles.box3step1} />
|
||||
<View style={styles.box3step2} />
|
||||
<View style={styles.box3step3} />
|
||||
<View style={styles.box4} />
|
||||
<View style={[
|
||||
styles.box5,
|
||||
this.state.pulse ? styles.box5Transform : null
|
||||
]} />
|
||||
</View>
|
||||
</UIExplorerBlock>
|
||||
</UIExplorerPage>
|
||||
<View style={styles.flipCardContainer}>
|
||||
<Animated.View style={[
|
||||
styles.flipCard,
|
||||
{transform: [
|
||||
{perspective: 850},
|
||||
{rotateX: this.state.theta.interpolate({
|
||||
inputRange: [0, 180],
|
||||
outputRange: ['0deg', '180deg']
|
||||
})},
|
||||
]}]}>
|
||||
<Text style={styles.flipText}>
|
||||
This text is flipping great.
|
||||
</Text>
|
||||
</Animated.View>
|
||||
<Animated.View style={[styles.flipCard, {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
backgroundColor: 'red',
|
||||
transform: [
|
||||
{perspective: 850},
|
||||
{rotateX: this.state.theta.interpolate({
|
||||
inputRange: [0, 180],
|
||||
outputRange: ['180deg', '360deg']
|
||||
})},
|
||||
]}]}>
|
||||
<Text style={styles.flipText}>
|
||||
On the flip side...
|
||||
</Text>
|
||||
</Animated.View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
|
||||
_update() {
|
||||
this.setState({
|
||||
pulse: !this.state.pulse,
|
||||
});
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
height: 500,
|
||||
},
|
||||
box1: {
|
||||
left: 0,
|
||||
backgroundColor: 'green',
|
||||
@@ -88,7 +112,7 @@ var styles = StyleSheet.create({
|
||||
},
|
||||
box3step1: {
|
||||
left: 0,
|
||||
backgroundColor: '#ffb6c1', // lightpink
|
||||
backgroundColor: 'lightpink',
|
||||
height: 50,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
@@ -99,7 +123,7 @@ var styles = StyleSheet.create({
|
||||
},
|
||||
box3step2: {
|
||||
left: 0,
|
||||
backgroundColor: '#ff69b4', //hotpink
|
||||
backgroundColor: 'hotpink',
|
||||
height: 50,
|
||||
opacity: 0.5,
|
||||
position: 'absolute',
|
||||
@@ -113,7 +137,7 @@ var styles = StyleSheet.create({
|
||||
},
|
||||
box3step3: {
|
||||
left: 0,
|
||||
backgroundColor: '#ff1493', // deeppink
|
||||
backgroundColor: 'deeppink',
|
||||
height: 50,
|
||||
opacity: 0.5,
|
||||
position: 'absolute',
|
||||
@@ -129,7 +153,7 @@ var styles = StyleSheet.create({
|
||||
},
|
||||
box4: {
|
||||
left: 0,
|
||||
backgroundColor: '#ff8c00', // darkorange
|
||||
backgroundColor: 'darkorange',
|
||||
height: 50,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
@@ -141,7 +165,7 @@ var styles = StyleSheet.create({
|
||||
width: 100,
|
||||
},
|
||||
box5: {
|
||||
backgroundColor: '#800000', // maroon
|
||||
backgroundColor: 'maroon',
|
||||
height: 50,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
@@ -155,7 +179,110 @@ var styles = StyleSheet.create({
|
||||
{scale: 2},
|
||||
],
|
||||
},
|
||||
flipCardContainer: {
|
||||
marginVertical: 40,
|
||||
flex: 1,
|
||||
alignSelf: 'center',
|
||||
},
|
||||
flipCard: {
|
||||
width: 200,
|
||||
height: 200,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'blue',
|
||||
backfaceVisibility: 'hidden',
|
||||
},
|
||||
flipText: {
|
||||
width: 90,
|
||||
fontSize: 20,
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
module.exports = TransformExample;
|
||||
exports.title = 'Transforms';
|
||||
exports.description = 'View transforms';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Perspective',
|
||||
description: 'perspective: 850, rotateX: Animated.timing(0 -> 360)',
|
||||
render(): ReactElement { return <Flip />; }
|
||||
},
|
||||
{
|
||||
title: 'Translate, Rotate, Scale',
|
||||
description: "translateX: 100, translateY: 50, rotate: '30deg', scaleX: 2, scaleY: 2",
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.box1} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Scale, Translate, Rotate, ',
|
||||
description: "scaleX: 2, scaleY: 2, translateX: 100, translateY: 50, rotate: '30deg'",
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.box2} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Rotate',
|
||||
description: "rotate: '30deg'",
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.box3step1} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Rotate, Scale',
|
||||
description: "rotate: '30deg', scaleX: 2, scaleY: 2",
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.box3step2} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Rotate, Scale, Translate ',
|
||||
description: "rotate: '30deg', scaleX: 2, scaleY: 2, translateX: 100, translateY: 50",
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.box3step3} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Translate, Scale, Rotate',
|
||||
description: "translate: [200, 350], scale: 2.5, rotate: '-0.2rad'",
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.box4} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Translate, Rotate, Scale',
|
||||
description: "translate: [-50, 35], rotate: '50deg', scale: 2",
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.box5, styles.box5Transform]} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -7,19 +7,25 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1300627E1B59179B0043FE5A /* RCTGzipTests.m */; };
|
||||
13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FE81AA91428003F314A /* libRCTImage.a */; };
|
||||
134180011AA9153C003F314A /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FEF1AA914B8003F314A /* libRCTText.a */; };
|
||||
1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; };
|
||||
134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.a */; };
|
||||
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; };
|
||||
138D6A171B53CD440074A87E /* RCTCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A151B53CD440074A87E /* RCTCacheTests.m */; };
|
||||
138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */; };
|
||||
1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */; };
|
||||
139FDEDB1B0651FB00C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; };
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */; };
|
||||
13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */; };
|
||||
141FC1211B222EBB004D5FFB /* IntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 141FC1201B222EBB004D5FFB /* IntegrationTests.m */; };
|
||||
143BC5A11B21E45C00462512 /* UIExplorerSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */; };
|
||||
144D21241B2204C5006DB32B /* RCTClippingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 144D21231B2204C5006DB32B /* RCTClippingTests.m */; };
|
||||
144D21241B2204C5006DB32B /* RCTClipRectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 144D21231B2204C5006DB32B /* RCTClipRectTests.m */; };
|
||||
147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 147CED4B1AB34F8C00DA3E4C /* libRCTActionSheet.a */; };
|
||||
1497CFAC1B21F5E400C1F8F2 /* RCTAllocationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */; };
|
||||
1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */; };
|
||||
@@ -47,6 +53,7 @@
|
||||
14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; };
|
||||
3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 357859011B28D2C500341EDB /* libRCTLinking.a */; };
|
||||
834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; };
|
||||
83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; };
|
||||
D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -153,11 +160,15 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
004D289E1AAF61C70097A701 /* UIExplorerUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1300627E1B59179B0043FE5A /* RCTGzipTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGzipTests.m; sourceTree = "<group>"; };
|
||||
13417FE31AA91428003F314A /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = "<group>"; };
|
||||
13417FEA1AA914B8003F314A /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = "<group>"; };
|
||||
134180261AA91779003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
|
||||
134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = "<group>"; };
|
||||
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
|
||||
138D6A151B53CD440074A87E /* RCTCacheTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCacheTests.m; sourceTree = "<group>"; };
|
||||
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowViewTests.m; sourceTree = "<group>"; };
|
||||
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethodTests.m; sourceTree = "<group>"; };
|
||||
139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = "<group>"; };
|
||||
13B07F961A680F5B00A75B9A /* UIExplorer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UIExplorer.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = UIExplorer/AppDelegate.h; sourceTree = "<group>"; };
|
||||
@@ -167,6 +178,8 @@
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = UIExplorer/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = UIExplorer/main.m; sourceTree = "<group>"; };
|
||||
13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
|
||||
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSONTests.m; sourceTree = "<group>"; };
|
||||
13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMethodArgumentTests.m; sourceTree = "<group>"; };
|
||||
141FC1201B222EBB004D5FFB /* IntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntegrationTests.m; sourceTree = "<group>"; };
|
||||
143BC57E1B21E18100462512 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
143BC5811B21E18100462512 /* testLayoutExampleSnapshot_1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "testLayoutExampleSnapshot_1@2x.png"; sourceTree = "<group>"; };
|
||||
@@ -178,7 +191,7 @@
|
||||
143BC5951B21E3E100462512 /* UIExplorerIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIExplorerIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
143BC5981B21E3E100462512 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIExplorerSnapshotTests.m; sourceTree = "<group>"; };
|
||||
144D21231B2204C5006DB32B /* RCTClippingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTClippingTests.m; sourceTree = "<group>"; };
|
||||
144D21231B2204C5006DB32B /* RCTClipRectTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTClipRectTests.m; sourceTree = "<group>"; };
|
||||
1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAllocationTests.m; sourceTree = "<group>"; };
|
||||
1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeTests.m; sourceTree = "<group>"; };
|
||||
1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTContextExecutorTests.m; sourceTree = "<group>"; };
|
||||
@@ -202,6 +215,7 @@
|
||||
14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
|
||||
357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = "<group>"; };
|
||||
58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = "<group>"; };
|
||||
83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerScenarioTests.m; sourceTree = "<group>"; };
|
||||
D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -342,11 +356,17 @@
|
||||
children = (
|
||||
1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */,
|
||||
1497CFA51B21F5E400C1F8F2 /* RCTBridgeTests.m */,
|
||||
144D21231B2204C5006DB32B /* RCTClippingTests.m */,
|
||||
138D6A151B53CD440074A87E /* RCTCacheTests.m */,
|
||||
144D21231B2204C5006DB32B /* RCTClipRectTests.m */,
|
||||
1497CFA61B21F5E400C1F8F2 /* RCTContextExecutorTests.m */,
|
||||
1497CFA71B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m */,
|
||||
1497CFA81B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m */,
|
||||
1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */,
|
||||
1300627E1B59179B0043FE5A /* RCTGzipTests.m */,
|
||||
13DB03471B5D2ED500C27245 /* RCTJSONTests.m */,
|
||||
13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */,
|
||||
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */,
|
||||
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */,
|
||||
1497CFAA1B21F5E400C1F8F2 /* RCTSparseArrayTests.m */,
|
||||
1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */,
|
||||
143BC57E1B21E18100462512 /* Info.plist */,
|
||||
@@ -383,6 +403,7 @@
|
||||
children = (
|
||||
141FC1201B222EBB004D5FFB /* IntegrationTests.m */,
|
||||
143BC5A01B21E45C00462512 /* UIExplorerSnapshotTests.m */,
|
||||
83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */,
|
||||
143BC5971B21E3E100462512 /* Supporting Files */,
|
||||
);
|
||||
path = UIExplorerIntegrationTests;
|
||||
@@ -772,14 +793,20 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1497CFB01B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m in Sources */,
|
||||
144D21241B2204C5006DB32B /* RCTClippingTests.m in Sources */,
|
||||
144D21241B2204C5006DB32B /* RCTClipRectTests.m in Sources */,
|
||||
1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */,
|
||||
1497CFB21B21F5E400C1F8F2 /* RCTSparseArrayTests.m in Sources */,
|
||||
1300627F1B59179B0043FE5A /* RCTGzipTests.m in Sources */,
|
||||
1497CFAF1B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m in Sources */,
|
||||
1497CFAE1B21F5E400C1F8F2 /* RCTContextExecutorTests.m in Sources */,
|
||||
1497CFAD1B21F5E400C1F8F2 /* RCTBridgeTests.m in Sources */,
|
||||
1497CFB11B21F5E400C1F8F2 /* RCTEventDispatcherTests.m in Sources */,
|
||||
1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */,
|
||||
138D6A171B53CD440074A87E /* RCTCacheTests.m in Sources */,
|
||||
13DB03481B5D2ED500C27245 /* RCTJSONTests.m in Sources */,
|
||||
1497CFAC1B21F5E400C1F8F2 /* RCTAllocationTests.m in Sources */,
|
||||
13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */,
|
||||
138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -797,6 +824,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
141FC1211B222EBB004D5FFB /* IntegrationTests.m in Sources */,
|
||||
83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */,
|
||||
143BC5A11B21E45C00462512 /* UIExplorerSnapshotTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -1014,9 +1042,11 @@
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_STRICT_SELECTOR_MATCH = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
@@ -1028,6 +1058,7 @@
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
"$(SRCROOT)/../../Libraries/**",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
@@ -1068,9 +1099,11 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_STRICT_SELECTOR_MATCH = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
@@ -1082,6 +1115,7 @@
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
"$(SRCROOT)/../../Libraries/**",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
|
||||
@@ -14,49 +14,23 @@
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTJavaScriptLoader.h"
|
||||
#import "RCTRootView.h"
|
||||
|
||||
@interface AppDelegate() <RCTBridgeDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
NSURL *jsCodeLocation;
|
||||
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self
|
||||
launchOptions:launchOptions];
|
||||
|
||||
/**
|
||||
* Loading JavaScript code - uncomment the one you want.
|
||||
*
|
||||
* OPTION 1
|
||||
* Load from development server. Start the server from the repository root:
|
||||
*
|
||||
* $ npm start
|
||||
*
|
||||
* To run on device, change `localhost` to the IP address of your computer
|
||||
* (you can get this by typing `ifconfig` into the terminal and selecting the
|
||||
* `inet` value under `en0:`) and make sure your computer and iOS device are
|
||||
* on the same Wi-Fi network.
|
||||
*/
|
||||
|
||||
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle?dev=true"];
|
||||
|
||||
/**
|
||||
* OPTION 2
|
||||
* Load from pre-bundled file on disk. To re-generate the static bundle, `cd`
|
||||
* to your Xcode project folder and run
|
||||
*
|
||||
* $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.includeRequire.runModule.bundle' -o main.jsbundle
|
||||
*
|
||||
* then add the `main.jsbundle` file to your project and uncomment this line:
|
||||
*/
|
||||
|
||||
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
|
||||
#if RUNNING_ON_CI
|
||||
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
#endif
|
||||
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
|
||||
moduleName:@"UIExplorerApp"
|
||||
launchOptions:launchOptions];
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
|
||||
moduleName:@"UIExplorerApp"];
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UIViewController *rootViewController = [[UIViewController alloc] init];
|
||||
@@ -66,4 +40,50 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge
|
||||
{
|
||||
NSURL *sourceURL;
|
||||
|
||||
/**
|
||||
* Loading JavaScript code - uncomment the one you want.
|
||||
*
|
||||
* OPTION 1
|
||||
* Load from development server. Start the server from the repository root:
|
||||
*
|
||||
* $ npm start
|
||||
*
|
||||
* To run on device, change `localhost` to the IP address of your computer
|
||||
* (you can get this by typing `ifconfig` into the terminal and selecting the
|
||||
* `inet` value under `en0:`) and make sure your computer and iOS device are
|
||||
* on the same Wi-Fi network.
|
||||
*/
|
||||
|
||||
sourceURL = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle?dev=true"];
|
||||
|
||||
/**
|
||||
* OPTION 2
|
||||
* Load from pre-bundled file on disk. To re-generate the static bundle, `cd`
|
||||
* to your Xcode project folder and run
|
||||
*
|
||||
* $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle' -o main.jsbundle
|
||||
*
|
||||
* then add the `main.jsbundle` file to your project and uncomment this line:
|
||||
*/
|
||||
|
||||
// sourceURL = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
|
||||
#if RUNNING_ON_CI
|
||||
sourceURL = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
#endif
|
||||
|
||||
return sourceURL;
|
||||
}
|
||||
|
||||
- (void)loadSourceForBridge:(RCTBridge *)bridge
|
||||
withBlock:(RCTSourceLoadBlock)loadCallback
|
||||
{
|
||||
[RCTJavaScriptLoader loadBundleAtURL:[self sourceURLForBridge:bridge]
|
||||
onComplete:loadCallback];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var UIExplorerList = require('./UIExplorerList');
|
||||
var UIExplorerList = require('./UIExplorerList.ios');
|
||||
var {
|
||||
AppRegistry,
|
||||
NavigatorIOS,
|
||||
|
||||
@@ -26,11 +26,11 @@
|
||||
- (void)setUp
|
||||
{
|
||||
#if __LP64__
|
||||
RCTAssert(false, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
|
||||
RCTAssert(NO, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
|
||||
#endif
|
||||
|
||||
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
|
||||
RCTAssert(version.majorVersion == 8 || version.minorVersion == 3, @"Tests should be run on iOS 8.3, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
|
||||
RCTAssert(version.majorVersion == 8 || version.minorVersion >= 3, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
|
||||
_runner = RCTInitRunnerForApp(@"Examples/UIExplorer/UIExplorerIntegrationTests/js/IntegrationTestsApp", nil);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "UIView+React.h"
|
||||
|
||||
@interface RCTUIManager (Testing)
|
||||
|
||||
- (void)_manageChildren:(NSNumber *)containerReactTag
|
||||
moveFromIndices:(NSArray *)moveFromIndices
|
||||
moveToIndices:(NSArray *)moveToIndices
|
||||
addChildReactTags:(NSArray *)addChildReactTags
|
||||
addAtIndices:(NSArray *)addAtIndices
|
||||
removeAtIndices:(NSArray *)removeAtIndices
|
||||
registry:(RCTSparseArray *)registry;
|
||||
|
||||
@property (nonatomic, readonly) RCTSparseArray *viewRegistry;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTUIManagerScenarioTests : XCTestCase
|
||||
|
||||
@property (nonatomic, readwrite, strong) RCTUIManager *uiManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTUIManagerScenarioTests
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
|
||||
_uiManager = [[RCTUIManager alloc] init];
|
||||
|
||||
// Register 20 views to use in the tests
|
||||
for (NSInteger i = 1; i <= 20; i++) {
|
||||
UIView *registeredView = [[UIView alloc] init];
|
||||
[registeredView setReactTag:@(i)];
|
||||
_uiManager.viewRegistry[i] = registeredView;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testManagingChildrenToAddViews
|
||||
{
|
||||
UIView *containerView = _uiManager.viewRegistry[20];
|
||||
NSMutableArray *addedViews = [NSMutableArray array];
|
||||
|
||||
NSArray *tagsToAdd = @[@1, @2, @3, @4, @5];
|
||||
NSArray *addAtIndices = @[@0, @1, @2, @3, @4];
|
||||
for (NSNumber *tag in tagsToAdd) {
|
||||
[addedViews addObject:_uiManager.viewRegistry[tag]];
|
||||
}
|
||||
|
||||
// Add views 1-5 to view 20
|
||||
[_uiManager _manageChildren:@20
|
||||
moveFromIndices:nil
|
||||
moveToIndices:nil
|
||||
addChildReactTags:tagsToAdd
|
||||
addAtIndices:addAtIndices
|
||||
removeAtIndices:nil
|
||||
registry:_uiManager.viewRegistry];
|
||||
|
||||
XCTAssertTrue([[containerView reactSubviews] count] == 5,
|
||||
@"Expect to have 5 react subviews after calling manage children \
|
||||
with 5 tags to add, instead have %lu", (unsigned long)[[containerView reactSubviews] count]);
|
||||
for (UIView *view in addedViews) {
|
||||
XCTAssertTrue([view superview] == containerView,
|
||||
@"Expected to have manage children successfully add children");
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testManagingChildrenToRemoveViews
|
||||
{
|
||||
UIView *containerView = _uiManager.viewRegistry[20];
|
||||
NSMutableArray *removedViews = [NSMutableArray array];
|
||||
|
||||
NSArray *removeAtIndices = @[@0, @4, @8, @12, @16];
|
||||
for (NSNumber *index in removeAtIndices) {
|
||||
NSNumber *reactTag = @([index integerValue] + 2);
|
||||
[removedViews addObject:_uiManager.viewRegistry[reactTag]];
|
||||
}
|
||||
for (NSInteger i = 2; i < 20; i++) {
|
||||
UIView *view = _uiManager.viewRegistry[i];
|
||||
[containerView addSubview:view];
|
||||
}
|
||||
|
||||
// Remove views 1-5 from view 20
|
||||
[_uiManager _manageChildren:@20
|
||||
moveFromIndices:nil
|
||||
moveToIndices:nil
|
||||
addChildReactTags:nil
|
||||
addAtIndices:nil
|
||||
removeAtIndices:removeAtIndices
|
||||
registry:_uiManager.viewRegistry];
|
||||
|
||||
XCTAssertEqual(containerView.reactSubviews.count, (NSUInteger)13,
|
||||
@"Expect to have 13 react subviews after calling manage children\
|
||||
with 5 tags to remove and 18 prior children, instead have %zd",
|
||||
containerView.reactSubviews.count);
|
||||
for (UIView *view in removedViews) {
|
||||
XCTAssertTrue([view superview] == nil,
|
||||
@"Expected to have manage children successfully remove children");
|
||||
// After removing views are unregistered - we need to reregister
|
||||
_uiManager.viewRegistry[view.reactTag] = view;
|
||||
}
|
||||
for (NSInteger i = 2; i < 20; i++) {
|
||||
UIView *view = _uiManager.viewRegistry[i];
|
||||
if (![removedViews containsObject:view]) {
|
||||
XCTAssertTrue([view superview] == containerView,
|
||||
@"Should not have removed view with react tag %ld during delete but did", (long)i);
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We want to start with views 1-10 added at indices 0-9
|
||||
// Then we'll remove indices 2, 3, 5 and 8
|
||||
// Add views 11 and 12 to indices 0 and 6
|
||||
// And move indices 4 and 9 to 1 and 7
|
||||
// So in total it goes from:
|
||||
// [1,2,3,4,5,6,7,8,9,10]
|
||||
// to
|
||||
// [11,5,1,2,7,8,12,10]
|
||||
- (void)testManagingChildrenToAddRemoveAndMove
|
||||
{
|
||||
UIView *containerView = _uiManager.viewRegistry[20];
|
||||
|
||||
NSArray *removeAtIndices = @[@2, @3, @5, @8];
|
||||
NSArray *addAtIndices = @[@0, @6];
|
||||
NSArray *tagsToAdd = @[@11, @12];
|
||||
NSArray *moveFromIndices = @[@4, @9];
|
||||
NSArray *moveToIndices = @[@1, @7];
|
||||
|
||||
// We need to keep these in array to keep them around
|
||||
NSMutableArray *viewsToRemove = [NSMutableArray array];
|
||||
for (NSUInteger i = 0; i < removeAtIndices.count; i++) {
|
||||
NSNumber *reactTagToRemove = @([removeAtIndices[i] integerValue] + 1);
|
||||
UIView *viewToRemove = _uiManager.viewRegistry[reactTagToRemove];
|
||||
[viewsToRemove addObject:viewToRemove];
|
||||
}
|
||||
|
||||
for (NSInteger i = 1; i < 11; i++) {
|
||||
UIView *view = _uiManager.viewRegistry[i];
|
||||
[containerView addSubview:view];
|
||||
}
|
||||
|
||||
[_uiManager _manageChildren:@20
|
||||
moveFromIndices:moveFromIndices
|
||||
moveToIndices:moveToIndices
|
||||
addChildReactTags:tagsToAdd
|
||||
addAtIndices:addAtIndices
|
||||
removeAtIndices:removeAtIndices
|
||||
registry:_uiManager.viewRegistry];
|
||||
|
||||
XCTAssertTrue([[containerView reactSubviews] count] == 8,
|
||||
@"Expect to have 8 react subviews after calling manage children,\
|
||||
instead have the following subviews %@", [containerView reactSubviews]);
|
||||
|
||||
NSArray *expectedReactTags = @[@11, @5, @1, @2, @7, @8, @12, @10];
|
||||
for (NSUInteger i = 0; i < containerView.subviews.count; i++) {
|
||||
XCTAssertEqualObjects([[containerView reactSubviews][i] reactTag], expectedReactTags[i],
|
||||
@"Expected subview at index %ld to have react tag #%@ but has tag #%@",
|
||||
(long)i, expectedReactTags[i], [[containerView reactSubviews][i] reactTag]);
|
||||
}
|
||||
|
||||
// Clean up after ourselves
|
||||
for (NSInteger i = 1; i < 13; i++) {
|
||||
UIView *view = _uiManager.viewRegistry[i];
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
for (UIView *view in viewsToRemove) {
|
||||
_uiManager.viewRegistry[view.reactTag] = view;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
@@ -32,11 +32,12 @@
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
#ifdef __LP64__
|
||||
RCTAssert(!__LP64__, @"Snapshot tests should be run on 32-bit device simulators (e.g. iPhone 5)");
|
||||
#if __LP64__
|
||||
RCTAssert(NO, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
|
||||
#endif
|
||||
NSString *version = [[UIDevice currentDevice] systemVersion];
|
||||
RCTAssert([version isEqualToString:@"8.3"], @"Snapshot tests should be run on iOS 8.3, found %@", version);
|
||||
|
||||
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
|
||||
RCTAssert(version.majorVersion == 8 || version.minorVersion >= 3, @"Snapshot tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
|
||||
_runner = RCTInitRunnerForApp(@"Examples/UIExplorer/UIExplorerApp.ios", nil);
|
||||
_runner.recordMode = NO;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
require('regenerator/runtime');
|
||||
|
||||
var React = require('react-native');
|
||||
|
||||
var {
|
||||
@@ -59,12 +61,12 @@ var IntegrationTestsApp = React.createClass({
|
||||
<View style={styles.separator} />
|
||||
<ScrollView>
|
||||
{TESTS.map((test) => [
|
||||
<TouchableOpacity onPress={() => this.setState({test})}>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.testName}>
|
||||
{test.displayName}
|
||||
</Text>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
onPress={() => this.setState({test})}
|
||||
style={styles.row}>
|
||||
<Text style={styles.testName}>
|
||||
{test.displayName}
|
||||
</Text>
|
||||
</TouchableOpacity>,
|
||||
<View style={styles.separator} />
|
||||
])}
|
||||
|
||||
@@ -51,8 +51,7 @@ var LayoutEventsTest = React.createClass({
|
||||
() => {
|
||||
debug('layout animation done.');
|
||||
this.checkLayout(this.addWrapText);
|
||||
},
|
||||
(error) => { throw new Error(JSON.stringify(error)); }
|
||||
}
|
||||
);
|
||||
this.setState({viewStyle: {margin: 60}});
|
||||
},
|
||||
|
||||
@@ -17,13 +17,18 @@ var PromiseTest = React.createClass({
|
||||
|
||||
shouldResolve: false,
|
||||
shouldReject: false,
|
||||
shouldSucceedAsync: false,
|
||||
shouldThrowAsync: false,
|
||||
|
||||
componentDidMount() {
|
||||
Promise.all([
|
||||
this.testShouldResolve(),
|
||||
this.testShouldReject(),
|
||||
this.testShouldSucceedAsync(),
|
||||
this.testShouldThrowAsync(),
|
||||
]).then(() => RCTTestModule.markTestPassed(
|
||||
this.shouldResolve && this.shouldReject
|
||||
this.shouldResolve && this.shouldReject &&
|
||||
this.shouldSucceedAsync && this.shouldThrowAsync
|
||||
));
|
||||
},
|
||||
|
||||
@@ -41,6 +46,24 @@ var PromiseTest = React.createClass({
|
||||
.catch(() => this.shouldReject = true);
|
||||
},
|
||||
|
||||
async testShouldSucceedAsync() {
|
||||
try {
|
||||
await RCTTestModule.shouldResolve();
|
||||
this.shouldSucceedAsync = true;
|
||||
} catch (e) {
|
||||
this.shouldSucceedAsync = false;
|
||||
}
|
||||
},
|
||||
|
||||
async testShouldThrowAsync() {
|
||||
try {
|
||||
await RCTTestModule.shouldReject();
|
||||
this.shouldThrowAsync = false;
|
||||
} catch (e) {
|
||||
this.shouldThrowAsync = true;
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
return <React.View />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
AppRegistry,
|
||||
Settings,
|
||||
StyleSheet,
|
||||
} = React;
|
||||
|
||||
var { TestModule } = React.addons;
|
||||
|
||||
import type { NavigationContext } from 'NavigationContext';
|
||||
|
||||
var UIExplorerListBase = require('./UIExplorerListBase');
|
||||
|
||||
var COMPONENTS = [
|
||||
require('./ActivityIndicatorIOSExample'),
|
||||
require('./DatePickerIOSExample'),
|
||||
require('./ImageExample'),
|
||||
require('./LayoutEventsExample'),
|
||||
require('./ListViewExample'),
|
||||
require('./ListViewGridLayoutExample'),
|
||||
require('./ListViewPagingExample'),
|
||||
require('./MapViewExample'),
|
||||
require('./ModalExample'),
|
||||
require('./Navigator/NavigatorExample'),
|
||||
require('./NavigatorIOSColorsExample'),
|
||||
require('./NavigatorIOSExample'),
|
||||
require('./PickerIOSExample'),
|
||||
require('./ProgressViewIOSExample'),
|
||||
require('./ScrollViewExample'),
|
||||
require('./SegmentedControlIOSExample'),
|
||||
require('./SliderIOSExample'),
|
||||
require('./SwitchIOSExample'),
|
||||
require('./TabBarIOSExample'),
|
||||
require('./TextExample.ios'),
|
||||
require('./TextInputExample'),
|
||||
require('./TouchableExample'),
|
||||
require('./ViewExample'),
|
||||
require('./WebViewExample'),
|
||||
];
|
||||
|
||||
var APIS = [
|
||||
require('./AccessibilityIOSExample'),
|
||||
require('./ActionSheetIOSExample'),
|
||||
require('./AdSupportIOSExample'),
|
||||
require('./AlertIOSExample'),
|
||||
require('./AnimationExample/AnExApp'),
|
||||
require('./AppStateIOSExample'),
|
||||
require('./AsyncStorageExample'),
|
||||
require('./BorderExample'),
|
||||
require('./CameraRollExample.ios'),
|
||||
require('./GeolocationExample'),
|
||||
require('./LayoutExample'),
|
||||
require('./NetInfoExample'),
|
||||
require('./PanResponderExample'),
|
||||
require('./PointerEventsExample'),
|
||||
require('./PushNotificationIOSExample'),
|
||||
require('./StatusBarIOSExample'),
|
||||
require('./TimerExample'),
|
||||
require('./VibrationIOSExample'),
|
||||
require('./XHRExample'),
|
||||
require('./ImageEditingExample'),
|
||||
];
|
||||
|
||||
// Register suitable examples for snapshot tests
|
||||
COMPONENTS.concat(APIS).forEach((Example) => {
|
||||
if (Example.displayName) {
|
||||
var Snapshotter = React.createClass({
|
||||
componentDidMount: function() {
|
||||
// View is still blank after first RAF :\
|
||||
global.requestAnimationFrame(() =>
|
||||
global.requestAnimationFrame(() => TestModule.verifySnapshot(
|
||||
TestModule.markTestPassed
|
||||
)
|
||||
));
|
||||
},
|
||||
render: function() {
|
||||
var Renderable = UIExplorerListBase.makeRenderable(Example);
|
||||
return <Renderable />;
|
||||
},
|
||||
});
|
||||
AppRegistry.registerComponent(Example.displayName, () => Snapshotter);
|
||||
}
|
||||
});
|
||||
|
||||
type Props = {
|
||||
navigator: {
|
||||
navigationContext: NavigationContext,
|
||||
push: (route: {title: string, component: ReactClass<any,any,any>}) => void,
|
||||
},
|
||||
onExternalExampleRequested: Function,
|
||||
};
|
||||
|
||||
class UIExplorerList extends React.Component {
|
||||
props: Props;
|
||||
|
||||
render() {
|
||||
return (
|
||||
<UIExplorerListBase
|
||||
components={COMPONENTS}
|
||||
apis={APIS}
|
||||
searchText={Settings.get('searchText')}
|
||||
renderAdditionalView={this.renderAdditionalView.bind(this)}
|
||||
search={this.search.bind(this)}
|
||||
onPressRow={this.onPressRow.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
var wasUIExplorer = false;
|
||||
var didOpenExample = false;
|
||||
|
||||
this.props.navigator.navigationContext.addListener('didfocus', (event) => {
|
||||
var isUIExplorer = event.data.route.title === 'UIExplorer';
|
||||
|
||||
if (!didOpenExample && isUIExplorer) {
|
||||
didOpenExample = true;
|
||||
|
||||
var visibleExampleTitle = Settings.get('visibleExample');
|
||||
if (visibleExampleTitle) {
|
||||
var predicate = (example) => example.title === visibleExampleTitle;
|
||||
var foundExample = APIS.find(predicate) || COMPONENTS.find(predicate);
|
||||
if (foundExample) {
|
||||
setTimeout(() => this._openExample(foundExample), 100);
|
||||
}
|
||||
} else if (!wasUIExplorer && isUIExplorer) {
|
||||
Settings.set({visibleExample: null});
|
||||
}
|
||||
}
|
||||
|
||||
wasUIExplorer = isUIExplorer;
|
||||
});
|
||||
}
|
||||
|
||||
renderAdditionalView(renderRow: Function, renderTextInput: Function): React.Component {
|
||||
return renderTextInput(styles.searchTextInput);
|
||||
}
|
||||
|
||||
search(text: mixed) {
|
||||
Settings.set({searchText: text});
|
||||
}
|
||||
|
||||
_openExample(example: any) {
|
||||
if (example.external) {
|
||||
this.props.onExternalExampleRequested(example);
|
||||
return;
|
||||
}
|
||||
|
||||
var Component = UIExplorerListBase.makeRenderable(example);
|
||||
this.props.navigator.push({
|
||||
title: Component.title,
|
||||
component: Component,
|
||||
});
|
||||
}
|
||||
|
||||
onPressRow(example: any) {
|
||||
Settings.set({visibleExample: example.title});
|
||||
this._openExample(example);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
searchTextInput: {
|
||||
height: 30,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = UIExplorerList;
|
||||
@@ -1,351 +0,0 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
AppRegistry,
|
||||
ListView,
|
||||
PixelRatio,
|
||||
Platform,
|
||||
Settings,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var { TestModule } = React.addons;
|
||||
|
||||
import type { ExampleModule } from 'ExampleTypes';
|
||||
import type { NavigationContext } from 'NavigationContext';
|
||||
|
||||
var createExamplePage = require('./createExamplePage');
|
||||
|
||||
var COMMON_COMPONENTS = [
|
||||
require('./ImageExample'),
|
||||
require('./LayoutEventsExample'),
|
||||
require('./ListViewExample'),
|
||||
require('./ListViewPagingExample'),
|
||||
require('./MapViewExample'),
|
||||
require('./Navigator/NavigatorExample'),
|
||||
require('./ScrollViewExample'),
|
||||
require('./TextInputExample'),
|
||||
require('./TouchableExample'),
|
||||
require('./ViewExample'),
|
||||
require('./WebViewExample'),
|
||||
];
|
||||
|
||||
var COMMON_APIS = [
|
||||
require('./AnimationExample/AnExApp'),
|
||||
require('./GeolocationExample'),
|
||||
require('./LayoutExample'),
|
||||
require('./PanResponderExample'),
|
||||
require('./PointerEventsExample'),
|
||||
];
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
var COMPONENTS = COMMON_COMPONENTS.concat([
|
||||
require('./ActivityIndicatorIOSExample'),
|
||||
require('./DatePickerIOSExample'),
|
||||
require('./NavigatorIOSColorsExample'),
|
||||
require('./NavigatorIOSExample'),
|
||||
require('./PickerIOSExample'),
|
||||
require('./ProgressViewIOSExample'),
|
||||
require('./SegmentedControlIOSExample'),
|
||||
require('./SliderIOSExample'),
|
||||
require('./SwitchIOSExample'),
|
||||
require('./TabBarIOSExample'),
|
||||
require('./TextExample.ios'),
|
||||
]);
|
||||
|
||||
var APIS = COMMON_APIS.concat([
|
||||
require('./AccessibilityIOSExample'),
|
||||
require('./ActionSheetIOSExample'),
|
||||
require('./AdSupportIOSExample'),
|
||||
require('./AlertIOSExample'),
|
||||
require('./AppStateIOSExample'),
|
||||
require('./AsyncStorageExample'),
|
||||
require('./BorderExample'),
|
||||
require('./CameraRollExample.ios'),
|
||||
require('./NetInfoExample'),
|
||||
require('./PushNotificationIOSExample'),
|
||||
require('./StatusBarIOSExample'),
|
||||
require('./TimerExample'),
|
||||
require('./VibrationIOSExample'),
|
||||
require('./XHRExample'),
|
||||
]);
|
||||
|
||||
} else if (Platform.OS === 'android') {
|
||||
var COMPONENTS = COMMON_COMPONENTS.concat([
|
||||
]);
|
||||
|
||||
var APIS = COMMON_APIS.concat([
|
||||
]);
|
||||
}
|
||||
|
||||
var ds = new ListView.DataSource({
|
||||
rowHasChanged: (r1, r2) => r1 !== r2,
|
||||
sectionHeaderHasChanged: (h1, h2) => h1 !== h2,
|
||||
});
|
||||
|
||||
function makeRenderable(example: any): ReactClass<any, any, any> {
|
||||
return example.examples ?
|
||||
createExamplePage(null, example) :
|
||||
example;
|
||||
}
|
||||
|
||||
// Register suitable examples for snapshot tests
|
||||
COMPONENTS.concat(APIS).forEach((Example) => {
|
||||
if (Example.displayName) {
|
||||
var Snapshotter = React.createClass({
|
||||
componentDidMount: function() {
|
||||
// View is still blank after first RAF :\
|
||||
global.requestAnimationFrame(() =>
|
||||
global.requestAnimationFrame(() => TestModule.verifySnapshot(
|
||||
TestModule.markTestPassed
|
||||
)
|
||||
));
|
||||
},
|
||||
render: function() {
|
||||
var Renderable = makeRenderable(Example);
|
||||
return <Renderable />;
|
||||
},
|
||||
});
|
||||
AppRegistry.registerComponent(Example.displayName, () => Snapshotter);
|
||||
}
|
||||
});
|
||||
|
||||
type Props = {
|
||||
navigator: {
|
||||
navigationContext: NavigationContext,
|
||||
push: (route: {title: string, component: ReactClass<any,any,any>}) => void,
|
||||
},
|
||||
onExternalExampleRequested: Function,
|
||||
onSelectExample: Function,
|
||||
isInDrawer: bool,
|
||||
};
|
||||
|
||||
class UIExplorerList extends React.Component {
|
||||
props: Props;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
dataSource: ds.cloneWithRowsAndSections({
|
||||
components: COMPONENTS,
|
||||
apis: APIS,
|
||||
}),
|
||||
searchText: Platform.OS === 'ios' ? Settings.get('searchText') : '',
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.props.navigator.navigationContext.addListener('didfocus', function(event) {
|
||||
if (event.data.route.title === 'UIExplorer') {
|
||||
Settings.set({visibleExample: null});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._search(this.state.searchText);
|
||||
|
||||
var visibleExampleTitle = Settings.get('visibleExample');
|
||||
if (visibleExampleTitle) {
|
||||
var predicate = (example) => example.title === visibleExampleTitle;
|
||||
var foundExample = APIS.find(predicate) || COMPONENTS.find(predicate);
|
||||
if (foundExample) {
|
||||
setTimeout(() => this._openExample(foundExample), 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (Platform.OS === 'ios' ||
|
||||
(Platform.OS === 'android' && !this.props.isInDrawer)) {
|
||||
var platformTextInputStyle =
|
||||
Platform.OS === 'ios' ? styles.searchTextInputIOS :
|
||||
Platform.OS === 'android' ? styles.searchTextInputAndroid : {};
|
||||
var textInput = (
|
||||
<View style={styles.searchRow}>
|
||||
<TextInput
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
clearButtonMode="always"
|
||||
onChangeText={this._search.bind(this)}
|
||||
placeholder="Search..."
|
||||
style={[styles.searchTextInput, platformTextInputStyle]}
|
||||
testID="explorer_search"
|
||||
value={this.state.searchText}
|
||||
/>
|
||||
</View>);
|
||||
}
|
||||
|
||||
var homePage;
|
||||
if (Platform.OS === 'android' && this.props.isInDrawer) {
|
||||
homePage = this._renderRow({
|
||||
title: 'UIExplorer',
|
||||
description: 'List of examples',
|
||||
}, -1);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.listContainer}>
|
||||
{textInput}
|
||||
{homePage}
|
||||
<ListView
|
||||
style={styles.list}
|
||||
dataSource={this.state.dataSource}
|
||||
renderRow={this._renderRow.bind(this)}
|
||||
renderSectionHeader={this._renderSectionHeader}
|
||||
keyboardShouldPersistTaps={true}
|
||||
automaticallyAdjustContentInsets={false}
|
||||
keyboardDismissMode="on-drag"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_renderSectionHeader(data: any, section: string) {
|
||||
return (
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text style={styles.sectionHeaderTitle}>
|
||||
{section.toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_renderRow(example: any, i: number) {
|
||||
return (
|
||||
<View key={i}>
|
||||
<TouchableHighlight onPress={() => this._onPressRow(example)}>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowTitleText}>
|
||||
{example.title}
|
||||
</Text>
|
||||
<Text style={styles.rowDetailText}>
|
||||
{example.description}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<View style={styles.separator} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_search(text: mixed) {
|
||||
var regex = new RegExp(text, 'i');
|
||||
var filter = (component) => regex.test(component.title);
|
||||
|
||||
this.setState({
|
||||
dataSource: ds.cloneWithRowsAndSections({
|
||||
components: COMPONENTS.filter(filter),
|
||||
apis: APIS.filter(filter),
|
||||
}),
|
||||
searchText: text,
|
||||
});
|
||||
Settings.set({searchText: text});
|
||||
}
|
||||
|
||||
_openExample(example: any) {
|
||||
if (example.external) {
|
||||
this.props.onExternalExampleRequested(example);
|
||||
return;
|
||||
}
|
||||
|
||||
var Component = makeRenderable(example);
|
||||
if (Platform.OS === 'ios') {
|
||||
this.props.navigator.push({
|
||||
title: Component.title,
|
||||
component: Component,
|
||||
});
|
||||
} else if (Platform.OS === 'android') {
|
||||
this.props.onSelectExample({
|
||||
title: Component.title,
|
||||
component: Component,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_onPressRow(example: any) {
|
||||
Settings.set({visibleExample: example.title});
|
||||
this._openExample(example);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
listContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
list: {
|
||||
backgroundColor: '#eeeeee',
|
||||
},
|
||||
sectionHeader: {
|
||||
padding: 5,
|
||||
},
|
||||
group: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
sectionHeaderTitle: {
|
||||
fontWeight: '500',
|
||||
fontSize: 11,
|
||||
},
|
||||
row: {
|
||||
backgroundColor: 'white',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 8,
|
||||
},
|
||||
separator: {
|
||||
height: 1 / PixelRatio.get(),
|
||||
backgroundColor: '#bbbbbb',
|
||||
marginLeft: 15,
|
||||
},
|
||||
rowTitleText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
},
|
||||
rowDetailText: {
|
||||
fontSize: 15,
|
||||
color: '#888888',
|
||||
lineHeight: 20,
|
||||
},
|
||||
searchRow: {
|
||||
backgroundColor: '#eeeeee',
|
||||
paddingTop: 75,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
paddingBottom: 10,
|
||||
},
|
||||
searchTextInput: {
|
||||
backgroundColor: 'white',
|
||||
borderColor: '#cccccc',
|
||||
borderRadius: 3,
|
||||
borderWidth: 1,
|
||||
paddingLeft: 8,
|
||||
},
|
||||
searchTextInputIOS: {
|
||||
height: 30,
|
||||
},
|
||||
searchTextInputAndroid: {
|
||||
padding: 2,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = UIExplorerList;
|
||||
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
ListView,
|
||||
PixelRatio,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = React;
|
||||
var createExamplePage = require('./createExamplePage');
|
||||
|
||||
var ds = new ListView.DataSource({
|
||||
rowHasChanged: (r1, r2) => r1 !== r2,
|
||||
sectionHeaderHasChanged: (h1, h2) => h1 !== h2,
|
||||
});
|
||||
|
||||
class UIExplorerListBase extends React.Component {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
dataSource: ds.cloneWithRowsAndSections({
|
||||
components: [],
|
||||
apis: [],
|
||||
}),
|
||||
searchText: this.props.searchText,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.search(this.state.searchText);
|
||||
}
|
||||
|
||||
render() {
|
||||
var topView = this.props.renderAdditionalView &&
|
||||
this.props.renderAdditionalView(this.renderRow.bind(this), this.renderTextInput.bind(this));
|
||||
|
||||
return (
|
||||
<View style={styles.listContainer}>
|
||||
{topView}
|
||||
<ListView
|
||||
style={styles.list}
|
||||
dataSource={this.state.dataSource}
|
||||
renderRow={this.renderRow.bind(this)}
|
||||
renderSectionHeader={this._renderSectionHeader}
|
||||
keyboardShouldPersistTaps={true}
|
||||
automaticallyAdjustContentInsets={false}
|
||||
keyboardDismissMode="on-drag"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderTextInput(searchTextInputStyle: any) {
|
||||
return (
|
||||
<View style={styles.searchRow}>
|
||||
<TextInput
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
clearButtonMode="always"
|
||||
onChangeText={this.search.bind(this)}
|
||||
placeholder="Search..."
|
||||
style={[styles.searchTextInput, searchTextInputStyle]}
|
||||
testID="explorer_search"
|
||||
value={this.state.searchText}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_renderSectionHeader(data: any, section: string) {
|
||||
return (
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text style={styles.sectionHeaderTitle}>
|
||||
{section.toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderRow(example: any, i: number) {
|
||||
return (
|
||||
<View key={i}>
|
||||
<TouchableHighlight onPress={() => this.onPressRow(example)}>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowTitleText}>
|
||||
{example.title}
|
||||
</Text>
|
||||
<Text style={styles.rowDetailText}>
|
||||
{example.description}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<View style={styles.separator} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
search(text: mixed): void {
|
||||
this.props.search && this.props.search(text);
|
||||
|
||||
var regex = new RegExp(text, 'i');
|
||||
var filter = (component) => regex.test(component.title);
|
||||
|
||||
this.setState({
|
||||
dataSource: ds.cloneWithRowsAndSections({
|
||||
components: this.props.components.filter(filter),
|
||||
apis: this.props.apis.filter(filter),
|
||||
}),
|
||||
searchText: text,
|
||||
});
|
||||
}
|
||||
|
||||
onPressRow(example: any): void {
|
||||
this.props.onPressRow && this.props.onPressRow(example);
|
||||
}
|
||||
|
||||
static makeRenderable(example: any): ReactClass<any, any, any> {
|
||||
return example.examples ?
|
||||
createExamplePage(null, example) :
|
||||
example;
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
listContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
list: {
|
||||
backgroundColor: '#eeeeee',
|
||||
},
|
||||
sectionHeader: {
|
||||
padding: 5,
|
||||
},
|
||||
group: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
sectionHeaderTitle: {
|
||||
fontWeight: '500',
|
||||
fontSize: 11,
|
||||
},
|
||||
row: {
|
||||
backgroundColor: 'white',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 8,
|
||||
},
|
||||
separator: {
|
||||
height: 1 / PixelRatio.get(),
|
||||
backgroundColor: '#bbbbbb',
|
||||
marginLeft: 15,
|
||||
},
|
||||
rowTitleText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
},
|
||||
rowDetailText: {
|
||||
fontSize: 15,
|
||||
color: '#888888',
|
||||
lineHeight: 20,
|
||||
},
|
||||
searchRow: {
|
||||
backgroundColor: '#eeeeee',
|
||||
paddingTop: 75,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
paddingBottom: 10,
|
||||
},
|
||||
searchTextInput: {
|
||||
backgroundColor: 'white',
|
||||
borderColor: '#cccccc',
|
||||
borderRadius: 3,
|
||||
borderWidth: 1,
|
||||
paddingLeft: 8,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = UIExplorerListBase;
|
||||
@@ -1,23 +1,34 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTContextExecutor.h"
|
||||
#import "RCTModuleMethod.h"
|
||||
#import "RCTRootView.h"
|
||||
|
||||
#define RUN_RUNLOOP_WHILE(CONDITION, TIMEOUT) \
|
||||
#define RUN_RUNLOOP_WHILE(CONDITION) \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
|
||||
NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:TIMEOUT]; \
|
||||
NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:0.1]; \
|
||||
while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \
|
||||
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; \
|
||||
} \
|
||||
_Pragma("clang diagnostic pop")
|
||||
|
||||
#define DEFAULT_TIMEOUT 2
|
||||
|
||||
@interface RCTBridge (RCTAllocationTests)
|
||||
|
||||
@property (nonatomic, weak) RCTBridge *batchedBridge;
|
||||
@@ -52,6 +63,11 @@ RCT_EXPORT_MODULE();
|
||||
_valid = NO;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(test:(__unused NSString *)a
|
||||
:(__unused NSNumber *)b
|
||||
:(__unused RCTResponseSenderBlock)c
|
||||
:(__unused RCTResponseErrorBlock)d) {}
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTAllocationTests : XCTestCase
|
||||
@@ -71,7 +87,6 @@ RCT_EXPORT_MODULE();
|
||||
(void)view;
|
||||
}
|
||||
|
||||
sleep(DEFAULT_TIMEOUT);
|
||||
XCTAssertNil(weakBridge, @"RCTBridge should have been deallocated");
|
||||
}
|
||||
|
||||
@@ -88,12 +103,7 @@ RCT_EXPORT_MODULE();
|
||||
(void)bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep on the main thread to allow js thread deallocations then run the runloop
|
||||
* to allow the module to be deallocated on the main thread
|
||||
*/
|
||||
sleep(1);
|
||||
RUN_RUNLOOP_WHILE(module.isValid, 1)
|
||||
RUN_RUNLOOP_WHILE(module.isValid)
|
||||
XCTAssertFalse(module.isValid, @"AllocationTestModule should have been invalidated by the bridge");
|
||||
}
|
||||
|
||||
@@ -112,15 +122,23 @@ RCT_EXPORT_MODULE();
|
||||
(void)bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep on the main thread to allow js thread deallocations then run the runloop
|
||||
* to allow the module to be deallocated on the main thread
|
||||
*/
|
||||
sleep(1);
|
||||
RUN_RUNLOOP_WHILE(weakModule, 1)
|
||||
RUN_RUNLOOP_WHILE(weakModule)
|
||||
XCTAssertNil(weakModule, @"AllocationTestModule should have been deallocated");
|
||||
}
|
||||
|
||||
- (void)testModuleMethodsAreDeallocated
|
||||
{
|
||||
__weak RCTModuleMethod *weakMethod;
|
||||
@autoreleasepool {
|
||||
__autoreleasing RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:@"test:(NSString *)a :(nonnull NSNumber *)b :(RCTResponseSenderBlock)c :(RCTResponseErrorBlock)d" JSMethodName:@"" moduleClass:[AllocationTestModule class]];
|
||||
weakMethod = method;
|
||||
XCTAssertNotNil(method, @"RCTModuleMethod should have been created");
|
||||
}
|
||||
|
||||
RUN_RUNLOOP_WHILE(weakMethod)
|
||||
XCTAssertNil(weakMethod, @"RCTModuleMethod should have been deallocated");
|
||||
}
|
||||
|
||||
- (void)testJavaScriptExecutorIsDeallocated
|
||||
{
|
||||
__weak id<RCTJavaScriptExecutor> weakExecutor;
|
||||
@@ -133,8 +151,7 @@ RCT_EXPORT_MODULE();
|
||||
(void)bridge;
|
||||
}
|
||||
|
||||
RUN_RUNLOOP_WHILE(weakExecutor, 1);
|
||||
sleep(1);
|
||||
RUN_RUNLOOP_WHILE(weakExecutor);
|
||||
XCTAssertNil(weakExecutor, @"JavaScriptExecutor should have been released");
|
||||
}
|
||||
|
||||
@@ -146,13 +163,12 @@ RCT_EXPORT_MODULE();
|
||||
moduleProvider:nil
|
||||
launchOptions:nil];
|
||||
id executor = [bridge.batchedBridge valueForKey:@"javaScriptExecutor"];
|
||||
RUN_RUNLOOP_WHILE(!(weakContext = [executor valueForKey:@"context"]), DEFAULT_TIMEOUT);
|
||||
RUN_RUNLOOP_WHILE(!(weakContext = [executor valueForKey:@"context"]));
|
||||
XCTAssertNotNil(weakContext, @"RCTJavaScriptContext should have been created");
|
||||
(void)bridge;
|
||||
}
|
||||
|
||||
RUN_RUNLOOP_WHILE(weakContext, 1);
|
||||
sleep(1);
|
||||
RUN_RUNLOOP_WHILE(weakContext);
|
||||
XCTAssertNil(weakContext, @"RCTJavaScriptContext should have been deallocated");
|
||||
}
|
||||
|
||||
@@ -164,12 +180,11 @@ RCT_EXPORT_MODULE();
|
||||
__weak id rootContentView;
|
||||
@autoreleasepool {
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@""];
|
||||
RUN_RUNLOOP_WHILE(!(rootContentView = [rootView valueForKey:@"contentView"]), DEFAULT_TIMEOUT)
|
||||
RUN_RUNLOOP_WHILE(!(rootContentView = [rootView valueForKey:@"contentView"]))
|
||||
XCTAssertTrue([rootContentView isValid], @"RCTContentView should be valid");
|
||||
(void)rootView;
|
||||
}
|
||||
|
||||
sleep(DEFAULT_TIMEOUT);
|
||||
XCTAssertFalse([rootContentView isValid], @"RCTContentView should have been invalidated");
|
||||
}
|
||||
|
||||
@@ -184,8 +199,7 @@ RCT_EXPORT_MODULE();
|
||||
[bridge reload];
|
||||
}
|
||||
|
||||
// Use RUN_RUNLOOP_WHILE because `batchedBridge` deallocates on the main thread.
|
||||
RUN_RUNLOOP_WHILE(batchedBridge != nil, DEFAULT_TIMEOUT)
|
||||
RUN_RUNLOOP_WHILE(batchedBridge != nil)
|
||||
|
||||
XCTAssertNotNil(bridge, @"RCTBridge should not have been deallocated");
|
||||
XCTAssertNil(batchedBridge, @"RCTBatchedBridge should have been deallocated");
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
@@ -12,7 +24,7 @@
|
||||
|
||||
@property (nonatomic, strong, readonly) RCTBridge *batchedBridge;
|
||||
|
||||
- (void)_handleBuffer:(id)buffer context:(NSNumber *)context;
|
||||
- (void)_handleBuffer:(id)buffer;
|
||||
- (void)setUp;
|
||||
|
||||
@end
|
||||
@@ -45,7 +57,6 @@ RCT_EXPORT_MODULE()
|
||||
- (void)executeJSCall:(__unused NSString *)name
|
||||
method:(__unused NSString *)method
|
||||
arguments:(__unused NSArray *)arguments
|
||||
context:(__unused NSNumber *)executorID
|
||||
callback:(RCTJavaScriptCallback)onComplete
|
||||
{
|
||||
onComplete(nil, nil);
|
||||
@@ -109,10 +120,22 @@ RCT_EXPORT_MODULE(TestModule)
|
||||
[_bridge invalidate];
|
||||
}
|
||||
|
||||
#define RUN_RUNLOOP_WHILE(CONDITION) \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
|
||||
NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:0.1]; \
|
||||
while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \
|
||||
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; \
|
||||
} \
|
||||
_Pragma("clang diagnostic pop")
|
||||
|
||||
- (void)testHookRegistration
|
||||
{
|
||||
TestExecutor *executor = [_bridge.batchedBridge valueForKey:@"_javaScriptExecutor"];
|
||||
NSString *injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"];
|
||||
|
||||
NSString *injectedStuff;
|
||||
RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"]));
|
||||
|
||||
NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL);
|
||||
NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"];
|
||||
NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"];
|
||||
@@ -131,7 +154,10 @@ RCT_EXPORT_MODULE(TestModule)
|
||||
- (void)testCallNativeMethod
|
||||
{
|
||||
TestExecutor *executor = [_bridge.batchedBridge valueForKey:@"_javaScriptExecutor"];
|
||||
NSString *injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"];
|
||||
|
||||
NSString *injectedStuff;
|
||||
RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"]));
|
||||
|
||||
NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL);
|
||||
NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"];
|
||||
NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"];
|
||||
@@ -143,7 +169,7 @@ RCT_EXPORT_MODULE(TestModule)
|
||||
NSArray *args = @[@1234, @5678, @"stringy", @{@"a": @1}, @42];
|
||||
NSArray *buffer = @[@[testModuleID], @[testMethodID], @[args], @[], @1234567];
|
||||
|
||||
[_bridge.batchedBridge _handleBuffer:buffer context:RCTGetExecutorID(executor)];
|
||||
[_bridge.batchedBridge _handleBuffer:buffer];
|
||||
|
||||
dispatch_sync(_methodQueue, ^{
|
||||
// clear the queue
|
||||
@@ -159,7 +185,7 @@ RCT_EXPORT_MODULE(TestModule)
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(testMethod:(NSInteger)integer
|
||||
number:(NSNumber *)number
|
||||
number:(nonnull NSNumber *)number
|
||||
string:(NSString *)string
|
||||
dictionary:(NSDictionary *)dict
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "RCTCache.h"
|
||||
|
||||
// Silence silly sign warnings when using int literals
|
||||
#pragma clang diagnostic ignored "-Wsign-compare"
|
||||
|
||||
@interface RCTCache (Private)
|
||||
|
||||
- (void)cleanUpAllObjects;
|
||||
- (void)resequence;
|
||||
- (NSDictionary *)cache;
|
||||
- (void)setSequenceNumber:(NSInteger)number;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTCacheTests : XCTestCase
|
||||
|
||||
@property (nonatomic, strong) RCTCache *cache;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTCacheTests
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
self.cache = [[RCTCache alloc] init];
|
||||
self.cache.countLimit = 3;
|
||||
self.cache.totalCostLimit = 100;
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
self.cache = nil;
|
||||
}
|
||||
|
||||
- (void)testInsertion
|
||||
{
|
||||
[self.cache setObject:@1 forKey:@"foo" cost:1];
|
||||
[self.cache setObject:@2 forKey:@"bar" cost:2];
|
||||
[self.cache setObject:@3 forKey:@"baz" cost:3];
|
||||
|
||||
XCTAssertEqual([self.cache count], 3);
|
||||
XCTAssertEqual([self.cache totalCost], 6);
|
||||
}
|
||||
|
||||
- (void)testRemoval
|
||||
{
|
||||
[self.cache setObject:@1 forKey:@"foo" cost:1];
|
||||
[self.cache setObject:@2 forKey:@"bar" cost:2];
|
||||
[self.cache setObject:@3 forKey:@"baz" cost:3];
|
||||
|
||||
[self.cache removeObjectForKey:@"bar"];
|
||||
|
||||
XCTAssertEqual([self.cache count], 2);
|
||||
XCTAssertNil([self.cache objectForKey:@"bar"]);
|
||||
}
|
||||
|
||||
- (void)testCountEviction
|
||||
{
|
||||
[self.cache setObject:@1 forKey:@"foo"];
|
||||
[self.cache setObject:@2 forKey:@"bar"];
|
||||
[self.cache setObject:@3 forKey:@"baz"];
|
||||
[self.cache setObject:@4 forKey:@"bam"];
|
||||
|
||||
XCTAssertEqual([self.cache count], 3);
|
||||
XCTAssertNil([self.cache objectForKey:@"foo"]);
|
||||
|
||||
[self.cache setObject:@5 forKey:@"boo"];
|
||||
|
||||
XCTAssertEqual([self.cache count], 3);
|
||||
XCTAssertNil([self.cache objectForKey:@"bar"]);
|
||||
}
|
||||
|
||||
- (void)testCostEviction
|
||||
{
|
||||
[self.cache setObject:@1 forKey:@"foo" cost:99];
|
||||
[self.cache setObject:@2 forKey:@"bar" cost:2];
|
||||
|
||||
XCTAssertEqual([self.cache count], 1);
|
||||
XCTAssertEqual([self.cache totalCost], 2);
|
||||
XCTAssertNil([self.cache objectForKey:@"foo"]);
|
||||
|
||||
[self.cache setObject:@3 forKey:@"baz" cost:999];
|
||||
|
||||
XCTAssertEqual([self.cache count], 0);
|
||||
XCTAssertEqual([self.cache totalCost], 0);
|
||||
}
|
||||
|
||||
- (void)testCleanup
|
||||
{
|
||||
[self.cache setObject:@1 forKey:@"foo"];
|
||||
[self.cache setObject:@2 forKey:@"bar"];
|
||||
[self.cache setObject:@3 forKey:@"baz"];
|
||||
|
||||
//simulate memory warning
|
||||
[self.cache cleanUpAllObjects];
|
||||
|
||||
XCTAssertEqual([self.cache count], 0);
|
||||
XCTAssertEqual([self.cache totalCost], 0);
|
||||
}
|
||||
|
||||
- (void)testResequence
|
||||
{
|
||||
[self.cache setObject:@1 forKey:@"foo"];
|
||||
[self.cache setObject:@2 forKey:@"bar"];
|
||||
[self.cache setObject:@3 forKey:@"baz"];
|
||||
|
||||
[self.cache resequence];
|
||||
|
||||
NSDictionary *innerCache = [self.cache cache];
|
||||
XCTAssertEqualObjects([innerCache[@"foo"] valueForKey:@"sequenceNumber"], @0);
|
||||
XCTAssertEqualObjects([innerCache[@"bar"] valueForKey:@"sequenceNumber"], @1);
|
||||
XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @2);
|
||||
|
||||
[self.cache removeObjectForKey:@"foo"];
|
||||
[self.cache resequence];
|
||||
|
||||
XCTAssertEqualObjects([innerCache[@"bar"] valueForKey:@"sequenceNumber"], @0);
|
||||
XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @1);
|
||||
}
|
||||
|
||||
- (void)testResequenceTrigger
|
||||
{
|
||||
[self.cache setObject:@1 forKey:@"foo"];
|
||||
[self.cache setObject:@2 forKey:@"bar"];
|
||||
|
||||
//first object should now be bar with sequence number of 1
|
||||
[self.cache removeObjectForKey:@"foo"];
|
||||
|
||||
//should trigger resequence
|
||||
[self.cache setSequenceNumber:NSIntegerMax];
|
||||
[self.cache setObject:@3 forKey:@"baz"];
|
||||
|
||||
NSDictionary *innerCache = [self.cache cache];
|
||||
XCTAssertEqualObjects([innerCache[@"bar"] valueForKey:@"sequenceNumber"], @0);
|
||||
XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @1);
|
||||
|
||||
//first object should now be baz with sequence number of 1
|
||||
[self.cache removeObjectForKey:@"bar"];
|
||||
|
||||
//should also trigger resequence
|
||||
[self.cache setSequenceNumber:NSIntegerMax];
|
||||
[self.cache objectForKey:@"baz"];
|
||||
|
||||
XCTAssertEqualObjects([innerCache[@"baz"] valueForKey:@"sequenceNumber"], @0);
|
||||
}
|
||||
|
||||
- (void)testName
|
||||
{
|
||||
self.cache.name = @"Hello";
|
||||
XCTAssertEqualObjects(self.cache.name, @"Hello");
|
||||
}
|
||||
|
||||
@end
|
||||
+15
-6
@@ -16,10 +16,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIView.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
extern CGRect RCTClipRect(CGSize contentSize, CGFloat contentScale,
|
||||
CGSize targetSize, CGFloat targetScale,
|
||||
UIViewContentMode resizeMode);
|
||||
#import "RCTImageUtils.h"
|
||||
|
||||
#define RCTAssertEqualPoints(a, b) { \
|
||||
XCTAssertEqual(a.x, b.x); \
|
||||
@@ -36,11 +33,11 @@ RCTAssertEqualPoints(a.origin, b.origin); \
|
||||
RCTAssertEqualSizes(a.size, b.size); \
|
||||
}
|
||||
|
||||
@interface ClippingTests : XCTestCase
|
||||
@interface RCTClipRectTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation ClippingTests
|
||||
@implementation RCTClipRectTests
|
||||
|
||||
- (void)testLandscapeSourceLandscapeTarget
|
||||
{
|
||||
@@ -109,6 +106,18 @@ RCTAssertEqualSizes(a.size, b.size); \
|
||||
|
||||
{
|
||||
CGRect expected = {{0, -37.5}, {10, 100}};
|
||||
CGRect result = RCTClipRect(content, 2, target, 2, UIViewContentModeScaleAspectFill);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testRounding
|
||||
{
|
||||
CGSize content = {10, 100};
|
||||
CGSize target = {20, 50};
|
||||
|
||||
{
|
||||
CGRect expected = {{0, -38}, {10, 100}};
|
||||
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFill);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
@@ -1,4 +1,16 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <mach/mach_time.h>
|
||||
|
||||
@@ -7,6 +19,7 @@
|
||||
#import "RCTContextExecutor.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#define RUN_PERF_TESTS 0
|
||||
|
||||
@interface RCTContextExecutorTests : XCTestCase
|
||||
|
||||
@@ -21,7 +34,6 @@
|
||||
{
|
||||
[super setUp];
|
||||
_executor = [[RCTContextExecutor alloc] init];
|
||||
RCTSetExecutorID(_executor);
|
||||
[_executor setUp];
|
||||
}
|
||||
|
||||
@@ -37,6 +49,8 @@
|
||||
[_executor invalidate];
|
||||
}
|
||||
|
||||
#if RUN_PERF_TESTS
|
||||
|
||||
static uint64_t _get_time_nanoseconds(void)
|
||||
{
|
||||
static struct mach_timebase_info tb_info = {0, 0};
|
||||
@@ -80,7 +94,7 @@ static uint64_t _get_time_nanoseconds(void)
|
||||
JSContextGroupRelease(group);
|
||||
}
|
||||
|
||||
- (void)MANUALLY_testJavaScriptCallSpeed
|
||||
- (void)testJavaScriptCallSpeed
|
||||
{
|
||||
/**
|
||||
* Since we almost don't change the RCTContextExecutor logic, and this test is
|
||||
@@ -127,7 +141,6 @@ static uint64_t _get_time_nanoseconds(void)
|
||||
[_executor executeJSCall:@"module"
|
||||
method:@"method"
|
||||
arguments:params
|
||||
context:RCTGetExecutorID(_executor)
|
||||
callback:^(id json, __unused NSError *unused) {
|
||||
XCTAssert([json isEqual:@YES], @"Invalid return");
|
||||
}];
|
||||
@@ -190,4 +203,6 @@ static uint64_t _get_time_nanoseconds(void)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@interface RCTConvert_NSURLTests : XCTestCase
|
||||
|
||||
@@ -13,8 +26,7 @@
|
||||
#define TEST_URL(name, _input, _expectedURL) \
|
||||
- (void)test_##name { \
|
||||
NSURL *result = [RCTConvert NSURL:_input]; \
|
||||
NSURL *expected = (_expectedURL) ? [NSURL URLWithString:_expectedURL ?: @""] : nil; \
|
||||
XCTAssertEqualObjects(result.absoluteURL, expected); \
|
||||
XCTAssertEqualObjects(result.absoluteString, _expectedURL); \
|
||||
} \
|
||||
|
||||
#define TEST_PATH(name, _input, _expectedPath) \
|
||||
@@ -30,7 +42,7 @@ TEST_PATH(name, _input, [[[NSBundle mainBundle] bundlePath] stringByAppendingPat
|
||||
TEST_URL(basic, @"http://example.com", @"http://example.com")
|
||||
TEST_URL(null, (id)kCFNull, nil)
|
||||
|
||||
// Local files
|
||||
// Resource files
|
||||
TEST_PATH(fileURL, @"file:///blah/hello.jsbundle", @"/blah/hello.jsbundle")
|
||||
TEST_BUNDLE_PATH(filePath, @"blah/hello.jsbundle", @"blah/hello.jsbundle")
|
||||
TEST_BUNDLE_PATH(filePathWithSpaces, @"blah blah/hello.jsbundle", @"blah blah/hello.jsbundle")
|
||||
@@ -38,6 +50,11 @@ TEST_BUNDLE_PATH(filePathWithEncodedSpaces, @"blah%20blah/hello.jsbundle", @"bla
|
||||
TEST_BUNDLE_PATH(imageAt2XPath, @"images/foo@2x.jpg", @"images/foo@2x.jpg")
|
||||
TEST_BUNDLE_PATH(imageFile, @"foo.jpg", @"foo.jpg")
|
||||
|
||||
// User documents
|
||||
TEST_PATH(documentsFolder, @"~/Documents",
|
||||
[NSSearchPathForDirectoriesInDomains
|
||||
(NSDocumentDirectory, NSUserDomainMask, YES) firstObject])
|
||||
|
||||
// Remote files
|
||||
TEST_URL(fullURL, @"http://example.com/blah/hello.jsbundle", @"http://example.com/blah/hello.jsbundle")
|
||||
TEST_URL(urlWithSpaces, @"http://example.com/blah blah/foo", @"http://example.com/blah%20blah/foo")
|
||||
@@ -45,4 +62,19 @@ TEST_URL(urlWithEncodedSpaces, @"http://example.com/blah%20blah/foo", @"http://e
|
||||
TEST_URL(imageURL, @"http://example.com/foo@2x.jpg", @"http://example.com/foo@2x.jpg")
|
||||
TEST_URL(imageURLWithSpaces, @"http://example.com/blah foo@2x.jpg", @"http://example.com/blah%20foo@2x.jpg")
|
||||
|
||||
// Unicode
|
||||
TEST_URL(unicodeURL,
|
||||
@"https://ru.wikipedia.org/wiki/\u0417\u0430\u0433\u043B\u0430\u0432"
|
||||
"\u043D\u0430\u044F_\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430",
|
||||
@"https://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2"
|
||||
"%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0")
|
||||
|
||||
// Data URLs
|
||||
- (void)testDataURL
|
||||
{
|
||||
NSURL *expectedURL = RCTDataURL(@"text/plain", [@"abcde" dataUsingEncoding:NSUTF8StringEncoding]);
|
||||
NSURL *testURL = [NSURL URLWithString:@"data:text/plain;base64,YWJjZGU="];
|
||||
XCTAssertEqualObjects([testURL absoluteString], [expectedURL absoluteString]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@@ -174,7 +186,7 @@
|
||||
RCTAssertEqualFonts(expected, result);
|
||||
}
|
||||
{
|
||||
UIFont *expected = [UIFont boldSystemFontOfSize:14];
|
||||
UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightBold];
|
||||
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"foobar", @"fontWeight": @"bold"}];
|
||||
RCTAssertEqualFonts(expected, result);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
@@ -61,7 +64,7 @@
|
||||
_eventDispatcher = [[RCTEventDispatcher alloc] init];
|
||||
((id<RCTBridgeModule>)_eventDispatcher).bridge = _bridge;
|
||||
|
||||
_eventName = @"sampleEvent";
|
||||
_eventName = RCTNormalizeInputEventName(@"sampleEvent");
|
||||
_body = @{ @"foo": @"bar" };
|
||||
_testEvent = [[RCTTestEvent alloc] initWithViewTag:nil
|
||||
eventName:_eventName
|
||||
@@ -122,7 +125,7 @@
|
||||
|
||||
- (void)testDifferentEventTypesDontCoalesce
|
||||
{
|
||||
NSString *firstEventName = @"firstEvent";
|
||||
NSString *firstEventName = RCTNormalizeInputEventName(@"firstEvent");
|
||||
RCTTestEvent *firstEvent = [[RCTTestEvent alloc] initWithViewTag:nil
|
||||
eventName:firstEventName
|
||||
body:_body];
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTNetworking.h"
|
||||
|
||||
extern BOOL RCTIsGzippedData(NSData *data);
|
||||
|
||||
@interface RCTNetworking (Private)
|
||||
|
||||
- (void)buildRequest:(NSDictionary *)query
|
||||
completionBlock:(void (^)(NSURLRequest *request))block;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTGzipTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTGzipTests
|
||||
|
||||
- (void)testGzip
|
||||
{
|
||||
//set up data
|
||||
NSString *inputString = @"Hello World!";
|
||||
NSData *inputData = [inputString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
//compress
|
||||
NSData *outputData = RCTGzipData(inputData, -1);
|
||||
XCTAssertTrue(RCTIsGzippedData(outputData));
|
||||
}
|
||||
|
||||
- (void)testDontRezipZippedData
|
||||
{
|
||||
//set up data
|
||||
NSString *inputString = @"Hello World!";
|
||||
NSData *inputData = [inputString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
//compress
|
||||
NSData *compressedData = RCTGzipData(inputData, -1);
|
||||
inputString = [[NSString alloc] initWithData:compressedData encoding:NSUTF8StringEncoding];
|
||||
|
||||
//compress again
|
||||
NSData *outputData = RCTGzipData(inputData, -1);
|
||||
NSString *outputString = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding];
|
||||
XCTAssertEqualObjects(outputString, inputString);
|
||||
}
|
||||
|
||||
- (void)testRequestBodyEncoding
|
||||
{
|
||||
NSDictionary *query = @{
|
||||
@"url": @"http://example.com",
|
||||
@"method": @"POST",
|
||||
@"data": @{@"string": @"Hello World"},
|
||||
@"headers": @{@"Content-Encoding": @"gzip"},
|
||||
};
|
||||
|
||||
RCTNetworking *networker = [[RCTNetworking alloc] init];
|
||||
__block NSURLRequest *request = nil;
|
||||
[networker buildRequest:query completionBlock:^(NSURLRequest *_request) {
|
||||
request = _request;
|
||||
}];
|
||||
|
||||
XCTAssertNotNil(request);
|
||||
XCTAssertNotNil(request.HTTPBody);
|
||||
XCTAssertTrue(RCTIsGzippedData(request.HTTPBody));
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@interface RCTJSONTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTJSONTests
|
||||
|
||||
- (void)testEncodingObject
|
||||
{
|
||||
NSDictionary *obj = @{@"foo": @"bar"};
|
||||
NSString *json = @"{\"foo\":\"bar\"}";
|
||||
XCTAssertEqualObjects(json, RCTJSONStringify(obj, NULL));
|
||||
}
|
||||
|
||||
- (void)testEncodingArray
|
||||
{
|
||||
NSArray *array = @[@"foo", @"bar"];
|
||||
NSString *json = @"[\"foo\",\"bar\"]";
|
||||
XCTAssertEqualObjects(json, RCTJSONStringify(array, NULL));
|
||||
}
|
||||
|
||||
- (void)testEncodingString
|
||||
{
|
||||
NSString *text = @"Hello\nWorld";
|
||||
NSString *json = @"\"Hello\\nWorld\"";
|
||||
XCTAssertEqualObjects(json, RCTJSONStringify(text, NULL));
|
||||
}
|
||||
|
||||
- (void)testDecodingObject
|
||||
{
|
||||
NSDictionary *obj = @{@"foo": @"bar"};
|
||||
NSString *json = @"{\"foo\":\"bar\"}";
|
||||
XCTAssertEqualObjects(obj, RCTJSONParse(json, NULL));
|
||||
}
|
||||
|
||||
- (void)testDecodingArray
|
||||
{
|
||||
NSArray *array = @[@"foo", @"bar"];
|
||||
NSString *json = @"[\"foo\",\"bar\"]";
|
||||
XCTAssertEqualObjects(array, RCTJSONParse(json, NULL));
|
||||
}
|
||||
|
||||
- (void)testDecodingString
|
||||
{
|
||||
NSString *text = @"Hello\nWorld";
|
||||
NSString *json = @"\"Hello\\nWorld\"";
|
||||
XCTAssertEqualObjects(text, RCTJSONParse(json, NULL));
|
||||
}
|
||||
|
||||
- (void)testDecodingMutableArray
|
||||
{
|
||||
NSString *json = @"[1,2,3]";
|
||||
NSMutableArray *array = RCTJSONParseMutable(json, NULL);
|
||||
XCTAssertNoThrow([array addObject:@4]);
|
||||
XCTAssertEqualObjects(array, (@[@1, @2, @3, @4]));
|
||||
}
|
||||
|
||||
- (void)testLeadingWhitespace
|
||||
{
|
||||
NSDictionary *obj = @{@"foo": @"bar"};
|
||||
NSString *json = @" \r\n\t{\"foo\":\"bar\"}";
|
||||
XCTAssertEqualObjects(obj, RCTJSONParse(json, NULL));
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "RCTModuleMethod.h"
|
||||
|
||||
@interface RCTMethodArgumentTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTMethodArgumentTests
|
||||
|
||||
extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes);
|
||||
|
||||
- (void)testOneArgument
|
||||
{
|
||||
NSArray *arguments;
|
||||
NSString *methodName = @"foo:(NSInteger)foo";
|
||||
RCTParseObjCMethodName(&methodName, &arguments);
|
||||
XCTAssertEqualObjects(methodName, @"foo:");
|
||||
XCTAssertEqual(arguments.count, (NSUInteger)1);
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger");
|
||||
}
|
||||
|
||||
- (void)testTwoArguments
|
||||
{
|
||||
NSArray *arguments;
|
||||
NSString *methodName = @"foo:(NSInteger)foo bar:(BOOL)bar";
|
||||
RCTParseObjCMethodName(&methodName, &arguments);
|
||||
XCTAssertEqualObjects(methodName, @"foo:bar:");
|
||||
XCTAssertEqual(arguments.count, (NSUInteger)2);
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
|
||||
}
|
||||
|
||||
- (void)testSpaces
|
||||
{
|
||||
NSArray *arguments;
|
||||
NSString *methodName = @"foo : (NSInteger)foo bar : (BOOL) bar";
|
||||
RCTParseObjCMethodName(&methodName, &arguments);
|
||||
XCTAssertEqualObjects(methodName, @"foo:bar:");
|
||||
XCTAssertEqual(arguments.count, (NSUInteger)2);
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
|
||||
}
|
||||
|
||||
- (void)testNewlines
|
||||
{
|
||||
NSArray *arguments;
|
||||
NSString *methodName = @"foo : (NSInteger)foo\nbar : (BOOL) bar";
|
||||
RCTParseObjCMethodName(&methodName, &arguments);
|
||||
XCTAssertEqualObjects(methodName, @"foo:bar:");
|
||||
XCTAssertEqual(arguments.count, (NSUInteger)2);
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
|
||||
}
|
||||
|
||||
- (void)testUnnamedArgs
|
||||
{
|
||||
NSArray *arguments;
|
||||
NSString *methodName = @"foo:(NSInteger)foo:(BOOL)bar";
|
||||
RCTParseObjCMethodName(&methodName, &arguments);
|
||||
XCTAssertEqualObjects(methodName, @"foo::");
|
||||
XCTAssertEqual(arguments.count, (NSUInteger)2);
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSInteger");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
|
||||
}
|
||||
|
||||
- (void)testUntypedUnnamedArgs
|
||||
{
|
||||
NSArray *arguments;
|
||||
NSString *methodName = @"foo:foo:bar:bar";
|
||||
RCTParseObjCMethodName(&methodName, &arguments);
|
||||
XCTAssertEqualObjects(methodName, @"foo:::");
|
||||
XCTAssertEqual(arguments.count, (NSUInteger)3);
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"id");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"id");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[2]).type, @"id");
|
||||
}
|
||||
|
||||
- (void)testAttributes
|
||||
{
|
||||
NSArray *arguments;
|
||||
NSString *methodName = @"foo:(__attribute__((nonnull)) NSString *)foo bar:(__unused BOOL)bar";
|
||||
RCTParseObjCMethodName(&methodName, &arguments);
|
||||
XCTAssertEqualObjects(methodName, @"foo:bar:");
|
||||
XCTAssertEqual(arguments.count, (NSUInteger)2);
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
|
||||
}
|
||||
|
||||
- (void)testNullability
|
||||
{
|
||||
NSArray *arguments;
|
||||
NSString *methodName = @"foo:(nullable NSString *)foo bar:(nonnull NSNumber *)bar baz:(id)baz";
|
||||
RCTParseObjCMethodName(&methodName, &arguments);
|
||||
XCTAssertEqualObjects(methodName, @"foo:bar:baz:");
|
||||
XCTAssertEqual(arguments.count, (NSUInteger)3);
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"NSNumber");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[2]).type, @"id");
|
||||
XCTAssertEqual(((RCTMethodArgument *)arguments[0]).nullability, RCTNullable);
|
||||
XCTAssertEqual(((RCTMethodArgument *)arguments[1]).nullability, RCTNonnullable);
|
||||
XCTAssertEqual(((RCTMethodArgument *)arguments[2]).nullability, RCTNullabilityUnspecified);
|
||||
}
|
||||
|
||||
- (void)testSemicolonStripping
|
||||
{
|
||||
NSArray *arguments;
|
||||
NSString *methodName = @"foo:(NSString *)foo bar:(BOOL)bar;";
|
||||
RCTParseObjCMethodName(&methodName, &arguments);
|
||||
XCTAssertEqualObjects(methodName, @"foo:bar:");
|
||||
XCTAssertEqual(arguments.count, (NSUInteger)2);
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL");
|
||||
}
|
||||
|
||||
- (void)testUnused
|
||||
{
|
||||
NSArray *arguments;
|
||||
NSString *methodName = @"foo:(__unused NSString *)foo bar:(NSNumber *)bar";
|
||||
RCTParseObjCMethodName(&methodName, &arguments);
|
||||
XCTAssertEqualObjects(methodName, @"foo:bar:");
|
||||
XCTAssertEqual(arguments.count, (NSUInteger)2);
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString");
|
||||
XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"NSNumber");
|
||||
XCTAssertTrue(((RCTMethodArgument *)arguments[0]).unused);
|
||||
XCTAssertFalse(((RCTMethodArgument *)arguments[1]).unused);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "RCTModuleMethod.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
static BOOL RCTLogsError(void (^block)(void))
|
||||
{
|
||||
__block BOOL loggedError = NO;
|
||||
RCTPerformBlockWithLogFunction(block, ^(RCTLogLevel level,
|
||||
__unused NSString *fileName,
|
||||
__unused NSNumber *lineNumber,
|
||||
__unused NSString *message) {
|
||||
loggedError = (level == RCTLogLevelError);
|
||||
});
|
||||
return loggedError;
|
||||
}
|
||||
|
||||
@interface RCTModuleMethodTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTModuleMethodTests
|
||||
|
||||
- (void)doFooWithBar:(__unused NSString *)bar { }
|
||||
|
||||
- (void)testNonnull
|
||||
{
|
||||
NSString *methodName = @"doFooWithBar:(nonnull NSString *)bar";
|
||||
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
||||
JSMethodName:nil
|
||||
moduleClass:[self class]];
|
||||
XCTAssertFalse(RCTLogsError(^{
|
||||
[method invokeWithBridge:nil module:self arguments:@[@"Hello World"]];
|
||||
}));
|
||||
|
||||
XCTAssertTrue(RCTLogsError(^{
|
||||
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
|
||||
}));
|
||||
}
|
||||
|
||||
- (void)doFooWithNumber:(__unused NSNumber *)n { }
|
||||
- (void)doFooWithDouble:(__unused double)n { }
|
||||
- (void)doFooWithInteger:(__unused NSInteger)n { }
|
||||
|
||||
- (void)testNumbersNonnull
|
||||
{
|
||||
{
|
||||
// Specifying an NSNumber param without nonnull isn't allowed
|
||||
XCTAssertTrue(RCTLogsError(^{
|
||||
NSString *methodName = @"doFooWithNumber:(NSNumber *)n";
|
||||
(void)[[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
||||
JSMethodName:nil
|
||||
moduleClass:[self class]];
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
NSString *methodName = @"doFooWithNumber:(nonnull NSNumber *)n";
|
||||
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
||||
JSMethodName:nil
|
||||
moduleClass:[self class]];
|
||||
XCTAssertTrue(RCTLogsError(^{
|
||||
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
NSString *methodName = @"doFooWithDouble:(double)n";
|
||||
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
||||
JSMethodName:nil
|
||||
moduleClass:[self class]];
|
||||
XCTAssertTrue(RCTLogsError(^{
|
||||
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
NSString *methodName = @"doFooWithInteger:(NSInteger)n";
|
||||
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
||||
JSMethodName:nil
|
||||
moduleClass:[self class]];
|
||||
XCTAssertTrue(RCTLogsError(^{
|
||||
[method invokeWithBridge:nil module:self arguments:@[[NSNull null]]];
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,4 +1,16 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@@ -13,10 +25,10 @@
|
||||
|
||||
- (void)testDictionary
|
||||
{
|
||||
NSObject<RCTViewNodeProtocol> *myView = [[UIView alloc] init];
|
||||
id<RCTComponent> myView = [[UIView alloc] init];
|
||||
myView.reactTag = @4;
|
||||
|
||||
NSObject<RCTViewNodeProtocol> *myOtherView = [[UIView alloc] init];
|
||||
id<RCTComponent> myOtherView = [[UIView alloc] init];
|
||||
myOtherView.reactTag = @5;
|
||||
|
||||
RCTSparseArray *registry = [[RCTSparseArray alloc] init];
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "RCTRootView.h"
|
||||
#import "RCTShadowView.h"
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "UIView+React.h"
|
||||
@@ -11,36 +21,14 @@
|
||||
@interface RCTUIManager (Testing)
|
||||
|
||||
- (void)_manageChildren:(NSNumber *)containerReactTag
|
||||
moveFromIndices:(NSArray *)moveFromIndices
|
||||
moveToIndices:(NSArray *)moveToIndices
|
||||
addChildReactTags:(NSArray *)addChildReactTags
|
||||
addAtIndices:(NSArray *)addAtIndices
|
||||
removeAtIndices:(NSArray *)removeAtIndices
|
||||
registry:(RCTSparseArray *)registry;
|
||||
|
||||
- (void)modifyManageChildren:(NSNumber *)containerReactTag
|
||||
addChildReactTags:(NSMutableArray *)mutableAddChildReactTags
|
||||
addAtIndices:(NSMutableArray *)mutableAddAtIndices
|
||||
removeAtIndices:(NSMutableArray *)mutableRemoveAtIndices;
|
||||
|
||||
- (void)createView:(NSNumber *)reactTag
|
||||
viewName:(NSString *)viewName
|
||||
rootTag:(NSNumber *)rootTag
|
||||
props:(NSDictionary *)props;
|
||||
|
||||
- (void)updateView:(NSNumber *)reactTag
|
||||
viewName:(NSString *)viewName
|
||||
props:(NSDictionary *)props;
|
||||
|
||||
- (void)manageChildren:(NSNumber *)containerReactTag
|
||||
moveFromIndices:(NSArray *)moveFromIndices
|
||||
moveToIndices:(NSArray *)moveToIndices
|
||||
addChildReactTags:(NSArray *)addChildReactTags
|
||||
addAtIndices:(NSArray *)addAtIndices
|
||||
removeAtIndices:(NSArray *)removeAtIndices;
|
||||
|
||||
- (void)flushUIBlocks;
|
||||
|
||||
@property (nonatomic, readonly) RCTSparseArray *viewRegistry;
|
||||
@property (nonatomic, readonly) RCTSparseArray *shadowViewRegistry; // RCT thread only
|
||||
|
||||
@end
|
||||
|
||||
@@ -63,11 +51,6 @@
|
||||
UIView *registeredView = [[UIView alloc] init];
|
||||
[registeredView setReactTag:@(i)];
|
||||
_uiManager.viewRegistry[i] = registeredView;
|
||||
|
||||
RCTShadowView *registeredShadowView = [[RCTShadowView alloc] init];
|
||||
registeredShadowView.viewName = @"RCTView";
|
||||
[registeredShadowView setReactTag:@(i)];
|
||||
_uiManager.shadowViewRegistry[i] = registeredShadowView;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +67,8 @@
|
||||
|
||||
// Add views 1-5 to view 20
|
||||
[_uiManager _manageChildren:@20
|
||||
moveFromIndices:nil
|
||||
moveToIndices:nil
|
||||
addChildReactTags:tagsToAdd
|
||||
addAtIndices:addAtIndices
|
||||
removeAtIndices:nil
|
||||
@@ -116,6 +101,8 @@
|
||||
|
||||
// Remove views 1-5 from view 20
|
||||
[_uiManager _manageChildren:@20
|
||||
moveFromIndices:nil
|
||||
moveToIndices:nil
|
||||
addChildReactTags:nil
|
||||
addAtIndices:nil
|
||||
removeAtIndices:removeAtIndices
|
||||
@@ -153,9 +140,11 @@
|
||||
{
|
||||
UIView *containerView = _uiManager.viewRegistry[20];
|
||||
|
||||
NSArray *removeAtIndices = @[@2, @3, @5, @8, @4, @9];
|
||||
NSArray *addAtIndices = @[@0, @6, @1, @7];
|
||||
NSArray *tagsToAdd = @[@11, @12, @5, @10];
|
||||
NSArray *removeAtIndices = @[@2, @3, @5, @8];
|
||||
NSArray *addAtIndices = @[@0, @6];
|
||||
NSArray *tagsToAdd = @[@11, @12];
|
||||
NSArray *moveFromIndices = @[@4, @9];
|
||||
NSArray *moveToIndices = @[@1, @7];
|
||||
|
||||
// We need to keep these in array to keep them around
|
||||
NSMutableArray *viewsToRemove = [NSMutableArray array];
|
||||
@@ -171,6 +160,8 @@
|
||||
}
|
||||
|
||||
[_uiManager _manageChildren:@20
|
||||
moveFromIndices:moveFromIndices
|
||||
moveToIndices:moveToIndices
|
||||
addChildReactTags:tagsToAdd
|
||||
addAtIndices:addAtIndices
|
||||
removeAtIndices:removeAtIndices
|
||||
@@ -197,329 +188,4 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* +-----------------------------------------------------------+ +----------------------+
|
||||
* | Shadow Hierarchy | | Legend |
|
||||
* +-----------------------------------------------------------+ +----------------------+
|
||||
* | | | |
|
||||
* | +---+ ****** | | ******************** |
|
||||
* | | 1 | * 11 * | | * Layout-only View * |
|
||||
* | +---+ ****** | | ******************** |
|
||||
* | | | | | |
|
||||
* | +-------+---+---+----------+ +---+---+ | | +----+ |
|
||||
* | | | | | | | | | |View| Subview |
|
||||
* | v v v v v v | | +----+ -----------> |
|
||||
* | ***** +---+ ***** +---+ +----+ +----+ | | |
|
||||
* | * 2 * | 3 | * 4 * | 5 | | 12 | | 13 | | +----------------------+
|
||||
* | ***** +---+ ***** +---+ +----+ +----+ |
|
||||
* | | | | |
|
||||
* | +---+--+ | +---+---+ |
|
||||
* | | | | | | |
|
||||
* | v v v v v |
|
||||
* | +---+ +---+ +---+ +---+ ****** |
|
||||
* | | 6 | | 7 | | 8 | | 9 | * 10 * |
|
||||
* | +---+ +---+ +---+ +---+ ****** |
|
||||
* | |
|
||||
* +-----------------------------------------------------------+
|
||||
*
|
||||
* +-----------------------------------------------------------+
|
||||
* | View Hierarchy |
|
||||
* +-----------------------------------------------------------+
|
||||
* | |
|
||||
* | +---+ ****** |
|
||||
* | | 1 | * 11 * |
|
||||
* | +---+ ****** |
|
||||
* | | | |
|
||||
* | +------+------+------+------+ +---+---+ |
|
||||
* | | | | | | | | |
|
||||
* | v v v v v v v |
|
||||
* | +---+ +---+ +---+ +---+ +---+ +----+ +----+ |
|
||||
* | | 6 | | 7 | | 3 | | 8 | | 5 | | 12 | | 13 | |
|
||||
* | +---+ +---+ +---+ +---+ +---+ +----+ +----+ |
|
||||
* | | |
|
||||
* | v |
|
||||
* | +---+ |
|
||||
* | | 9 | |
|
||||
* | +---+ |
|
||||
* | |
|
||||
* +-----------------------------------------------------------+
|
||||
*/
|
||||
|
||||
- (void)updateShadowViewWithReactTag:(NSNumber *)reactTag layoutOnly:(BOOL)isLayoutOnly childTags:(NSArray *)childTags
|
||||
{
|
||||
RCTShadowView *shadowView = _uiManager.shadowViewRegistry[reactTag];
|
||||
shadowView.allProps = isLayoutOnly ? @{} : @{@"collapsible": @NO};
|
||||
[childTags enumerateObjectsUsingBlock:^(NSNumber *childTag, NSUInteger idx, __unused BOOL *stop) {
|
||||
[shadowView insertReactSubview:_uiManager.shadowViewRegistry[childTag] atIndex:idx];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setUpShadowViewHierarchy
|
||||
{
|
||||
[self updateShadowViewWithReactTag:@1 layoutOnly:NO childTags:@[@2, @3, @4, @5]];
|
||||
[self updateShadowViewWithReactTag:@2 layoutOnly:YES childTags:@[@6, @7]];
|
||||
[self updateShadowViewWithReactTag:@3 layoutOnly:NO childTags:nil];
|
||||
[self updateShadowViewWithReactTag:@4 layoutOnly:YES childTags:@[@8]];
|
||||
[self updateShadowViewWithReactTag:@5 layoutOnly:NO childTags:@[@9, @10]];
|
||||
[self updateShadowViewWithReactTag:@6 layoutOnly:NO childTags:nil];
|
||||
[self updateShadowViewWithReactTag:@7 layoutOnly:NO childTags:nil];
|
||||
[self updateShadowViewWithReactTag:@8 layoutOnly:NO childTags:nil];
|
||||
[self updateShadowViewWithReactTag:@9 layoutOnly:NO childTags:nil];
|
||||
[self updateShadowViewWithReactTag:@10 layoutOnly:YES childTags:nil];
|
||||
[self updateShadowViewWithReactTag:@11 layoutOnly:YES childTags:@[@12, @13]];
|
||||
[self updateShadowViewWithReactTag:@12 layoutOnly:NO childTags:nil];
|
||||
[self updateShadowViewWithReactTag:@13 layoutOnly:NO childTags:nil];
|
||||
}
|
||||
|
||||
- (void)testModifyIndices1
|
||||
{
|
||||
[self setUpShadowViewHierarchy];
|
||||
|
||||
NSMutableArray *addTags = [@[@2] mutableCopy];
|
||||
NSMutableArray *addIndices = [@[@3] mutableCopy];
|
||||
NSMutableArray *removeIndices = [@[@0] mutableCopy];
|
||||
[self.uiManager modifyManageChildren:@1
|
||||
addChildReactTags:addTags
|
||||
addAtIndices:addIndices
|
||||
removeAtIndices:removeIndices];
|
||||
XCTAssertEqualObjects(addTags, (@[@6, @7]));
|
||||
XCTAssertEqualObjects(addIndices, (@[@3, @4]));
|
||||
XCTAssertEqualObjects(removeIndices, (@[@0, @1]));
|
||||
}
|
||||
|
||||
- (void)testModifyIndices2
|
||||
{
|
||||
[self setUpShadowViewHierarchy];
|
||||
|
||||
NSMutableArray *addTags = [@[@11] mutableCopy];
|
||||
NSMutableArray *addIndices = [@[@4] mutableCopy];
|
||||
NSMutableArray *removeIndices = [@[] mutableCopy];
|
||||
[self.uiManager modifyManageChildren:@1
|
||||
addChildReactTags:addTags
|
||||
addAtIndices:addIndices
|
||||
removeAtIndices:removeIndices];
|
||||
XCTAssertEqualObjects(addTags, (@[@12, @13]));
|
||||
XCTAssertEqualObjects(addIndices, (@[@5, @6]));
|
||||
XCTAssertEqualObjects(removeIndices, (@[]));
|
||||
}
|
||||
|
||||
- (void)testModifyIndices3
|
||||
{
|
||||
[self setUpShadowViewHierarchy];
|
||||
|
||||
NSMutableArray *addTags = [@[] mutableCopy];
|
||||
NSMutableArray *addIndices = [@[] mutableCopy];
|
||||
NSMutableArray *removeIndices = [@[@2] mutableCopy];
|
||||
[self.uiManager modifyManageChildren:@1
|
||||
addChildReactTags:addTags
|
||||
addAtIndices:addIndices
|
||||
removeAtIndices:removeIndices];
|
||||
XCTAssertEqualObjects(addTags, (@[]));
|
||||
XCTAssertEqualObjects(addIndices, (@[]));
|
||||
XCTAssertEqualObjects(removeIndices, (@[@3]));
|
||||
}
|
||||
|
||||
- (void)testModifyIndices4
|
||||
{
|
||||
[self setUpShadowViewHierarchy];
|
||||
|
||||
NSMutableArray *addTags = [@[@11] mutableCopy];
|
||||
NSMutableArray *addIndices = [@[@3] mutableCopy];
|
||||
NSMutableArray *removeIndices = [@[@2] mutableCopy];
|
||||
[self.uiManager modifyManageChildren:@1
|
||||
addChildReactTags:addTags
|
||||
addAtIndices:addIndices
|
||||
removeAtIndices:removeIndices];
|
||||
XCTAssertEqualObjects(addTags, (@[@12, @13]));
|
||||
XCTAssertEqualObjects(addIndices, (@[@4, @5]));
|
||||
XCTAssertEqualObjects(removeIndices, (@[@3]));
|
||||
}
|
||||
|
||||
- (void)testModifyIndices5
|
||||
{
|
||||
[self setUpShadowViewHierarchy];
|
||||
|
||||
NSMutableArray *addTags = [@[] mutableCopy];
|
||||
NSMutableArray *addIndices = [@[] mutableCopy];
|
||||
NSMutableArray *removeIndices = [@[@0, @1, @2, @3] mutableCopy];
|
||||
[self.uiManager modifyManageChildren:@1
|
||||
addChildReactTags:addTags
|
||||
addAtIndices:addIndices
|
||||
removeAtIndices:removeIndices];
|
||||
XCTAssertEqualObjects(addTags, (@[]));
|
||||
XCTAssertEqualObjects(addIndices, (@[]));
|
||||
XCTAssertEqualObjects(removeIndices, (@[@0, @1, @2, @3, @4]));
|
||||
}
|
||||
|
||||
- (void)testModifyIndices6
|
||||
{
|
||||
[self setUpShadowViewHierarchy];
|
||||
|
||||
NSMutableArray *addTags = [@[@11] mutableCopy];
|
||||
NSMutableArray *addIndices = [@[@0] mutableCopy];
|
||||
NSMutableArray *removeIndices = [@[@0, @1, @2, @3] mutableCopy];
|
||||
[self.uiManager modifyManageChildren:@1
|
||||
addChildReactTags:addTags
|
||||
addAtIndices:addIndices
|
||||
removeAtIndices:removeIndices];
|
||||
XCTAssertEqualObjects(addTags, (@[@12, @13]));
|
||||
XCTAssertEqualObjects(addIndices, (@[@0, @1]));
|
||||
XCTAssertEqualObjects(removeIndices, (@[@0, @1, @2, @3, @4]));
|
||||
}
|
||||
|
||||
- (void)testModifyIndices7
|
||||
{
|
||||
[self setUpShadowViewHierarchy];
|
||||
|
||||
NSMutableArray *addTags = [@[@11] mutableCopy];
|
||||
NSMutableArray *addIndices = [@[@1] mutableCopy];
|
||||
NSMutableArray *removeIndices = [@[@0, @2, @3] mutableCopy];
|
||||
[self.uiManager modifyManageChildren:@1
|
||||
addChildReactTags:addTags
|
||||
addAtIndices:addIndices
|
||||
removeAtIndices:removeIndices];
|
||||
XCTAssertEqualObjects(addTags, (@[@12, @13]));
|
||||
XCTAssertEqualObjects(addIndices, (@[@1, @2]));
|
||||
XCTAssertEqualObjects(removeIndices, (@[@0, @1, @3, @4]));
|
||||
}
|
||||
|
||||
- (void)DISABLED_testScenario1 // t7660646
|
||||
{
|
||||
RCTUIManager *uiManager = [[RCTUIManager alloc] init];
|
||||
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[uiManager]; } launchOptions:nil];
|
||||
NS_VALID_UNTIL_END_OF_SCOPE RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"Test"];
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@""];
|
||||
|
||||
dispatch_queue_t shadowQueue = [uiManager valueForKey:@"shadowQueue"];
|
||||
dispatch_async(shadowQueue, ^{
|
||||
// Make sure root view finishes loading.
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{});
|
||||
|
||||
/* */[uiManager createView:@2 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
|
||||
/* */[uiManager createView:@3 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
|
||||
/* V */[uiManager createView:@4 viewName:@"RCTView" rootTag:@1 props:@{@"alignItems":@"center",@"backgroundColor":@"#F5FCFF",@"flex":@1,@"justifyContent":@"center"}];
|
||||
/* V */[uiManager createView:@5 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"blue",@"height":@50,@"width":@50}];
|
||||
/* */[uiManager createView:@6 viewName:@"RCTView" rootTag:@1 props:@{@"width":@250}];
|
||||
/* V */[uiManager createView:@7 viewName:@"RCTView" rootTag:@1 props:@{@"borderWidth":@10,@"margin":@50}];
|
||||
/* V */[uiManager createView:@8 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"yellow",@"height":@50}];
|
||||
/* V */[uiManager createView:@9 viewName:@"RCTText" rootTag:@1 props:@{@"accessible":@1,@"fontSize":@20,@"isHighlighted":@0,@"margin":@10,@"textAlign":@"center"}];
|
||||
/* */[uiManager createView:@10 viewName:@"RCTRawText" rootTag:@1 props:@{@"text":@"This tests removal of layout-only views."}];
|
||||
/* */[uiManager manageChildren:@9 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@10] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
/* V */[uiManager createView:@12 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"green",@"height":@50}];
|
||||
/* */[uiManager manageChildren:@7 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@8,@9,@12] addAtIndices:@[@0,@1,@2] removeAtIndices:nil];
|
||||
/* */[uiManager manageChildren:@6 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@7] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
/* V */[uiManager createView:@13 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"red",@"height":@50,@"width":@50}];
|
||||
/* */[uiManager manageChildren:@4 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@5,@6,@13] addAtIndices:@[@0,@1,@2] removeAtIndices:nil];
|
||||
/* */[uiManager manageChildren:@3 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@4] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
/* */[uiManager manageChildren:@2 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@3] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
/* */[uiManager manageChildren:@1 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@2] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
|
||||
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
|
||||
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)12);
|
||||
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)8);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[uiManager flushUIBlocks];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
|
||||
expectation = [self expectationWithDescription:@""];
|
||||
dispatch_async(shadowQueue, ^{
|
||||
[uiManager updateView:@7 viewName:@"RCTView" props:@{@"borderWidth":[NSNull null]}];
|
||||
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
|
||||
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)12);
|
||||
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)7);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[uiManager flushUIBlocks];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
|
||||
expectation = [self expectationWithDescription:@""];
|
||||
dispatch_async(shadowQueue, ^{
|
||||
[uiManager updateView:@7 viewName:@"RCTView" props:@{@"borderWidth":@10}];
|
||||
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
|
||||
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)12);
|
||||
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)8);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[uiManager flushUIBlocks];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
}
|
||||
|
||||
- (void)DISABLED_testScenario2 // t7660646
|
||||
{
|
||||
RCTUIManager *uiManager = [[RCTUIManager alloc] init];
|
||||
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[uiManager]; } launchOptions:nil];
|
||||
NS_VALID_UNTIL_END_OF_SCOPE RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"Test"];
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@""];
|
||||
|
||||
dispatch_queue_t shadowQueue = [uiManager valueForKey:@"shadowQueue"];
|
||||
dispatch_async(shadowQueue, ^{
|
||||
// Make sure root view finishes loading.
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{});
|
||||
|
||||
/* */[uiManager createView:@2 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
|
||||
/* */[uiManager createView:@3 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
|
||||
/* V */[uiManager createView:@4 viewName:@"RCTView" rootTag:@1 props:@{@"alignItems":@"center",@"backgroundColor":@"#F5FCFF",@"flex":@1,@"justifyContent":@"center"}];
|
||||
/* */[uiManager createView:@5 viewName:@"RCTView" rootTag:@1 props:@{@"width":@250}];
|
||||
/* V */[uiManager createView:@6 viewName:@"RCTView" rootTag:@1 props:@{@"borderWidth":@1}];
|
||||
/* V */[uiManager createView:@7 viewName:@"RCTText" rootTag:@1 props:@{@"accessible":@1,@"fontSize":@20,@"isHighlighted":@0,@"margin":@10,@"textAlign":@"center"}];
|
||||
/* */[uiManager createView:@8 viewName:@"RCTRawText" rootTag:@1 props:@{@"text":@"This tests removal of layout-only views."}];
|
||||
/* */[uiManager manageChildren:@7 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@8] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
/* */[uiManager manageChildren:@6 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@7] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
/* */[uiManager manageChildren:@5 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@6] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
/* */[uiManager manageChildren:@4 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@5] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
/* */[uiManager manageChildren:@3 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@4] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
/* */[uiManager manageChildren:@2 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@3] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
/* */[uiManager manageChildren:@1 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@2] addAtIndices:@[@0] removeAtIndices:nil];
|
||||
|
||||
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
|
||||
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)8);
|
||||
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)4);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[uiManager flushUIBlocks];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
|
||||
expectation = [self expectationWithDescription:@""];
|
||||
dispatch_async(shadowQueue, ^{
|
||||
[uiManager updateView:@6 viewName:@"RCTView" props:@{@"borderWidth":[NSNull null]}];
|
||||
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
|
||||
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)8);
|
||||
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)3);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[uiManager flushUIBlocks];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
|
||||
expectation = [self expectationWithDescription:@""];
|
||||
dispatch_async(shadowQueue, ^{
|
||||
[uiManager updateView:@6 viewName:@"RCTView" props:@{@"borderWidth":@1}];
|
||||
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
|
||||
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)8);
|
||||
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)4);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[uiManager flushUIBlocks];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -58,19 +58,19 @@ var WebViewExample = React.createClass({
|
||||
return (
|
||||
<View style={[styles.container]}>
|
||||
<View style={[styles.addressBarRow]}>
|
||||
<TouchableOpacity onPress={this.goBack}>
|
||||
<View style={this.state.backButtonEnabled ? styles.navButton : styles.disabledButton}>
|
||||
<Text>
|
||||
{'<'}
|
||||
</Text>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
onPress={this.goBack}
|
||||
style={this.state.backButtonEnabled ? styles.navButton : styles.disabledButton}>
|
||||
<Text>
|
||||
{'<'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={this.goForward}>
|
||||
<View style={this.state.forwardButtonEnabled ? styles.navButton : styles.disabledButton}>
|
||||
<Text>
|
||||
{'>'}
|
||||
</Text>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
onPress={this.goForward}
|
||||
style={this.state.forwardButtonEnabled ? styles.navButton : styles.disabledButton}>
|
||||
<Text>
|
||||
{'>'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TextInput
|
||||
ref={TEXT_INPUT_REF}
|
||||
|
||||
@@ -128,6 +128,7 @@ class FormUploader extends React.Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
isUploading: false,
|
||||
uploadProgress: null,
|
||||
randomPhoto: null,
|
||||
textParams: [],
|
||||
};
|
||||
@@ -143,7 +144,6 @@ class FormUploader extends React.Component {
|
||||
CameraRoll.getPhotos(
|
||||
{first: PAGE_SIZE},
|
||||
(data) => {
|
||||
console.log('isMounted', this._isMounted);
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
}
|
||||
@@ -217,6 +217,14 @@ class FormUploader extends React.Component {
|
||||
this.state.textParams.forEach(
|
||||
(param) => formdata.append(param.name, param.value)
|
||||
);
|
||||
if (xhr.upload) {
|
||||
xhr.upload.onprogress = (event) => {
|
||||
console.log('upload onprogress', event);
|
||||
if (event.lengthComputable) {
|
||||
this.setState({uploadProgress: event.loaded / event.total});
|
||||
}
|
||||
};
|
||||
}
|
||||
xhr.send(formdata);
|
||||
this.setState({isUploading: true});
|
||||
}
|
||||
@@ -251,6 +259,10 @@ class FormUploader extends React.Component {
|
||||
</View>
|
||||
));
|
||||
var uploadButtonLabel = this.state.isUploading ? 'Uploading...' : 'Upload';
|
||||
var uploadProgress = this.state.uploadProgress;
|
||||
if (uploadProgress !== null) {
|
||||
uploadButtonLabel += ' ' + Math.round(uploadProgress * 100) + '%';
|
||||
}
|
||||
var uploadButton = (
|
||||
<View style={styles.uploadButtonBox}>
|
||||
<Text style={styles.uploadButtonLabel}>{uploadButtonLabel}</Text>
|
||||
|
||||
@@ -54,7 +54,9 @@ var createExamplePage = function(title: ?string, exampleModule: ExampleModule)
|
||||
};
|
||||
var result = example.render(null);
|
||||
if (result) {
|
||||
renderedComponent = result;
|
||||
renderedComponent = React.cloneElement(result, {
|
||||
navigator: this.props.navigator,
|
||||
});
|
||||
}
|
||||
(React: Object).render = originalRender;
|
||||
(React: Object).renderComponent = originalRenderComponent;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#import "JSContextRef.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
JSValueRef nativeProfilerStart(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception);
|
||||
|
||||
JSValueRef nativeProfilerEnd(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
//#include "config.h"
|
||||
|
||||
#include "JSCLegacyProfiler.h"
|
||||
|
||||
#include "APICast.h"
|
||||
#include "LegacyProfiler.h"
|
||||
#include "OpaqueJSString.h"
|
||||
#include "JSProfilerPrivate.h"
|
||||
#include "JSStringRef.h"
|
||||
|
||||
#include <yajl/yajl_gen.h>
|
||||
|
||||
#define GEN_AND_CHECK(expr) \
|
||||
do { \
|
||||
yajl_gen_status GEN_AND_CHECK_status = (expr); \
|
||||
if (GEN_AND_CHECK_status != yajl_gen_status_ok) { \
|
||||
return GEN_AND_CHECK_status; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
static inline yajl_gen_status yajl_gen_cstring(yajl_gen gen, const char *str) {
|
||||
return yajl_gen_string(gen, (const unsigned char*)str, strlen(str));
|
||||
}
|
||||
|
||||
static yajl_gen_status append_children_array_json(yajl_gen gen, const JSC::ProfileNode *node);
|
||||
static yajl_gen_status append_node_json(yajl_gen gen, const JSC::ProfileNode *node);
|
||||
|
||||
static yajl_gen_status append_root_json(yajl_gen gen, const JSC::Profile *profile) {
|
||||
GEN_AND_CHECK(yajl_gen_map_open(gen));
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "rootNodes"));
|
||||
GEN_AND_CHECK(append_children_array_json(gen, profile->head()));
|
||||
GEN_AND_CHECK(yajl_gen_map_close(gen));
|
||||
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
static yajl_gen_status append_children_array_json(yajl_gen gen, const JSC::ProfileNode *node) {
|
||||
GEN_AND_CHECK(yajl_gen_array_open(gen));
|
||||
for (RefPtr<JSC::ProfileNode> child : node->children()) {
|
||||
GEN_AND_CHECK(append_node_json(gen, child.get()));
|
||||
}
|
||||
GEN_AND_CHECK(yajl_gen_array_close(gen));
|
||||
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
static yajl_gen_status append_node_json(yajl_gen gen, const JSC::ProfileNode *node) {
|
||||
GEN_AND_CHECK(yajl_gen_map_open(gen));
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "id"));
|
||||
GEN_AND_CHECK(yajl_gen_integer(gen, node->id()));
|
||||
|
||||
if (!node->functionName().isEmpty()) {
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "functionName"));
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, node->functionName().utf8().data()));
|
||||
}
|
||||
|
||||
if (!node->url().isEmpty()) {
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "url"));
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, node->url().utf8().data()));
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "lineNumber"));
|
||||
GEN_AND_CHECK(yajl_gen_integer(gen, node->lineNumber()));
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "columnNumber"));
|
||||
GEN_AND_CHECK(yajl_gen_integer(gen, node->columnNumber()));
|
||||
}
|
||||
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "calls"));
|
||||
GEN_AND_CHECK(yajl_gen_array_open(gen));
|
||||
for (const JSC::ProfileNode::Call &call : node->calls()) {
|
||||
GEN_AND_CHECK(yajl_gen_map_open(gen));
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "startTime"));
|
||||
GEN_AND_CHECK(yajl_gen_double(gen, call.startTime()));
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "totalTime"));
|
||||
GEN_AND_CHECK(yajl_gen_double(gen, call.totalTime()));
|
||||
GEN_AND_CHECK(yajl_gen_map_close(gen));
|
||||
}
|
||||
GEN_AND_CHECK(yajl_gen_array_close(gen));
|
||||
|
||||
if (!node->children().isEmpty()) {
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "children"));
|
||||
GEN_AND_CHECK(append_children_array_json(gen, node));
|
||||
}
|
||||
|
||||
GEN_AND_CHECK(yajl_gen_map_close(gen));
|
||||
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
static char *render_error_code(yajl_gen_status status) {
|
||||
char err[1024];
|
||||
snprintf(err, sizeof(err), "{\"error\": %d}", (int)status);
|
||||
return strdup(err);
|
||||
}
|
||||
|
||||
static char *convert_to_json(const JSC::Profile *profile) {
|
||||
yajl_gen_status status;
|
||||
yajl_gen gen = yajl_gen_alloc(NULL);
|
||||
|
||||
status = append_root_json(gen, profile);
|
||||
if (status != yajl_gen_status_ok) {
|
||||
yajl_gen_free(gen);
|
||||
return render_error_code(status);
|
||||
}
|
||||
|
||||
const unsigned char *buf;
|
||||
size_t buf_size;
|
||||
status = yajl_gen_get_buf(gen, &buf, &buf_size);
|
||||
if (status != yajl_gen_status_ok) {
|
||||
yajl_gen_free(gen);
|
||||
return render_error_code(status);
|
||||
}
|
||||
|
||||
char *json_copy = strdup((const char*)buf);
|
||||
yajl_gen_free(gen);
|
||||
return json_copy;
|
||||
}
|
||||
|
||||
static char *JSEndProfilingAndRender(JSContextRef ctx, JSStringRef title)
|
||||
{
|
||||
JSC::ExecState *exec = toJS(ctx);
|
||||
JSC::LegacyProfiler *profiler = JSC::LegacyProfiler::profiler();
|
||||
RefPtr<JSC::Profile> rawProfile = profiler->stopProfiling(exec, title->string());
|
||||
return convert_to_json(rawProfile.get());
|
||||
}
|
||||
|
||||
JSValueRef nativeProfilerStart(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception) {
|
||||
if (argumentCount < 1) {
|
||||
// Could raise an exception here.
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
JSStringRef title = JSValueToStringCopy(ctx, arguments[0], NULL);
|
||||
JSStartProfiling(ctx, title);
|
||||
JSStringRelease(title);
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
JSValueRef nativeProfilerEnd(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception) {
|
||||
if (argumentCount < 1) {
|
||||
// Could raise an exception here.
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
JSStringRef title = JSValueToStringCopy(ctx, arguments[0], NULL);
|
||||
char *rendered = JSEndProfilingAndRender(ctx, title);
|
||||
JSStringRelease(title);
|
||||
JSStringRef profile = JSStringCreateWithUTF8CString(rendered);
|
||||
free(rendered);
|
||||
return JSValueMakeString(ctx, profile);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
HEADER_PATHS := `find ./tmp/JavaScriptCore -name '*.h' | xargs -I{} dirname {} | uniq | xargs -I{} echo "-I {}"`
|
||||
CERT ?= "iPhone Developer"
|
||||
|
||||
ios8: prepare build generate
|
||||
|
||||
prepare: clean create download
|
||||
|
||||
build: x86_64 arm64 armv7
|
||||
|
||||
generate: lipo codesign
|
||||
|
||||
clean:
|
||||
@rm -rf tmp/ /tmp/RCTJSCProfiler
|
||||
|
||||
lipo:
|
||||
lipo -create -output /tmp/RCTJSCProfiler/RCTJSCProfiler.ios8.dylib ./tmp/RCTJSCProfiler_x86_64 ./tmp/RCTJSCProfiler_arm64 ./tmp/RCTJSCProfiler_armv7
|
||||
|
||||
codesign:
|
||||
codesign -f -s ${CERT} /tmp/RCTJSCProfiler/RCTJSCProfiler.ios8.dylib
|
||||
|
||||
create:
|
||||
mkdir -p ./tmp /tmp/RCTJSCProfiler/ ./tmp/CoreFoundation ./tmp/Foundation
|
||||
for file in ./tmp/CoreFoundation/CFUserNotification.h ./tmp/CoreFoundation/CFXMLNode.h ./tmp/CoreFoundation/CFXMLParser.h ./tmp/Foundation/Foundation.h; do echo '' > "$$file"; done
|
||||
|
||||
download: wtf jsc webcore yajl
|
||||
|
||||
wtf:
|
||||
curl -o tmp/WTF.tar.gz http://www.opensource.apple.com/tarballs/WTF/WTF-7600.1.24.tar.gz
|
||||
tar -zxvf tmp/WTF.tar.gz -C tmp
|
||||
|
||||
jsc:
|
||||
curl -o tmp/JSC.tar.gz http://www.opensource.apple.com/tarballs/JavaScriptCore/JavaScriptCore-7600.1.17.tar.gz
|
||||
tar -zxvf tmp/JSC.tar.gz -C tmp
|
||||
mv ./tmp/JavaScriptCore-7600.1.17 ./tmp/JavaScriptCore
|
||||
python ./tmp/JavaScriptCore/generate-bytecode-files --bytecodes_h ./tmp/JavaScriptCore/Bytecodes.h ./tmp/JavaScriptCore/bytecode/BytecodeList.json
|
||||
|
||||
webcore:
|
||||
curl -o tmp/WebCore.tar.gz http://www.opensource.apple.com/tarballs/WebCore/WebCore-7600.1.25.tar.gz
|
||||
tar -zxvf tmp/WebCore.tar.gz -C tmp
|
||||
|
||||
yajl:
|
||||
curl -o tmp/yajl.tar.gz https://codeload.github.com/lloyd/yajl/tar.gz/2.1.0
|
||||
tar -zxvf tmp/yajl.tar.gz -C tmp
|
||||
mkdir -p ./tmp/yajl-2.1.0/build && cd ./tmp/yajl-2.1.0/build && cmake .. && make
|
||||
echo `find . -name '*.c'`
|
||||
cd ./tmp/yajl-2.1.0/src && \
|
||||
clang -arch arm64 -arch armv7 -std=c99 \
|
||||
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include/ \
|
||||
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include/machine \
|
||||
-I ../build/yajl-2.1.0/include \
|
||||
-c `find . -name '*.c'`
|
||||
libtool -static -o ./tmp/yajl.a `find ./tmp/yajl-2.1.0/src/ -name '*.o'`
|
||||
|
||||
x86_64:
|
||||
clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_x86_64 -std=c++11 \
|
||||
-install_name RCTJSCProfiler.ios8.dylib \
|
||||
-include ./tmp/JavaScriptCore/config.h \
|
||||
-I ./tmp \
|
||||
-I ./tmp/WebCore-7600.1.25/icu \
|
||||
-I ./tmp/WTF-7600.1.24 \
|
||||
-I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \
|
||||
-DNDEBUG=1\
|
||||
-miphoneos-version-min=8.0 \
|
||||
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib \
|
||||
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib/system \
|
||||
${HEADER_PATHS} \
|
||||
-undefined dynamic_lookup \
|
||||
./JSCLegacyProfiler.mm ./tmp/yajl-2.1.0/build/yajl-2.1.0/lib/libyajl_s.a
|
||||
|
||||
arm64:
|
||||
echo $(HEADER_PATHS)
|
||||
clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_arm64 -std=c++11 \
|
||||
-install_name RCTJSCProfiler.ios8.dylib \
|
||||
-arch arm64 \
|
||||
-include ./tmp/JavaScriptCore/config.h \
|
||||
-I ./tmp \
|
||||
-I ./tmp/WebCore-7600.1.25/icu \
|
||||
-I ./tmp/WTF-7600.1.24 \
|
||||
-I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \
|
||||
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include \
|
||||
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include/machine \
|
||||
-DNDEBUG=1\
|
||||
-miphoneos-version-min=8.0 \
|
||||
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib \
|
||||
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib/system \
|
||||
${HEADER_PATHS} \
|
||||
-undefined dynamic_lookup \
|
||||
./JSCLegacyProfiler.mm ./tmp/yajl.a
|
||||
|
||||
armv7:
|
||||
clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_armv7 -std=c++11 \
|
||||
-install_name RCTJSCProfiler.ios8.dylib \
|
||||
-arch armv7 \
|
||||
-include ./tmp/JavaScriptCore/config.h \
|
||||
-I ./tmp \
|
||||
-I ./tmp/WebCore-7600.1.25/icu \
|
||||
-I ./tmp/WTF-7600.1.24 \
|
||||
-I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \
|
||||
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include \
|
||||
-DNDEBUG=1\
|
||||
-miphoneos-version-min=8.0 \
|
||||
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib \
|
||||
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib/system \
|
||||
${HEADER_PATHS} \
|
||||
-undefined dynamic_lookup \
|
||||
./JSCLegacyProfiler.mm ./tmp/yajl.a
|
||||
|
||||
.PHONY: ios8
|
||||
@@ -87,7 +87,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
|
||||
}
|
||||
|
||||
NSDictionary *fontDict = dict[@"font"];
|
||||
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"]];
|
||||
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] scaleMultiplier:1.0];
|
||||
if (!font) {
|
||||
return frame;
|
||||
}
|
||||
@@ -144,7 +144,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
|
||||
+ (ARTBrush *)ARTBrush:(id)json
|
||||
{
|
||||
NSArray *arr = [self NSArray:json];
|
||||
NSUInteger type = [self NSUInteger:arr[0]];
|
||||
NSUInteger type = [self NSUInteger:arr.firstObject];
|
||||
switch (type) {
|
||||
case 0: // solid color
|
||||
// These are probably expensive allocations since it's often the same value.
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
@@ -190,6 +191,7 @@
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#import "RCTActionSheetManager.h"
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@@ -72,13 +73,13 @@ RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options
|
||||
successCallback:(RCTResponseSenderBlock)successCallback)
|
||||
{
|
||||
NSMutableArray *items = [NSMutableArray array];
|
||||
id message = options[@"message"];
|
||||
id url = options[@"url"];
|
||||
if ([message isKindOfClass:[NSString class]]) {
|
||||
NSString *message = [RCTConvert NSString:options[@"message"]];
|
||||
if (message) {
|
||||
[items addObject:message];
|
||||
}
|
||||
if ([url isKindOfClass:[NSString class]]) {
|
||||
[items addObject:[NSURL URLWithString:url]];
|
||||
NSURL *URL = [RCTConvert NSURL:options[@"url"]];
|
||||
if (URL) {
|
||||
[items addObject:URL];
|
||||
}
|
||||
if ([items count] == 0) {
|
||||
failureCallback(@[@"No `url` or `message` to share"]);
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
@@ -195,6 +196,7 @@
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
|
||||
@@ -17,12 +17,12 @@ var InteractionManager = require('InteractionManager');
|
||||
var Interpolation = require('Interpolation');
|
||||
var React = require('React');
|
||||
var Set = require('Set');
|
||||
var SpringConfig = require('SpringConfig');
|
||||
var Text = require('Text');
|
||||
var View = require('View');
|
||||
var invariant = require('invariant');
|
||||
|
||||
var flattenStyle = require('flattenStyle');
|
||||
var rebound = require('rebound');
|
||||
var requestAnimationFrame = require('requestAnimationFrame');
|
||||
|
||||
import type InterpolationConfigType from 'Interpolation';
|
||||
@@ -351,12 +351,12 @@ class SpringAnimation extends Animation {
|
||||
config.tension === undefined && config.friction === undefined,
|
||||
'You can only define bounciness/speed or tension/friction but not both',
|
||||
);
|
||||
springConfig = rebound.SpringConfig.fromBouncinessAndSpeed(
|
||||
springConfig = SpringConfig.fromBouncinessAndSpeed(
|
||||
withDefault(config.bounciness, 8),
|
||||
withDefault(config.speed, 12),
|
||||
);
|
||||
} else {
|
||||
springConfig = rebound.SpringConfig.fromOrigamiTensionAndFriction(
|
||||
springConfig = SpringConfig.fromOrigamiTensionAndFriction(
|
||||
withDefault(config.tension, 40),
|
||||
withDefault(config.friction, 7),
|
||||
);
|
||||
@@ -479,7 +479,13 @@ class SpringAnimation extends Animation {
|
||||
if (this._tension !== 0) {
|
||||
isDisplacement = Math.abs(this._toValue - position) <= this._restDisplacementThreshold;
|
||||
}
|
||||
|
||||
if (isOvershooting || (isVelocity && isDisplacement)) {
|
||||
if (this._tension !== 0) {
|
||||
// Ensure that we end up with a round value
|
||||
this._onUpdate(this._toValue);
|
||||
}
|
||||
|
||||
this.__debouncedOnEnd({finished: true});
|
||||
return;
|
||||
}
|
||||
@@ -1177,7 +1183,7 @@ var parallel = function(
|
||||
}
|
||||
|
||||
animations.forEach((animation, idx) => {
|
||||
animation.start(endResult => {
|
||||
var cb = function(endResult) {
|
||||
hasEnded[idx] = true;
|
||||
doneCount++;
|
||||
if (doneCount === animations.length) {
|
||||
@@ -1189,7 +1195,13 @@ var parallel = function(
|
||||
if (!endResult.finished && stopTogether) {
|
||||
result.stop();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (!animation) {
|
||||
cb({finished: true});
|
||||
} else {
|
||||
animation.start(cb);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule SpringConfig
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
type SpringConfigType = {
|
||||
tension: number,
|
||||
friction: number,
|
||||
};
|
||||
|
||||
function tensionFromOrigamiValue(oValue) {
|
||||
return (oValue - 30) * 3.62 + 194;
|
||||
}
|
||||
|
||||
function frictionFromOrigamiValue(oValue) {
|
||||
return (oValue - 8) * 3 + 25;
|
||||
}
|
||||
|
||||
function fromOrigamiTensionAndFriction(
|
||||
tension: number,
|
||||
friction: number,
|
||||
): SpringConfigType {
|
||||
return {
|
||||
tension: tensionFromOrigamiValue(tension),
|
||||
friction: frictionFromOrigamiValue(friction)
|
||||
};
|
||||
}
|
||||
|
||||
function fromBouncinessAndSpeed(
|
||||
bounciness: number,
|
||||
speed: number,
|
||||
): SpringConfigType {
|
||||
function normalize(value, startValue, endValue) {
|
||||
return (value - startValue) / (endValue - startValue);
|
||||
}
|
||||
|
||||
function projectNormal(n, start, end) {
|
||||
return start + (n * (end - start));
|
||||
}
|
||||
|
||||
function linearInterpolation(t, start, end) {
|
||||
return t * end + (1 - t) * start;
|
||||
}
|
||||
|
||||
function quadraticOutInterpolation(t, start, end) {
|
||||
return linearInterpolation(2 * t - t * t, start, end);
|
||||
}
|
||||
|
||||
function b3Friction1(x) {
|
||||
return (0.0007 * Math.pow(x, 3)) -
|
||||
(0.031 * Math.pow(x, 2)) + 0.64 * x + 1.28;
|
||||
}
|
||||
|
||||
function b3Friction2(x) {
|
||||
return (0.000044 * Math.pow(x, 3)) -
|
||||
(0.006 * Math.pow(x, 2)) + 0.36 * x + 2;
|
||||
}
|
||||
|
||||
function b3Friction3(x) {
|
||||
return (0.00000045 * Math.pow(x, 3)) -
|
||||
(0.000332 * Math.pow(x, 2)) + 0.1078 * x + 5.84;
|
||||
}
|
||||
|
||||
function b3Nobounce(tension) {
|
||||
if (tension <= 18) {
|
||||
return b3Friction1(tension);
|
||||
} else if (tension > 18 && tension <= 44) {
|
||||
return b3Friction2(tension);
|
||||
} else {
|
||||
return b3Friction3(tension);
|
||||
}
|
||||
}
|
||||
|
||||
var b = normalize(bounciness / 1.7, 0, 20);
|
||||
b = projectNormal(b, 0, 0.8);
|
||||
var s = normalize(speed / 1.7, 0, 20);
|
||||
var bouncyTension = projectNormal(s, 0.5, 200);
|
||||
var bouncyFriction = quadraticOutInterpolation(
|
||||
b,
|
||||
b3Nobounce(bouncyTension),
|
||||
0.01
|
||||
);
|
||||
|
||||
return {
|
||||
tension: tensionFromOrigamiValue(bouncyTension),
|
||||
friction: frictionFromOrigamiValue(bouncyFriction)
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fromOrigamiTensionAndFriction,
|
||||
fromBouncinessAndSpeed,
|
||||
};
|
||||
@@ -127,6 +127,19 @@ describe('Animated', () => {
|
||||
Animated.spring(anim, {toValue: 0, velocity: 0}).start(callback);
|
||||
expect(callback).toBeCalled();
|
||||
});
|
||||
|
||||
it('send toValue when a spring stops', () => {
|
||||
var anim = new Animated.Value(0);
|
||||
var listener = jest.genMockFunction();
|
||||
anim.addListener(listener);
|
||||
Animated.spring(anim, {toValue: 15}).start();
|
||||
jest.runAllTimers();
|
||||
var lastValue = listener.mock.calls[listener.mock.calls.length - 2][0].value;
|
||||
expect(lastValue).not.toBe(15);
|
||||
expect(lastValue).toBeCloseTo(15);
|
||||
expect(anim.__getValue()).toBe(15);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -205,6 +218,16 @@ describe('Animated Parallel', () => {
|
||||
expect(cb).toBeCalledWith({finished: true});
|
||||
});
|
||||
|
||||
it('works with an empty element in array', () => {
|
||||
var anim1 = {start: jest.genMockFunction()};
|
||||
var cb = jest.genMockFunction();
|
||||
Animated.parallel([null, anim1]).start(cb);
|
||||
|
||||
expect(anim1.start).toBeCalled();
|
||||
anim1.start.mock.calls[0][0]({finished: true});
|
||||
|
||||
expect(cb).toBeCalledWith({finished: true});
|
||||
});
|
||||
|
||||
it('parellelizes well', () => {
|
||||
var anim1 = {start: jest.genMockFunction()};
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule AnimationExperimental
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var RCTAnimationManager = require('NativeModules').AnimationExperimentalManager;
|
||||
if (!RCTAnimationManager) {
|
||||
// AnimationExperimental isn't available internally - this is a temporary
|
||||
// workaround to enable its availability to be determined at runtime.
|
||||
// For Flow let's pretend like we always export AnimationExperimental
|
||||
// so all our users don't need to do null checks
|
||||
module.exports = null;
|
||||
} else {
|
||||
|
||||
var React = require('React');
|
||||
var AnimationUtils = require('AnimationUtils');
|
||||
|
||||
type EasingFunction = (t: number) => number;
|
||||
|
||||
var Properties = {
|
||||
opacity: true,
|
||||
position: true,
|
||||
positionX: true,
|
||||
positionY: true,
|
||||
rotation: true,
|
||||
scaleXY: true,
|
||||
};
|
||||
|
||||
type ValueType = number | Array<number> | {[key: string]: number};
|
||||
|
||||
/**
|
||||
* This is an experimental module that is under development, incomplete,
|
||||
* potentially buggy, not used in any production apps, and will probably change
|
||||
* in non-backward compatible ways.
|
||||
*
|
||||
* Use at your own risk.
|
||||
*/
|
||||
var AnimationExperimental = {
|
||||
startAnimation: function(
|
||||
anim: {
|
||||
node: any;
|
||||
duration: number;
|
||||
easing: ($Enum<typeof AnimationUtils.Defaults> | EasingFunction);
|
||||
property: $Enum<typeof Properties>;
|
||||
toValue: ValueType;
|
||||
fromValue?: ValueType;
|
||||
delay?: number;
|
||||
},
|
||||
callback?: ?(finished: bool) => void
|
||||
): number {
|
||||
var nodeHandle = React.findNodeHandle(anim.node);
|
||||
var easingSample = AnimationUtils.evaluateEasingFunction(
|
||||
anim.duration,
|
||||
anim.easing
|
||||
);
|
||||
var tag: number = AnimationUtils.allocateTag();
|
||||
var props = {};
|
||||
props[anim.property] = {to: anim.toValue};
|
||||
RCTAnimationManager.startAnimation(
|
||||
nodeHandle,
|
||||
tag,
|
||||
anim.duration,
|
||||
anim.delay,
|
||||
easingSample,
|
||||
props,
|
||||
callback
|
||||
);
|
||||
return tag;
|
||||
},
|
||||
|
||||
stopAnimation: function(tag: number) {
|
||||
RCTAnimationManager.stopAnimation(tag);
|
||||
},
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
if (RCTAnimationManager && RCTAnimationManager.Properties) {
|
||||
var a = Object.keys(Properties);
|
||||
var b = RCTAnimationManager.Properties;
|
||||
var diff = a.filter((i) => b.indexOf(i) < 0).concat(
|
||||
b.filter((i) => a.indexOf(i) < 0)
|
||||
);
|
||||
if (diff.length > 0) {
|
||||
throw new Error('JS animation properties don\'t match native properties.' +
|
||||
JSON.stringify(diff, null, ' '));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AnimationExperimental;
|
||||
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* The easing functions come from the the jQuery UI project.
|
||||
* See http://api.jqueryui.com/easings/
|
||||
* Copyright jQuery Foundation and other contributors, https://jquery.org/
|
||||
* Copyright (c) 2008 George McGinley Smith
|
||||
* Copyright (c) 2001 Robert Penner
|
||||
*
|
||||
* @providesModule AnimationUtils
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
type EasingFunction = (t: number) => number;
|
||||
|
||||
var defaults = {
|
||||
linear: function(t: number): number {
|
||||
return t;
|
||||
},
|
||||
easeInQuad: function(t: number): number {
|
||||
return t * t;
|
||||
},
|
||||
easeOutQuad: function(t: number): number {
|
||||
return -t * (t - 2);
|
||||
},
|
||||
easeInOutQuad: function(t: number): number {
|
||||
t = t * 2;
|
||||
if (t < 1) {
|
||||
return 0.5 * t * t;
|
||||
}
|
||||
return -((t - 1) * (t - 3) - 1) / 2;
|
||||
},
|
||||
easeInCubic: function(t: number): number {
|
||||
return t * t * t;
|
||||
},
|
||||
easeOutCubic: function(t: number): number {
|
||||
t -= 1;
|
||||
return t * t * t + 1;
|
||||
},
|
||||
easeInOutCubic: function(t: number): number {
|
||||
t *= 2;
|
||||
if (t < 1) {
|
||||
return 0.5 * t * t * t;
|
||||
}
|
||||
t -= 2;
|
||||
return (t * t * t + 2) / 2;
|
||||
},
|
||||
easeInQuart: function(t: number): number {
|
||||
return t * t * t * t;
|
||||
},
|
||||
easeOutQuart: function(t: number): number {
|
||||
t -= 1;
|
||||
return -(t * t * t * t - 1);
|
||||
},
|
||||
easeInOutQuart: function(t: number): number {
|
||||
t *= 2;
|
||||
if (t < 1) {
|
||||
return 0.5 * t * t * t * t;
|
||||
}
|
||||
t -= 2;
|
||||
return -(t * t * t * t - 2) / 2;
|
||||
},
|
||||
easeInQuint: function(t: number): number {
|
||||
return t * t * t * t * t;
|
||||
},
|
||||
easeOutQuint: function(t: number): number {
|
||||
t -= 1;
|
||||
return t * t * t * t * t + 1;
|
||||
},
|
||||
easeInOutQuint: function(t: number): number {
|
||||
t *= 2;
|
||||
if (t < 1) {
|
||||
return (t * t * t * t * t) / 2;
|
||||
}
|
||||
t -= 2;
|
||||
return (t * t * t * t * t + 2) / 2;
|
||||
},
|
||||
easeInSine: function(t: number): number {
|
||||
return -Math.cos(t * (Math.PI / 2)) + 1;
|
||||
},
|
||||
easeOutSine: function(t: number): number {
|
||||
return Math.sin(t * (Math.PI / 2));
|
||||
},
|
||||
easeInOutSine: function(t: number): number {
|
||||
return -(Math.cos(Math.PI * t) - 1) / 2;
|
||||
},
|
||||
easeInExpo: function(t: number): number {
|
||||
return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
|
||||
},
|
||||
easeOutExpo: function(t: number): number {
|
||||
return (t === 1) ? 1 : (-Math.pow(2, -10 * t) + 1);
|
||||
},
|
||||
easeInOutExpo: function(t: number): number {
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if (t === 1) {
|
||||
return 1;
|
||||
}
|
||||
t *= 2;
|
||||
if (t < 1) {
|
||||
return 0.5 * Math.pow(2, 10 * (t - 1));
|
||||
}
|
||||
return (-Math.pow(2, -10 * (t - 1)) + 2) / 2;
|
||||
},
|
||||
easeInCirc: function(t: number): number {
|
||||
return -(Math.sqrt(1 - t * t) - 1);
|
||||
},
|
||||
easeOutCirc: function(t: number): number {
|
||||
t -= 1;
|
||||
return Math.sqrt(1 - t * t);
|
||||
},
|
||||
easeInOutCirc: function(t: number): number {
|
||||
t *= 2;
|
||||
if (t < 1) {
|
||||
return -(Math.sqrt(1 - t * t) - 1) / 2;
|
||||
}
|
||||
t -= 2;
|
||||
return (Math.sqrt(1 - t * t) + 1) / 2;
|
||||
},
|
||||
easeInElastic: function(t: number): number {
|
||||
var s = 1.70158;
|
||||
var p = 0.3;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if (t === 1) {
|
||||
return 1;
|
||||
}
|
||||
var s = p / (2 * Math.PI) * Math.asin(1);
|
||||
t -= 1;
|
||||
return -(Math.pow(2, 10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
|
||||
},
|
||||
easeOutElastic: function(t: number): number {
|
||||
var s = 1.70158;
|
||||
var p = 0.3;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
if (t === 1) {
|
||||
return 1;
|
||||
}
|
||||
var s = p / (2 * Math.PI) * Math.asin(1);
|
||||
return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
|
||||
},
|
||||
easeInOutElastic: function(t: number): number {
|
||||
var s = 1.70158;
|
||||
var p = 0.3 * 1.5;
|
||||
if (t === 0) {
|
||||
return 0;
|
||||
}
|
||||
t *= 2;
|
||||
if (t === 2) {
|
||||
return 1;
|
||||
}
|
||||
var s = p / (2 * Math.PI) * Math.asin(1);
|
||||
if (t < 1) {
|
||||
t -= 1;
|
||||
return -(Math.pow(2, 10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)) / 2;
|
||||
}
|
||||
t -= 1;
|
||||
return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) / 2 + 1;
|
||||
},
|
||||
easeInBack: function(t: number): number {
|
||||
var s = 1.70158;
|
||||
return t * t * ((s + 1) * t - s);
|
||||
},
|
||||
easeOutBack: function(t: number): number {
|
||||
var s = 1.70158;
|
||||
t -= 1;
|
||||
return (t * t * ((s + 1) * t + s) + 1);
|
||||
},
|
||||
easeInOutBack: function(t: number): number {
|
||||
var s = 1.70158 * 1.525;
|
||||
t *= 2;
|
||||
if (t < 1) {
|
||||
return (t * t * ((s + 1) * t - s)) / 2;
|
||||
}
|
||||
t -= 2;
|
||||
return (t * t * ((s + 1) * t + s) + 2) / 2;
|
||||
},
|
||||
easeInBounce: function(t: number): number {
|
||||
return 1 - this.easeOutBounce(1 - t);
|
||||
},
|
||||
easeOutBounce: function(t: number): number {
|
||||
if (t < (1 / 2.75)) {
|
||||
return 7.5625 * t * t;
|
||||
} else if (t < (2 / 2.75)) {
|
||||
t -= 1.5 / 2.75;
|
||||
return 7.5625 * t * t + 0.75;
|
||||
} else if (t < (2.5 / 2.75)) {
|
||||
t -= 2.25 / 2.75;
|
||||
return 7.5625 * t * t + 0.9375;
|
||||
} else {
|
||||
t -= 2.625 / 2.75;
|
||||
return 7.5625 * t * t + 0.984375;
|
||||
}
|
||||
},
|
||||
easeInOutBounce: function(t: number): number {
|
||||
if (t < 0.5) {
|
||||
return this.easeInBounce(t * 2) / 2;
|
||||
}
|
||||
return this.easeOutBounce(t * 2 - 1) / 2 + 0.5;
|
||||
},
|
||||
};
|
||||
|
||||
var ticksPerSecond = 60;
|
||||
var lastUsedTag = 0;
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Returns a new unique animation tag.
|
||||
*/
|
||||
allocateTag: function(): number {
|
||||
return ++lastUsedTag;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the values of the easing function at discrete ticks (60 per second).
|
||||
*/
|
||||
evaluateEasingFunction: function(duration: number, easing: string | EasingFunction): Array<number> {
|
||||
if (typeof easing === 'string') {
|
||||
easing = defaults[easing] || defaults.easeOutQuad;
|
||||
}
|
||||
|
||||
var tickCount = Math.round(duration * ticksPerSecond / 1000);
|
||||
var samples = [];
|
||||
if (tickCount > 0) {
|
||||
for (var i = 0; i <= tickCount; i++) {
|
||||
samples.push(easing.call(defaults, i / tickCount));
|
||||
}
|
||||
}
|
||||
|
||||
return samples;
|
||||
},
|
||||
|
||||
Defaults: defaults,
|
||||
};
|
||||
@@ -69,9 +69,11 @@ type Config = {
|
||||
delete?: Anim;
|
||||
}
|
||||
|
||||
function configureNext(config: Config, onAnimationDidEnd?: Function, onError?: Function) {
|
||||
function configureNext(config: Config, onAnimationDidEnd?: Function) {
|
||||
configChecker({config}, 'config', 'LayoutAnimation.configureNext');
|
||||
RCTUIManager.configureNextLayoutAnimation(config, onAnimationDidEnd, onError);
|
||||
RCTUIManager.configureNextLayoutAnimation(
|
||||
config, onAnimationDidEnd || function() {}, function() { /* unused */ }
|
||||
);
|
||||
}
|
||||
|
||||
function create(duration: number, type, creationProp): Config {
|
||||
@@ -107,8 +109,32 @@ var Presets = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Automatically animates views to their new positions when the
|
||||
* next layout happens.
|
||||
*
|
||||
* A common way to use this API is to call `LayoutAnimation.configureNext`
|
||||
* before calling `setState`.
|
||||
*/
|
||||
var LayoutAnimation = {
|
||||
/**
|
||||
* Schedules an animation to happen on the next layout.
|
||||
*
|
||||
* @param config Specifies animation properties:
|
||||
*
|
||||
* - `duration` in milliseconds
|
||||
* - `create`, config for animating in new views (see `Anim` type)
|
||||
* - `update`, config for animating views that have been updated
|
||||
* (see `Anim` type)
|
||||
*
|
||||
* @param onAnimationDidEnd Called when the animation finished.
|
||||
* Only supported on iOS.
|
||||
* @param onError Called on error. Only supported on iOS.
|
||||
*/
|
||||
configureNext,
|
||||
/**
|
||||
* Helper for creating a config for `configureNext`.
|
||||
*/
|
||||
create,
|
||||
Types,
|
||||
Properties,
|
||||
|
||||
@@ -1,250 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
13BE3DEE1AC21097009241FE /* RCTAnimationExperimentalManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
58B511D91A9E6C8500147676 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTAnimationExperimental.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13BE3DEC1AC21097009241FE /* RCTAnimationExperimentalManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationExperimentalManager.h; sourceTree = "<group>"; };
|
||||
13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationExperimentalManager.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
58B511D81A9E6C8500147676 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
134814211AA4EA7D00B7C361 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13BE3DEC1AC21097009241FE /* RCTAnimationExperimentalManager.h */,
|
||||
13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
58B511DA1A9E6C8500147676 /* RCTAnimationExperimental */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimationExperimental" */;
|
||||
buildPhases = (
|
||||
58B511D71A9E6C8500147676 /* Sources */,
|
||||
58B511D81A9E6C8500147676 /* Frameworks */,
|
||||
58B511D91A9E6C8500147676 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = RCTAnimationExperimental;
|
||||
productName = RCTDataManager;
|
||||
productReference = 134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
58B511D31A9E6C8500147676 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0610;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
58B511DA1A9E6C8500147676 = {
|
||||
CreatedOnToolsVersion = 6.1.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimationExperimental" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 58B511D21A9E6C8500147676;
|
||||
productRefGroup = 58B511D21A9E6C8500147676;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
58B511DA1A9E6C8500147676 /* RCTAnimationExperimental */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
58B511D71A9E6C8500147676 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13BE3DEE1AC21097009241FE /* RCTAnimationExperimentalManager.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
58B511ED1A9E6C8500147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511EE1A9E6C8500147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
58B511F01A9E6C8500147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RCTAnimationExperimental;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511F11A9E6C8500147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RCTAnimationExperimental;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimationExperimental" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511ED1A9E6C8500147676 /* Debug */,
|
||||
58B511EE1A9E6C8500147676 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimationExperimental" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511F01A9E6C8500147676 /* Debug */,
|
||||
58B511F11A9E6C8500147676 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTAnimationExperimentalManager.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#if CGFLOAT_IS_DOUBLE
|
||||
#define CG_APPEND(PREFIX, SUFFIX_F, SUFFIX_D) PREFIX##SUFFIX_D
|
||||
#else
|
||||
#define CG_APPEND(PREFIX, SUFFIX_F, SUFFIX_D) PREFIX##SUFFIX_F
|
||||
#endif
|
||||
|
||||
@implementation RCTAnimationExperimentalManager
|
||||
{
|
||||
RCTSparseArray *_animationRegistry; // Main thread only; animation tag -> view tag
|
||||
RCTSparseArray *_callbackRegistry; // Main thread only; animation tag -> callback
|
||||
NSDictionary *_keypathMapping;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_animationRegistry = [[RCTSparseArray alloc] init];
|
||||
_callbackRegistry = [[RCTSparseArray alloc] init];
|
||||
_keypathMapping = @{
|
||||
@"opacity": @{
|
||||
@"keypath": @"opacity",
|
||||
@"type": @"NSNumber",
|
||||
},
|
||||
@"position": @{
|
||||
@"keypath": @"position",
|
||||
@"type": @"CGPoint",
|
||||
},
|
||||
@"positionX": @{
|
||||
@"keypath": @"position.x",
|
||||
@"type": @"NSNumber",
|
||||
},
|
||||
@"positionY": @{
|
||||
@"keypath": @"position.y",
|
||||
@"type": @"NSNumber",
|
||||
},
|
||||
@"rotation": @{
|
||||
@"keypath": @"transform.rotation.z",
|
||||
@"type": @"NSNumber",
|
||||
},
|
||||
@"scaleXY": @{
|
||||
@"keypath": @"transform.scale",
|
||||
@"type": @"CGPoint",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)methodQueue
|
||||
{
|
||||
return _bridge.uiManager.methodQueue;
|
||||
}
|
||||
|
||||
- (id (^)(CGFloat))interpolateFrom:(CGFloat[])fromArray to:(CGFloat[])toArray count:(NSUInteger)count typeName:(const char *)typeName
|
||||
{
|
||||
if (count == 1) {
|
||||
CGFloat from = *fromArray, to = *toArray, delta = to - from;
|
||||
return ^(CGFloat t) {
|
||||
return @(from + t * delta);
|
||||
};
|
||||
}
|
||||
|
||||
CG_APPEND(vDSP_vsub,,D)(fromArray, 1, toArray, 1, toArray, 1, count);
|
||||
|
||||
const size_t size = count * sizeof(CGFloat);
|
||||
NSData *deltaData = [NSData dataWithBytes:toArray length:size];
|
||||
NSData *fromData = [NSData dataWithBytes:fromArray length:size];
|
||||
|
||||
return ^(CGFloat t) {
|
||||
const CGFloat *delta = deltaData.bytes;
|
||||
const CGFloat *_fromArray = fromData.bytes;
|
||||
|
||||
CGFloat value[count];
|
||||
CG_APPEND(vDSP_vma,,D)(delta, 1, &t, 0, _fromArray, 1, value, 1, count);
|
||||
return [NSValue valueWithBytes:value objCType:typeName];
|
||||
};
|
||||
}
|
||||
|
||||
static void RCTInvalidAnimationProp(RCTSparseArray *callbacks, NSNumber *tag, NSString *key, id value)
|
||||
{
|
||||
RCTResponseSenderBlock callback = callbacks[tag];
|
||||
RCTLogError(@"Invalid animation property `%@ = %@`", key, value);
|
||||
if (callback) {
|
||||
callback(@[@NO]);
|
||||
callbacks[tag] = nil;
|
||||
}
|
||||
[CATransaction commit];
|
||||
return;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(startAnimation:(NSNumber *)reactTag
|
||||
animationTag:(NSNumber *)animationTag
|
||||
duration:(NSTimeInterval)duration
|
||||
delay:(NSTimeInterval)delay
|
||||
easingSample:(NSNumberArray *)easingSample
|
||||
properties:(NSDictionary *)properties
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
__weak RCTAnimationExperimentalManager *weakSelf = self;
|
||||
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
RCTAnimationExperimentalManager *strongSelf = weakSelf;
|
||||
|
||||
UIView *view = viewRegistry[reactTag];
|
||||
if (!view) {
|
||||
RCTLogWarn(@"React tag #%@ is not registered with the view registry", reactTag);
|
||||
return;
|
||||
}
|
||||
__block BOOL completionBlockSet = NO;
|
||||
[CATransaction begin];
|
||||
for (NSString *prop in properties) {
|
||||
NSString *keypath = _keypathMapping[prop][@"keypath"];
|
||||
id obj = properties[prop][@"to"];
|
||||
if (!keypath) {
|
||||
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
|
||||
}
|
||||
NSValue *toValue = nil;
|
||||
if ([keypath isEqualToString:@"transform.scale"]) {
|
||||
CGPoint point = [RCTConvert CGPoint:obj];
|
||||
if (point.x != point.y) {
|
||||
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
|
||||
}
|
||||
toValue = @(point.x);
|
||||
} else if ([obj respondsToSelector:@selector(count)]) {
|
||||
switch ([obj count]) {
|
||||
case 2:
|
||||
if (![obj respondsToSelector:@selector(objectForKeyedSubscript:)] || obj[@"x"]) {
|
||||
toValue = [NSValue valueWithCGPoint:[RCTConvert CGPoint:obj]];
|
||||
} else {
|
||||
toValue = [NSValue valueWithCGSize:[RCTConvert CGSize:obj]];
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
toValue = [NSValue valueWithCGRect:[RCTConvert CGRect:obj]];
|
||||
break;
|
||||
case 16:
|
||||
toValue = [NSValue valueWithCGAffineTransform:[RCTConvert CGAffineTransform:obj]];
|
||||
break;
|
||||
default:
|
||||
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
|
||||
}
|
||||
} else if (![obj respondsToSelector:@selector(objCType)]) {
|
||||
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
|
||||
}
|
||||
if (!toValue) {
|
||||
toValue = obj;
|
||||
}
|
||||
const char *typeName = toValue.objCType;
|
||||
|
||||
size_t count;
|
||||
switch (typeName[0]) {
|
||||
case 'i':
|
||||
case 'I':
|
||||
case 's':
|
||||
case 'S':
|
||||
case 'l':
|
||||
case 'L':
|
||||
case 'q':
|
||||
case 'Q':
|
||||
count = 1;
|
||||
break;
|
||||
|
||||
default: {
|
||||
NSUInteger size;
|
||||
NSGetSizeAndAlignment(typeName, &size, NULL);
|
||||
count = size / sizeof(CGFloat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CGFloat toFields[count];
|
||||
|
||||
switch (typeName[0]) {
|
||||
#define CASE(encoding, type) \
|
||||
case encoding: { \
|
||||
type value; \
|
||||
[toValue getValue:&value]; \
|
||||
toFields[0] = value; \
|
||||
break; \
|
||||
}
|
||||
|
||||
CASE('i', int)
|
||||
CASE('I', unsigned int)
|
||||
CASE('s', short)
|
||||
CASE('S', unsigned short)
|
||||
CASE('l', long)
|
||||
CASE('L', unsigned long)
|
||||
CASE('q', long long)
|
||||
CASE('Q', unsigned long long)
|
||||
|
||||
#undef CASE
|
||||
|
||||
default:
|
||||
[toValue getValue:toFields];
|
||||
break;
|
||||
}
|
||||
|
||||
NSValue *fromValue = [view.layer.presentationLayer valueForKeyPath:keypath];
|
||||
#if !CGFLOAT_IS_DOUBLE
|
||||
if ([fromValue isKindOfClass:[NSNumber class]]) {
|
||||
fromValue = [NSNumber numberWithFloat:[(NSNumber *)fromValue doubleValue]];
|
||||
}
|
||||
#endif
|
||||
CGFloat fromFields[count];
|
||||
[fromValue getValue:fromFields];
|
||||
|
||||
id (^interpolationBlock)(CGFloat t) = [strongSelf interpolateFrom:fromFields to:toFields count:count typeName:typeName];
|
||||
|
||||
NSMutableArray *sampledValues = [NSMutableArray arrayWithCapacity:easingSample.count];
|
||||
for (NSNumber *sample in easingSample) {
|
||||
CGFloat t = sample.CG_APPEND(, floatValue, doubleValue);
|
||||
[sampledValues addObject:interpolationBlock(t)];
|
||||
}
|
||||
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keypath];
|
||||
animation.beginTime = CACurrentMediaTime() + delay;
|
||||
animation.duration = duration;
|
||||
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
|
||||
animation.values = sampledValues;
|
||||
@try {
|
||||
[view.layer setValue:toValue forKey:keypath];
|
||||
NSString *animationKey = [@"RCT" stringByAppendingString:RCTJSONStringify(@{@"tag": animationTag, @"key": keypath}, nil)];
|
||||
if (!completionBlockSet) {
|
||||
strongSelf->_callbackRegistry[animationTag] = callback;
|
||||
[CATransaction setCompletionBlock:^{
|
||||
RCTResponseSenderBlock cb = strongSelf->_callbackRegistry[animationTag];
|
||||
if (cb) {
|
||||
cb(@[@YES]);
|
||||
strongSelf->_callbackRegistry[animationTag] = nil;
|
||||
}
|
||||
}];
|
||||
completionBlockSet = YES;
|
||||
}
|
||||
[view.layer addAnimation:animation forKey:animationKey];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, toValue);
|
||||
}
|
||||
}
|
||||
[CATransaction commit];
|
||||
strongSelf->_animationRegistry[animationTag] = reactTag;
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(stopAnimation:(NSNumber *)animationTag)
|
||||
{
|
||||
__weak RCTAnimationExperimentalManager *weakSelf = self;
|
||||
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
RCTAnimationExperimentalManager *strongSelf = weakSelf;
|
||||
|
||||
NSNumber *reactTag = strongSelf->_animationRegistry[animationTag];
|
||||
if (!reactTag) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIView *view = viewRegistry[reactTag];
|
||||
for (NSString *animationKey in view.layer.animationKeys) {
|
||||
if ([animationKey hasPrefix:@"RCT{"]) {
|
||||
NSDictionary *data = RCTJSONParse([animationKey substringFromIndex:3], nil);
|
||||
if (animationTag.integerValue == [data[@"tag"] integerValue]) {
|
||||
[view.layer removeAnimationForKey:animationKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
RCTResponseSenderBlock cb = strongSelf->_callbackRegistry[animationTag];
|
||||
if (cb) {
|
||||
cb(@[@NO]);
|
||||
strongSelf->_callbackRegistry[animationTag] = nil;
|
||||
}
|
||||
strongSelf->_animationRegistry[animationTag] = nil;
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
return @{@"Properties": [_keypathMapping allKeys] };
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -22,9 +22,11 @@ if (__DEV__) {
|
||||
|
||||
var runnables = {};
|
||||
|
||||
type ComponentProvider = () => ReactClass<any, any, any>;
|
||||
|
||||
type AppConfig = {
|
||||
appKey: string;
|
||||
component: ReactClass<any, any, any>;
|
||||
component?: ComponentProvider;
|
||||
run?: Function;
|
||||
};
|
||||
|
||||
@@ -46,12 +48,13 @@ var AppRegistry = {
|
||||
if (appConfig.run) {
|
||||
AppRegistry.registerRunnable(appConfig.appKey, appConfig.run);
|
||||
} else {
|
||||
invariant(appConfig.component, 'No component provider passed in');
|
||||
AppRegistry.registerComponent(appConfig.appKey, appConfig.component);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
registerComponent: function(appKey: string, getComponentFunc: Function): string {
|
||||
registerComponent: function(appKey: string, getComponentFunc: ComponentProvider): string {
|
||||
runnables[appKey] = {
|
||||
run: (appParameters) =>
|
||||
renderApplication(getComponentFunc(), appParameters.initialProps, appParameters.rootTag)
|
||||
@@ -64,6 +67,10 @@ var AppRegistry = {
|
||||
return appKey;
|
||||
},
|
||||
|
||||
getAppKeys: function(): Array<string> {
|
||||
return Object.keys(runnables);
|
||||
},
|
||||
|
||||
runApplication: function(appKey: string, appParameters: any): void {
|
||||
console.log(
|
||||
'Running application "' + appKey + '" with appParams: ' +
|
||||
|
||||
@@ -40,6 +40,10 @@ var NativeModules = {
|
||||
SourceCode: {
|
||||
scriptURL: null,
|
||||
},
|
||||
BuildInfo: {
|
||||
appVersion: '0',
|
||||
buildVersion: '0',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = NativeModules;
|
||||
|
||||
@@ -109,17 +109,28 @@ var getPhotosReturnChecker = createStrictShapeTypeChecker({
|
||||
}).isRequired,
|
||||
});
|
||||
|
||||
/**
|
||||
* `CameraRoll` provides access to the local camera roll / gallery.
|
||||
*/
|
||||
class CameraRoll {
|
||||
|
||||
static GroupTypesOptions: Array<string>;
|
||||
static AssetTypeOptions: Array<string>;
|
||||
/**
|
||||
* Saves the image with tag `tag` to the camera roll.
|
||||
* Saves the image to the camera roll / gallery.
|
||||
*
|
||||
* @param {string} tag - Can be any of the three kinds of tags we accept:
|
||||
* 1. URL
|
||||
* 2. assets-library tag
|
||||
* 3. tag returned from storing an image in memory
|
||||
* @param {string} tag On Android, this is a local URI, such
|
||||
* as `"file:///sdcard/img.png"`.
|
||||
*
|
||||
* On iOS, the tag can be one of the following:
|
||||
*
|
||||
* - local URI
|
||||
* - assets-library tag
|
||||
* - a tag not maching any of the above, which means the image data will
|
||||
* be stored in memory (and consume memory as long as the process is alive)
|
||||
*
|
||||
* @param successCallback Invoked with the value of `tag` on success.
|
||||
* @param errorCallback Invoked with error message on error.
|
||||
*/
|
||||
static saveImageWithTag(tag, successCallback, errorCallback) {
|
||||
invariant(
|
||||
@@ -140,10 +151,10 @@ class CameraRoll {
|
||||
* Invokes `callback` with photo identifier objects from the local camera
|
||||
* roll of the device matching shape defined by `getPhotosReturnChecker`.
|
||||
*
|
||||
* @param {object} params - See `getPhotosParamChecker`.
|
||||
* @param {function} callback - Invoked with arg of shape defined by
|
||||
* `getPhotosReturnChecker` on success.
|
||||
* @param {function} errorCallback - Invoked with error message on error.
|
||||
* @param {object} params See `getPhotosParamChecker`.
|
||||
* @param {function} callback Invoked with arg of shape defined by
|
||||
* `getPhotosReturnChecker` on success.
|
||||
* @param {function} errorCallback Invoked with error message on error.
|
||||
*/
|
||||
static getPhotos(params, callback, errorCallback) {
|
||||
var metaCallback = callback;
|
||||
|
||||
@@ -18,7 +18,6 @@ var StyleSheet = require('StyleSheet');
|
||||
var View = require('View');
|
||||
|
||||
var requireNativeComponent = require('requireNativeComponent');
|
||||
var verifyPropTypes = require('verifyPropTypes');
|
||||
|
||||
var GRAY = '#999999';
|
||||
|
||||
@@ -99,15 +98,8 @@ var styles = StyleSheet.create({
|
||||
|
||||
var RCTActivityIndicatorView = requireNativeComponent(
|
||||
'RCTActivityIndicatorView',
|
||||
null
|
||||
ActivityIndicatorIOS,
|
||||
{nativeOnly: {activityIndicatorViewStyle: true}},
|
||||
);
|
||||
if (__DEV__) {
|
||||
var nativeOnlyProps = {activityIndicatorViewStyle: true};
|
||||
verifyPropTypes(
|
||||
ActivityIndicatorIOS,
|
||||
RCTActivityIndicatorView.viewConfig,
|
||||
nativeOnlyProps
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = ActivityIndicatorIOS;
|
||||
|
||||
@@ -15,17 +15,14 @@ var EventEmitter = require('EventEmitter');
|
||||
var Image = require('Image');
|
||||
var NavigationContext = require('NavigationContext');
|
||||
var React = require('React');
|
||||
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
||||
var RCTNavigatorManager = require('NativeModules').NavigatorManager;
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var StaticContainer = require('StaticContainer.react');
|
||||
var View = require('View');
|
||||
|
||||
var createReactNativeComponentClass =
|
||||
require('createReactNativeComponentClass');
|
||||
var requireNativeComponent = require('requireNativeComponent');
|
||||
var invariant = require('invariant');
|
||||
var logError = require('logError');
|
||||
var merge = require('merge');
|
||||
|
||||
var TRANSITIONER_REF = 'transitionerRef';
|
||||
|
||||
@@ -36,36 +33,6 @@ function getuid() {
|
||||
return __uid++;
|
||||
}
|
||||
|
||||
var RCTNavigator = createReactNativeComponentClass({
|
||||
validAttributes: merge(ReactNativeViewAttributes.UIView, {
|
||||
requestedTopOfStack: true
|
||||
}),
|
||||
uiViewClassName: 'RCTNavigator',
|
||||
});
|
||||
|
||||
var RCTNavigatorItem = createReactNativeComponentClass({
|
||||
validAttributes: {
|
||||
// TODO: Remove or fix the attributes that are not fully functional.
|
||||
// NavigatorIOS does not use them all, because some are problematic
|
||||
title: true,
|
||||
barTintColor: true,
|
||||
leftButtonIcon: true,
|
||||
leftButtonTitle: true,
|
||||
onNavLeftButtonTap: true,
|
||||
rightButtonIcon: true,
|
||||
rightButtonTitle: true,
|
||||
onNavRightButtonTap: true,
|
||||
backButtonIcon: true,
|
||||
backButtonTitle: true,
|
||||
tintColor: true,
|
||||
translucent: true,
|
||||
navigationBarHidden: true,
|
||||
titleTextColor: true,
|
||||
style: true,
|
||||
},
|
||||
uiViewClassName: 'RCTNavItem',
|
||||
});
|
||||
|
||||
var NavigatorTransitionerIOS = React.createClass({
|
||||
requestSchedulingNavigation: function(cb) {
|
||||
RCTNavigatorManager.requestSchedulingJavaScriptNavigation(
|
||||
@@ -281,6 +248,11 @@ var NavigatorIOS = React.createClass({
|
||||
*/
|
||||
navigationBarHidden: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* A Boolean value that indicates whether to hide the 1px hairline shadow
|
||||
*/
|
||||
shadowHidden: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The default wrapper style for components in the navigator.
|
||||
* A common use case is to set the backgroundColor for every page
|
||||
@@ -640,6 +612,7 @@ var NavigatorIOS = React.createClass({
|
||||
rightButtonTitle={route.rightButtonTitle}
|
||||
onNavRightButtonTap={route.onRightButtonPress}
|
||||
navigationBarHidden={this.props.navigationBarHidden}
|
||||
shadowHidden={this.props.shadowHidden}
|
||||
tintColor={this.props.tintColor}
|
||||
barTintColor={this.props.barTintColor}
|
||||
translucent={this.props.translucent !== false}
|
||||
@@ -704,4 +677,7 @@ var styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
var RCTNavigator = requireNativeComponent('RCTNavigator');
|
||||
var RCTNavigatorItem = requireNativeComponent('RCTNavItem');
|
||||
|
||||
module.exports = NavigatorIOS;
|
||||
|
||||
@@ -19,7 +19,6 @@ var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
|
||||
var requireNativeComponent = require('requireNativeComponent');
|
||||
var verifyPropTypes = require('verifyPropTypes');
|
||||
|
||||
/**
|
||||
* Use `ProgressViewIOS` to render a UIProgressView on iOS.
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
'use strict';
|
||||
|
||||
var NativeModules = require('NativeModules');
|
||||
var Platform = require('Platform');
|
||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
var React = require('React');
|
||||
var Subscribable = require('Subscribable');
|
||||
var TextInputState = require('TextInputState');
|
||||
|
||||
var RCTUIManager = NativeModules.UIManager;
|
||||
var RCTUIManagerDeprecated = NativeModules.UIManager;
|
||||
var RCTScrollViewConsts = RCTUIManager.RCTScrollView.Constants;
|
||||
|
||||
var warning = require('warning');
|
||||
@@ -351,7 +351,19 @@ var ScrollResponderMixin = {
|
||||
* can also be used to quickly scroll to any element we want to focus
|
||||
*/
|
||||
scrollResponderScrollTo: function(offsetX: number, offsetY: number) {
|
||||
RCTUIManagerDeprecated.scrollTo(React.findNodeHandle(this), offsetX, offsetY);
|
||||
if (Platform.OS === 'android') {
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
React.findNodeHandle(this),
|
||||
RCTUIManager.RCTScrollView.Commands.scrollTo,
|
||||
[offsetX, offsetY],
|
||||
);
|
||||
} else {
|
||||
RCTUIManager.scrollTo(
|
||||
React.findNodeHandle(this),
|
||||
offsetX,
|
||||
offsetY
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -359,7 +371,7 @@ var ScrollResponderMixin = {
|
||||
* @param {object} rect Should have shape {x, y, width, height}
|
||||
*/
|
||||
scrollResponderZoomTo: function(rect: { x: number; y: number; width: number; height: number; }) {
|
||||
RCTUIManagerDeprecated.zoomToRect(React.findNodeHandle(this), rect);
|
||||
RCTUIManager.zoomToRect(React.findNodeHandle(this), rect);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -377,7 +389,7 @@ var ScrollResponderMixin = {
|
||||
this.preventNegativeScrollOffset = !!preventNegativeScrollOffset;
|
||||
RCTUIManager.measureLayout(
|
||||
nodeHandle,
|
||||
React.findNodeHandle(this),
|
||||
React.findNodeHandle(this.getInnerViewNode()),
|
||||
this.scrollResponderTextInputFocusError,
|
||||
this.scrollResponderInputMeasureAndScrollToKeyboard
|
||||
);
|
||||
@@ -429,6 +441,10 @@ var ScrollResponderMixin = {
|
||||
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillHide', this.scrollResponderKeyboardWillHide);
|
||||
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardDidShow', this.scrollResponderKeyboardDidShow);
|
||||
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardDidHide', this.scrollResponderKeyboardDidHide);
|
||||
warning(this.getInnerViewNode, 'You need to implement getInnerViewNode in '
|
||||
+ this.constructor.displayName + ' to get full'
|
||||
+ 'functionality from ScrollResponder mixin. See example of ListView and'
|
||||
+ ' ScrollView.');
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -469,9 +485,13 @@ var ScrollResponderMixin = {
|
||||
this.props.onKeyboardWillHide && this.props.onKeyboardWillHide(e);
|
||||
},
|
||||
|
||||
scrollResponderKeyboardDidShow: function() {
|
||||
this.keyboardWillOpenTo = null;
|
||||
this.props.onKeyboardDidShow && this.props.onKeyboardDidShow();
|
||||
scrollResponderKeyboardDidShow: function(e: Event) {
|
||||
// TODO(7693961): The event for DidShow is not available on iOS yet.
|
||||
// Use the one from WillShow and do not assign.
|
||||
if (e) {
|
||||
this.keyboardWillOpenTo = e;
|
||||
}
|
||||
this.props.onKeyboardDidShow && this.props.onKeyboardDidShow(e);
|
||||
},
|
||||
|
||||
scrollResponderKeyboardDidHide: function() {
|
||||
|
||||
@@ -17,7 +17,6 @@ var PointPropType = require('PointPropType');
|
||||
var RCTScrollView = require('NativeModules').UIManager.RCTScrollView;
|
||||
var RCTScrollViewConsts = RCTScrollView.Constants;
|
||||
var React = require('React');
|
||||
var ReactChildren = require('ReactChildren');
|
||||
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
||||
var RCTUIManager = require('NativeModules').UIManager;
|
||||
var ScrollResponder = require('ScrollResponder');
|
||||
@@ -54,44 +53,54 @@ var INNERVIEW = 'InnerScrollView';
|
||||
* Doesn't yet support other contained responders from blocking this scroll
|
||||
* view from becoming the responder.
|
||||
*/
|
||||
|
||||
var ScrollView = React.createClass({
|
||||
propTypes: {
|
||||
automaticallyAdjustContentInsets: PropTypes.bool, // true
|
||||
contentInset: EdgeInsetsPropType, // zeros
|
||||
contentOffset: PointPropType, // zeros
|
||||
onScroll: PropTypes.func,
|
||||
onScrollAnimationEnd: PropTypes.func,
|
||||
scrollEnabled: PropTypes.bool, // true
|
||||
scrollIndicatorInsets: EdgeInsetsPropType, // zeros
|
||||
showsHorizontalScrollIndicator: PropTypes.bool,
|
||||
showsVerticalScrollIndicator: PropTypes.bool,
|
||||
style: StyleSheetPropType(ViewStylePropTypes),
|
||||
scrollEventThrottle: PropTypes.number, // null
|
||||
|
||||
/**
|
||||
* Controls whether iOS should automatically adjust the content inset
|
||||
* for scroll views that are placed behind a navigation bar or
|
||||
* tab bar/ toolbar. The default value is true.
|
||||
* @platform ios
|
||||
*/
|
||||
automaticallyAdjustContentInsets: PropTypes.bool,
|
||||
/**
|
||||
* The amount by which the scroll view content is inset from the edges
|
||||
* of the scroll view. Defaults to `{0, 0, 0, 0}`.
|
||||
* @platform ios
|
||||
*/
|
||||
contentInset: EdgeInsetsPropType,
|
||||
/**
|
||||
* Used to manually set the starting scroll offset.
|
||||
* The default value is `{x: 0, y: 0}`.
|
||||
* @platform ios
|
||||
*/
|
||||
contentOffset: PointPropType,
|
||||
/**
|
||||
* When true, the scroll view bounces when it reaches the end of the
|
||||
* content if the content is larger then the scroll view along the axis of
|
||||
* the scroll direction. When false, it disables all bouncing even if
|
||||
* the `alwaysBounce*` props are true. The default value is true.
|
||||
* @platform ios
|
||||
*/
|
||||
bounces: PropTypes.bool,
|
||||
/**
|
||||
* When true, gestures can drive zoom past min/max and the zoom will animate
|
||||
* to the min/max value at gesture end, otherwise the zoom will not exceed
|
||||
* the limits.
|
||||
* @platform ios
|
||||
*/
|
||||
bouncesZoom: PropTypes.bool,
|
||||
/**
|
||||
* When true, the scroll view bounces horizontally when it reaches the end
|
||||
* even if the content is smaller than the scroll view itself. The default
|
||||
* value is true when `horizontal={true}` and false otherwise.
|
||||
* @platform ios
|
||||
*/
|
||||
alwaysBounceHorizontal: PropTypes.bool,
|
||||
/**
|
||||
* When true, the scroll view bounces vertically when it reaches the end
|
||||
* even if the content is smaller than the scroll view itself. The default
|
||||
* value is false when `horizontal={true}` and true otherwise.
|
||||
* @platform ios
|
||||
*/
|
||||
alwaysBounceVertical: PropTypes.bool,
|
||||
/**
|
||||
@@ -99,6 +108,7 @@ var ScrollView = React.createClass({
|
||||
* content is smaller than the scroll view bounds; when the content is
|
||||
* larger than the scroll view, this property has no effect. The default
|
||||
* value is false.
|
||||
* @platform ios
|
||||
*/
|
||||
centerContent: PropTypes.bool,
|
||||
/**
|
||||
@@ -122,6 +132,7 @@ var ScrollView = React.createClass({
|
||||
* decelerates after the user lifts their finger. Reasonable choices include
|
||||
* - Normal: 0.998 (the default)
|
||||
* - Fast: 0.9
|
||||
* @platform ios
|
||||
*/
|
||||
decelerationRate: PropTypes.number,
|
||||
/**
|
||||
@@ -132,17 +143,19 @@ var ScrollView = React.createClass({
|
||||
/**
|
||||
* When true, the ScrollView will try to lock to only vertical or horizontal
|
||||
* scrolling while dragging. The default value is false.
|
||||
* @platform ios
|
||||
*/
|
||||
directionalLockEnabled: PropTypes.bool,
|
||||
/**
|
||||
* When false, once tracking starts, won't try to drag if the touch moves.
|
||||
* The default value is true.
|
||||
* @platform ios
|
||||
*/
|
||||
canCancelContentTouches: PropTypes.bool,
|
||||
/**
|
||||
* Determines whether the keyboard gets dismissed in response to a drag.
|
||||
* - 'none' (the default), drags do not dismiss the keyboard.
|
||||
* - 'onDrag', the keyboard is dismissed when a drag begins.
|
||||
* - 'on-drag', the keyboard is dismissed when a drag begins.
|
||||
* - 'interactive', the keyboard is dismissed interactively with the drag
|
||||
* and moves in synchrony with the touch; dragging upwards cancels the
|
||||
* dismissal.
|
||||
@@ -157,35 +170,83 @@ var ScrollView = React.createClass({
|
||||
* is up dismisses the keyboard. When true, the scroll view will not catch
|
||||
* taps, and the keyboard will not dismiss automatically. The default value
|
||||
* is false.
|
||||
* @platform ios
|
||||
*/
|
||||
keyboardShouldPersistTaps: PropTypes.bool,
|
||||
/**
|
||||
* The maximum allowed zoom scale. The default value is 1.0.
|
||||
* @platform ios
|
||||
*/
|
||||
maximumZoomScale: PropTypes.number,
|
||||
/**
|
||||
* The minimum allowed zoom scale. The default value is 1.0.
|
||||
* @platform ios
|
||||
*/
|
||||
minimumZoomScale: PropTypes.number,
|
||||
/**
|
||||
* Fires at most once per frame during scrolling. The frequency of the
|
||||
* events can be contolled using the `scrollEventThrottle` prop.
|
||||
*/
|
||||
onScroll: PropTypes.func,
|
||||
/**
|
||||
* Called when a scrolling animation ends.
|
||||
* @platform ios
|
||||
*/
|
||||
onScrollAnimationEnd: PropTypes.func,
|
||||
/**
|
||||
* When true, the scroll view stops on multiples of the scroll view's size
|
||||
* when scrolling. This can be used for horizontal pagination. The default
|
||||
* value is false.
|
||||
* @platform ios
|
||||
*/
|
||||
pagingEnabled: PropTypes.bool,
|
||||
/**
|
||||
* When false, the content does not scroll.
|
||||
* The default value is true.
|
||||
* @platform ios
|
||||
*/
|
||||
scrollEnabled: PropTypes.bool,
|
||||
/**
|
||||
* This controls how often the scroll event will be fired while scrolling
|
||||
* (in events per seconds). A higher number yields better accuracy for code
|
||||
* that is tracking the scroll position, but can lead to scroll performance
|
||||
* problems due to the volume of information being send over the bridge.
|
||||
* The default value is zero, which means the scroll event will be sent
|
||||
* only once each time the view is scrolled.
|
||||
* @platform ios
|
||||
*/
|
||||
scrollEventThrottle: PropTypes.number,
|
||||
/**
|
||||
* The amount by which the scroll view indicators are inset from the edges
|
||||
* of the scroll view. This should normally be set to the same value as
|
||||
* the `contentInset`. Defaults to `{0, 0, 0, 0}`.
|
||||
* @platform ios
|
||||
*/
|
||||
scrollIndicatorInsets: EdgeInsetsPropType,
|
||||
/**
|
||||
* When true, the scroll view scrolls to top when the status bar is tapped.
|
||||
* The default value is true.
|
||||
* @platform ios
|
||||
*/
|
||||
scrollsToTop: PropTypes.bool,
|
||||
/**
|
||||
* When true, shows a horizontal scroll indicator.
|
||||
*/
|
||||
showsHorizontalScrollIndicator: PropTypes.bool,
|
||||
/**
|
||||
* When true, shows a vertical scroll indicator.
|
||||
*/
|
||||
showsVerticalScrollIndicator: PropTypes.bool,
|
||||
/**
|
||||
* An array of child indices determining which children get docked to the
|
||||
* top of the screen when scrolling. For example, passing
|
||||
* `stickyHeaderIndices={[0]}` will cause the first child to be fixed to the
|
||||
* top of the scroll view. This property is not supported in conjunction
|
||||
* with `horizontal={true}`.
|
||||
* @platform ios
|
||||
*/
|
||||
stickyHeaderIndices: PropTypes.arrayOf(PropTypes.number),
|
||||
style: StyleSheetPropType(ViewStylePropTypes),
|
||||
/**
|
||||
* Experimental: When true, offscreen child views (whose `overflow` value is
|
||||
* `hidden`) are removed from their native backing superview when offscreen.
|
||||
@@ -195,6 +256,7 @@ var ScrollView = React.createClass({
|
||||
removeClippedSubviews: PropTypes.bool,
|
||||
/**
|
||||
* The current scale of the scroll view content. The default value is 1.0.
|
||||
* @platform ios
|
||||
*/
|
||||
zoomScale: PropTypes.number,
|
||||
},
|
||||
@@ -224,19 +286,8 @@ var ScrollView = React.createClass({
|
||||
},
|
||||
|
||||
scrollTo: function(destY?: number, destX?: number) {
|
||||
if (Platform.OS === 'android') {
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
React.findNodeHandle(this),
|
||||
RCTUIManager.RCTScrollView.Commands.scrollTo,
|
||||
[destX || 0, destY || 0]
|
||||
);
|
||||
} else {
|
||||
RCTUIManager.scrollTo(
|
||||
React.findNodeHandle(this),
|
||||
destX || 0,
|
||||
destY || 0
|
||||
);
|
||||
}
|
||||
// $FlowFixMe - Don't know how to pass Mixin correctly. Postpone for now
|
||||
this.getScrollResponder().scrollResponderScrollTo(destX || 0, destY || 0);
|
||||
},
|
||||
|
||||
scrollWithoutAnimationTo: function(destY?: number, destX?: number) {
|
||||
@@ -247,6 +298,21 @@ var ScrollView = React.createClass({
|
||||
);
|
||||
},
|
||||
|
||||
handleScroll: function(e: Event) {
|
||||
if (__DEV__) {
|
||||
if (this.props.onScroll && !this.props.scrollEventThrottle) {
|
||||
console.log(
|
||||
'You specified `onScroll` on a <ScrollView> but not ' +
|
||||
'`scrollEventThrottle`. You will only receive one event. ' +
|
||||
'Using `16` you get all the events but be aware that it may ' +
|
||||
'cause frame drops, use a bigger number if you don\'t need as ' +
|
||||
'much precision.'
|
||||
);
|
||||
}
|
||||
}
|
||||
this.scrollResponderHandleScroll(e);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var contentContainerStyle = [
|
||||
this.props.horizontal && styles.contentContainerHorizontal,
|
||||
@@ -262,40 +328,14 @@ var ScrollView = React.createClass({
|
||||
') must by applied through the contentContainerStyle prop.'
|
||||
);
|
||||
}
|
||||
if (__DEV__) {
|
||||
if (this.props.onScroll && !this.props.scrollEventThrottle) {
|
||||
var onScroll = this.props.onScroll;
|
||||
this.props.onScroll = function() {
|
||||
console.log(
|
||||
'You specified `onScroll` on a <ScrollView> but not ' +
|
||||
'`scrollEventThrottle`. You will only receive one event. ' +
|
||||
'Using `16` you get all the events but be aware that it may ' +
|
||||
'cause frame drops, use a bigger number if you don\'t need as ' +
|
||||
'much precision.'
|
||||
);
|
||||
onScroll.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var children = this.props.children;
|
||||
if (this.props.stickyHeaderIndices) {
|
||||
children = ReactChildren.map(children, (child) => {
|
||||
if (child) {
|
||||
return <View collapsible={false}>{child}</View>;
|
||||
} else {
|
||||
return child;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var contentContainer =
|
||||
<View
|
||||
collapsible={false}
|
||||
ref={INNERVIEW}
|
||||
style={contentContainerStyle}
|
||||
removeClippedSubviews={this.props.removeClippedSubviews}>
|
||||
{children}
|
||||
removeClippedSubviews={this.props.removeClippedSubviews}
|
||||
collapsable={false}>
|
||||
{this.props.children}
|
||||
</View>;
|
||||
|
||||
var alwaysBounceHorizontal =
|
||||
@@ -323,7 +363,7 @@ var ScrollView = React.createClass({
|
||||
onStartShouldSetResponder: this.scrollResponderHandleStartShouldSetResponder,
|
||||
onStartShouldSetResponderCapture: this.scrollResponderHandleStartShouldSetResponderCapture,
|
||||
onScrollShouldSetResponder: this.scrollResponderHandleScrollShouldSetResponder,
|
||||
onScroll: this.scrollResponderHandleScroll,
|
||||
onScroll: this.handleScroll,
|
||||
onResponderGrant: this.scrollResponderHandleResponderGrant,
|
||||
onResponderTerminationRequest: this.scrollResponderHandleTerminationRequest,
|
||||
onResponderTerminate: this.scrollResponderHandleTerminate,
|
||||
|
||||
@@ -18,7 +18,6 @@ var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
|
||||
var requireNativeComponent = require('requireNativeComponent');
|
||||
var verifyPropTypes = require('verifyPropTypes');
|
||||
|
||||
type DefaultProps = {
|
||||
values: Array<string>;
|
||||
|
||||
@@ -35,6 +35,10 @@ var StatusBarIOS = {
|
||||
animation = animation || 'none';
|
||||
RCTStatusBarManager.setHidden(hidden, animation);
|
||||
},
|
||||
|
||||
setNetworkActivityIndicatorVisible(visible: boolean) {
|
||||
RCTStatusBarManager.setNetworkActivityIndicatorVisible(visible);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = StatusBarIOS;
|
||||
|
||||
@@ -32,7 +32,11 @@ var TabBarIOS = React.createClass({
|
||||
/**
|
||||
* Background color of the tab bar
|
||||
*/
|
||||
barTintColor: React.PropTypes.string
|
||||
barTintColor: React.PropTypes.string,
|
||||
/**
|
||||
* A Boolean value that indicates whether the tab bar is translucent
|
||||
*/
|
||||
translucent: React.PropTypes.bool,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@@ -40,7 +44,8 @@ var TabBarIOS = React.createClass({
|
||||
<RCTTabBar
|
||||
style={[styles.tabGroup, this.props.style]}
|
||||
tintColor={this.props.tintColor}
|
||||
barTintColor={this.props.barTintColor}>
|
||||
barTintColor={this.props.barTintColor}
|
||||
translucent={this.props.translucent !== false}>
|
||||
{this.props.children}
|
||||
</RCTTabBar>
|
||||
);
|
||||
|
||||
@@ -31,8 +31,8 @@ var invariant = require('invariant');
|
||||
var requireNativeComponent = require('requireNativeComponent');
|
||||
|
||||
var onlyMultiline = {
|
||||
onSelectionChange: true,
|
||||
onTextInput: true,
|
||||
onSelectionChange: true, // not supported in Open Source yet
|
||||
onTextInput: true, // not supported in Open Source yet
|
||||
children: true,
|
||||
};
|
||||
|
||||
@@ -47,6 +47,7 @@ var AndroidTextInputAttributes = {
|
||||
textAlign: true,
|
||||
textAlignVertical: true,
|
||||
keyboardType: true,
|
||||
mostRecentEventCount: true,
|
||||
multiline: true,
|
||||
password: true,
|
||||
placeholder: true,
|
||||
@@ -54,6 +55,7 @@ var AndroidTextInputAttributes = {
|
||||
text: true,
|
||||
testID: true,
|
||||
underlineColorAndroid: true,
|
||||
editable : true,
|
||||
};
|
||||
|
||||
var viewConfigAndroid = {
|
||||
@@ -64,10 +66,6 @@ var viewConfigAndroid = {
|
||||
var RCTTextView = requireNativeComponent('RCTTextView', null);
|
||||
var RCTTextField = requireNativeComponent('RCTTextField', null);
|
||||
|
||||
type DefaultProps = {
|
||||
bufferDelay: number;
|
||||
};
|
||||
|
||||
type Event = Object;
|
||||
|
||||
/**
|
||||
@@ -77,32 +75,30 @@ type Event = Object;
|
||||
* types, such as a numeric keypad.
|
||||
*
|
||||
* The simplest use case is to plop down a `TextInput` and subscribe to the
|
||||
* `onChangeText` events to read the user input. There are also other events, such
|
||||
* as `onSubmitEditing` and `onFocus` that can be subscribed to. A simple
|
||||
* `onChangeText` events to read the user input. There are also other events,
|
||||
* such as `onSubmitEditing` and `onFocus` that can be subscribed to. A simple
|
||||
* example:
|
||||
*
|
||||
* ```
|
||||
* <View>
|
||||
* <TextInput
|
||||
* style={{height: 40, borderColor: 'gray', borderWidth: 1}}
|
||||
* onChangeText={(text) => this.setState({input: text})}
|
||||
* onChangeText={(text) => this.setState({text})}
|
||||
* value={this.state.text}
|
||||
* />
|
||||
* <Text>{'user input: ' + this.state.input}</Text>
|
||||
* </View>
|
||||
* ```
|
||||
*
|
||||
* The `value` prop can be used to set the value of the input in order to make
|
||||
* the state of the component clear, but <TextInput> does not behave as a true
|
||||
* controlled component by default because all operations are asynchronous.
|
||||
* Setting `value` once is like setting the default value, but you can change it
|
||||
* continuously based on `onChangeText` events as well. If you really want to
|
||||
* force the component to always revert to the value you are setting, you can
|
||||
* set `controlled={true}`.
|
||||
* Note that some props are only available with multiline={true/false}:
|
||||
*
|
||||
* The `multiline` prop is not supported in all releases, and some props are
|
||||
* multiline only.
|
||||
* var onlyMultiline = {
|
||||
* onSelectionChange: true, // not supported in Open Source yet
|
||||
* onTextInput: true, // not supported in Open Source yet
|
||||
* children: true,
|
||||
* };
|
||||
*
|
||||
* var notMultiline = {
|
||||
* onSubmitEditing: true,
|
||||
* };
|
||||
*/
|
||||
|
||||
var TextInput = React.createClass({
|
||||
propTypes: {
|
||||
/**
|
||||
@@ -120,32 +116,43 @@ var TextInput = React.createClass({
|
||||
'characters',
|
||||
]),
|
||||
/**
|
||||
* If false, disables auto-correct. Default value is true.
|
||||
* If false, disables auto-correct. The default value is true.
|
||||
*/
|
||||
autoCorrect: PropTypes.bool,
|
||||
/**
|
||||
* If true, focuses the input on componentDidMount. Default value is false.
|
||||
* If true, focuses the input on componentDidMount.
|
||||
* The default value is false.
|
||||
*/
|
||||
autoFocus: PropTypes.bool,
|
||||
/**
|
||||
* Set the position of the cursor from where editing will begin.
|
||||
* @platorm android
|
||||
*/
|
||||
textAlign: PropTypes.oneOf([
|
||||
'start',
|
||||
'center',
|
||||
'end',
|
||||
]),
|
||||
/**
|
||||
* Aligns text vertically within the TextInput.
|
||||
* @platform android
|
||||
*/
|
||||
textAlignVertical: PropTypes.oneOf([
|
||||
'top',
|
||||
'center',
|
||||
'bottom',
|
||||
]),
|
||||
/**
|
||||
* If false, text is not editable. Default value is true.
|
||||
* If false, text is not editable. The default value is true.
|
||||
*/
|
||||
editable: PropTypes.bool,
|
||||
/**
|
||||
* Determines which keyboard to open, e.g.`numeric`.
|
||||
*
|
||||
* The following values work across platforms:
|
||||
* - default
|
||||
* - numeric
|
||||
* - email-address
|
||||
*/
|
||||
keyboardType: PropTypes.oneOf([
|
||||
// Cross-platform
|
||||
@@ -165,6 +172,7 @@ var TextInput = React.createClass({
|
||||
]),
|
||||
/**
|
||||
* Determines how the return key should look.
|
||||
* @platform ios
|
||||
*/
|
||||
returnKeyType: PropTypes.oneOf([
|
||||
'default',
|
||||
@@ -179,13 +187,21 @@ var TextInput = React.createClass({
|
||||
'done',
|
||||
'emergency-call',
|
||||
]),
|
||||
/**
|
||||
* Limits the maximum number of characters that can be entered. Use this
|
||||
* instead of implementing the logic in JS to avoid flicker.
|
||||
* @platform ios
|
||||
*/
|
||||
maxLength: PropTypes.number,
|
||||
/**
|
||||
* If true, the keyboard disables the return key when there is no text and
|
||||
* automatically enables it when there is text. Default value is false.
|
||||
* automatically enables it when there is text. The default value is false.
|
||||
* @platform ios
|
||||
*/
|
||||
enablesReturnKeyAutomatically: PropTypes.bool,
|
||||
/**
|
||||
* If true, the text input can be multiple lines. Default value is false.
|
||||
* If true, the text input can be multiple lines.
|
||||
* The default value is false.
|
||||
*/
|
||||
multiline: PropTypes.bool,
|
||||
/**
|
||||
@@ -214,14 +230,9 @@ var TextInput = React.createClass({
|
||||
*/
|
||||
onSubmitEditing: PropTypes.func,
|
||||
/**
|
||||
* Invoked on mount and layout changes with {x, y, width, height}.
|
||||
* Invoked on mount and layout changes with `{x, y, width, height}`.
|
||||
*/
|
||||
onLayout: PropTypes.func,
|
||||
/**
|
||||
* If true, the text input obscures the text entered so that sensitive text
|
||||
* like passwords stay secure. Default value is false.
|
||||
*/
|
||||
password: PropTypes.bool,
|
||||
/**
|
||||
* The string that will be rendered before text input has been entered
|
||||
*/
|
||||
@@ -230,30 +241,36 @@ var TextInput = React.createClass({
|
||||
* The text color of the placeholder string
|
||||
*/
|
||||
placeholderTextColor: PropTypes.string,
|
||||
/**
|
||||
* If true, the text input obscures the text entered so that sensitive text
|
||||
* like passwords stay secure. The default value is false.
|
||||
*/
|
||||
secureTextEntry: PropTypes.bool,
|
||||
/**
|
||||
* See DocumentSelectionState.js, some state that is responsible for
|
||||
* maintaining selection information for a document
|
||||
* @platform ios
|
||||
*/
|
||||
selectionState: PropTypes.instanceOf(DocumentSelectionState),
|
||||
/**
|
||||
* The default value for the text input
|
||||
* The value to show for the text input. TextInput is a controlled
|
||||
* component, which means the native value will be forced to match this
|
||||
* value prop if provided. For most uses this works great, but in some
|
||||
* cases this may cause flickering - one common cause is preventing edits
|
||||
* by keeping value the same. In addition to simply setting the same value,
|
||||
* either set `editable={false}`, or set/update `maxLength` to prevent
|
||||
* unwanted edits without flicker.
|
||||
*/
|
||||
value: PropTypes.string,
|
||||
/**
|
||||
* This helps avoid drops characters due to race conditions between JS and
|
||||
* the native text input. The default should be fine, but if you're
|
||||
* potentially doing very slow operations on every keystroke then you may
|
||||
* want to try increasing this.
|
||||
* Provides an initial value that will change when the user starts typing.
|
||||
* Useful for simple use-cases where you don't want to deal with listening
|
||||
* to events and updating the value prop to keep the controlled state in sync.
|
||||
*/
|
||||
bufferDelay: PropTypes.number,
|
||||
/**
|
||||
* If you really want this to behave as a controlled component, you can set
|
||||
* this true, but you will probably see flickering, dropped keystrokes,
|
||||
* and/or laggy typing, depending on how you process onChange events.
|
||||
*/
|
||||
controlled: PropTypes.bool,
|
||||
defaultValue: PropTypes.string,
|
||||
/**
|
||||
* When the clear button should appear on the right side of the text view
|
||||
* @platform ios
|
||||
*/
|
||||
clearButtonMode: PropTypes.oneOf([
|
||||
'never',
|
||||
@@ -263,10 +280,12 @@ var TextInput = React.createClass({
|
||||
]),
|
||||
/**
|
||||
* If true, clears the text field automatically when editing begins
|
||||
* @platform ios
|
||||
*/
|
||||
clearTextOnFocus: PropTypes.bool,
|
||||
/**
|
||||
* If true, selected the text automatically when editing begins
|
||||
* If true, all text will automatically be selected on focus
|
||||
* @platform ios
|
||||
*/
|
||||
selectTextOnFocus: PropTypes.bool,
|
||||
/**
|
||||
@@ -274,11 +293,12 @@ var TextInput = React.createClass({
|
||||
*/
|
||||
style: Text.propTypes.style,
|
||||
/**
|
||||
* Used to locate this view in end-to-end tests.
|
||||
* Used to locate this view in end-to-end tests
|
||||
*/
|
||||
testID: PropTypes.string,
|
||||
/**
|
||||
* The color of the textInput underline. Is only supported on Android.
|
||||
* The color of the textInput underline.
|
||||
* @platform android
|
||||
*/
|
||||
underlineColorAndroid: PropTypes.string,
|
||||
},
|
||||
@@ -297,16 +317,9 @@ var TextInput = React.createClass({
|
||||
React.findNodeHandle(this.refs.input);
|
||||
},
|
||||
|
||||
getDefaultProps: function(): DefaultProps {
|
||||
return {
|
||||
bufferDelay: 100,
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
mostRecentEventCounter: 0,
|
||||
bufferedValue: this.props.value,
|
||||
mostRecentEventCount: 0,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -346,52 +359,6 @@ var TextInput = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
_bufferTimeout: (undefined: ?number),
|
||||
|
||||
componentWillReceiveProps: function(newProps: {value: any}) {
|
||||
if (newProps.value !== this.props.value) {
|
||||
if (!this.isFocused()) {
|
||||
// Set the value immediately if the input is not focused since that
|
||||
// means there is no risk of the user typing immediately.
|
||||
this.setState({bufferedValue: newProps.value});
|
||||
} else {
|
||||
// The following clear and setTimeout buffers the value such that if more
|
||||
// characters are typed in quick succession, generating new values, the
|
||||
// out of date values will get cancelled before they are ever sent to
|
||||
// native.
|
||||
//
|
||||
// If we don't do this, it's likely the out of date values will blow
|
||||
// away recently typed characters in the native input that JS was not
|
||||
// yet aware of (since it is informed asynchronously), then the next
|
||||
// character will be appended to the older value, dropping the
|
||||
// characters in between. Here is a potential sequence of events
|
||||
// (recall we have multiple independently serial, interleaved queues):
|
||||
//
|
||||
// 1) User types 'R' => send 'R' to JS queue.
|
||||
// 2) User types 'e' => send 'Re' to JS queue.
|
||||
// 3) JS processes 'R' and sends 'R' back to native.
|
||||
// 4) Native recieves 'R' and changes input from 'Re' back to 'R'.
|
||||
// 5) User types 'a' => send 'Ra' to JS queue.
|
||||
// 6) JS processes 'Re' and sends 'Re' back to native.
|
||||
// 7) Native recieves 'Re' and changes input from 'R' back to 'Re'.
|
||||
// 8) JS processes 'Ra' and sends 'Ra' back to native.
|
||||
// 9) Native recieves final 'Ra' from JS - 'e' has been dropped!
|
||||
//
|
||||
// This isn't 100% foolproop (e.g. if it takes longer than
|
||||
// `props.bufferDelay` ms to process one keystroke), and there are of
|
||||
// course other potential algorithms to deal with this, but this is a
|
||||
// simple solution that seems to reduce the chance of dropped characters
|
||||
// drastically without compromising native input responsiveness (e.g. by
|
||||
// introducing delay from a synchronization protocol).
|
||||
this.clearTimeout(this._bufferTimeout);
|
||||
this._bufferTimeout = this.setTimeout(
|
||||
() => this.setState({bufferedValue: newProps.value}),
|
||||
this.props.bufferDelay
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getChildContext: function(): Object {
|
||||
return {isInAParentText: true};
|
||||
},
|
||||
@@ -400,6 +367,10 @@ var TextInput = React.createClass({
|
||||
isInAParentText: React.PropTypes.bool
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.setNativeProps({text: ''});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (Platform.OS === 'ios') {
|
||||
return this._renderIOS();
|
||||
@@ -408,12 +379,17 @@ var TextInput = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
_getText: function(): ?string {
|
||||
return typeof this.props.value === 'string' ?
|
||||
this.props.value :
|
||||
this.props.defaultValue;
|
||||
},
|
||||
|
||||
_renderIOS: function() {
|
||||
var textContainer;
|
||||
|
||||
var props = this.props;
|
||||
var props = Object.assign({}, this.props);
|
||||
props.style = [styles.input, this.props.style];
|
||||
|
||||
if (!props.multiline) {
|
||||
for (var propKey in onlyMultiline) {
|
||||
if (props[propKey]) {
|
||||
@@ -430,7 +406,8 @@ var TextInput = React.createClass({
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
onSelectionChangeShouldSetResponder={() => true}
|
||||
text={this.state.bufferedValue}
|
||||
text={this._getText()}
|
||||
mostRecentEventCount={this.state.mostRecentEventCount}
|
||||
/>;
|
||||
} else {
|
||||
for (var propKey in notMultiline) {
|
||||
@@ -459,14 +436,14 @@ var TextInput = React.createClass({
|
||||
ref="input"
|
||||
{...props}
|
||||
children={children}
|
||||
mostRecentEventCounter={this.state.mostRecentEventCounter}
|
||||
mostRecentEventCount={this.state.mostRecentEventCount}
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
onSelectionChange={this._onSelectionChange}
|
||||
onTextInput={this._onTextInput}
|
||||
onSelectionChangeShouldSetResponder={emptyFunction.thatReturnsTrue}
|
||||
text={this.state.bufferedValue}
|
||||
text={this._getText()}
|
||||
/>;
|
||||
}
|
||||
|
||||
@@ -505,6 +482,7 @@ var TextInput = React.createClass({
|
||||
textAlign={textAlign}
|
||||
textAlignVertical={textAlignVertical}
|
||||
keyboardType={this.props.keyboardType}
|
||||
mostRecentEventCount={this.state.mostRecentEventCount}
|
||||
multiline={this.props.multiline}
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
@@ -516,9 +494,10 @@ var TextInput = React.createClass({
|
||||
password={this.props.password || this.props.secureTextEntry}
|
||||
placeholder={this.props.placeholder}
|
||||
placeholderTextColor={this.props.placeholderTextColor}
|
||||
text={this.state.bufferedValue}
|
||||
text={this._getText()}
|
||||
underlineColorAndroid={this.props.underlineColorAndroid}
|
||||
children={children}
|
||||
editable={this.props.editable}
|
||||
/>;
|
||||
|
||||
return (
|
||||
@@ -537,15 +516,32 @@ var TextInput = React.createClass({
|
||||
},
|
||||
|
||||
_onPress: function(event: Event) {
|
||||
this.focus();
|
||||
if (this.props.editable || this.props.editable === undefined) {
|
||||
this.focus();
|
||||
}
|
||||
},
|
||||
|
||||
_onChange: function(event: Event) {
|
||||
if (this.props.controlled && event.nativeEvent.text !== this.props.value) {
|
||||
this.refs.input.setNativeProps({text: this.props.value});
|
||||
if (Platform.OS === 'android') {
|
||||
// Android expects the event count to be updated as soon as possible.
|
||||
this.refs.input.setNativeProps({
|
||||
mostRecentEventCount: event.nativeEvent.eventCount,
|
||||
});
|
||||
}
|
||||
var text = event.nativeEvent.text;
|
||||
var eventCount = event.nativeEvent.eventCount;
|
||||
this.props.onChange && this.props.onChange(event);
|
||||
this.props.onChangeText && this.props.onChangeText(event.nativeEvent.text);
|
||||
this.props.onChangeText && this.props.onChangeText(text);
|
||||
this.setState({mostRecentEventCount: eventCount}, () => {
|
||||
// This is a controlled component, so make sure to force the native value
|
||||
// to match. Most usage shouldn't need this, but if it does this will be
|
||||
// more correct but might flicker a bit and/or cause the cursor to jump.
|
||||
if (text !== this.props.value && typeof this.props.value === 'string') {
|
||||
this.refs.input.setNativeProps({
|
||||
text: this.props.value,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_onBlur: function(event: Event) {
|
||||
@@ -565,10 +561,6 @@ var TextInput = React.createClass({
|
||||
|
||||
_onTextInput: function(event: Event) {
|
||||
this.props.onTextInput && this.props.onTextInput(event);
|
||||
var counter = event.nativeEvent.eventCounter;
|
||||
if (counter > this.state.mostRecentEventCounter) {
|
||||
this.setState({mostRecentEventCounter: counter});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Platform = require('Platform');
|
||||
var RCTUIManager = require('NativeModules').UIManager;
|
||||
|
||||
var TextInputState = {
|
||||
@@ -39,7 +40,15 @@ var TextInputState = {
|
||||
focusTextInput: function(textFieldID: ?number) {
|
||||
if (this._currentlyFocusedID !== textFieldID && textFieldID !== null) {
|
||||
this._currentlyFocusedID = textFieldID;
|
||||
RCTUIManager.focus(textFieldID);
|
||||
if (Platform.OS === 'ios') {
|
||||
RCTUIManager.focus(textFieldID);
|
||||
} else if (Platform.OS === 'android') {
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
textFieldID,
|
||||
RCTUIManager.AndroidTextInput.Commands.focusTextInput,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -51,7 +60,15 @@ var TextInputState = {
|
||||
blurTextInput: function(textFieldID: ?number) {
|
||||
if (this._currentlyFocusedID === textFieldID && textFieldID !== null) {
|
||||
this._currentlyFocusedID = null;
|
||||
RCTUIManager.blur(textFieldID);
|
||||
if (Platform.OS === 'ios') {
|
||||
RCTUIManager.blur(textFieldID);
|
||||
} else if (Platform.OS === 'android') {
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
textFieldID,
|
||||
RCTUIManager.AndroidTextInput.Commands.blurTextInput,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,20 +11,12 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var AnimationExperimental = require('AnimationExperimental');
|
||||
var Animated = require('Animated');
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var POPAnimation = require('POPAnimation');
|
||||
var React = require('React');
|
||||
var Touchable = require('Touchable');
|
||||
|
||||
var merge = require('merge');
|
||||
var onlyChild = require('onlyChild');
|
||||
|
||||
var invariant = require('invariant');
|
||||
invariant(
|
||||
AnimationExperimental || POPAnimation,
|
||||
'Please add the RCTAnimationExperimental framework to your project, or add //Libraries/FBReactKit:RCTPOPAnimation to your BUCK file if running internally within Facebook.'
|
||||
);
|
||||
|
||||
type State = {
|
||||
animationID: ?number;
|
||||
@@ -58,40 +50,23 @@ var TouchableBounce = React.createClass({
|
||||
},
|
||||
|
||||
getInitialState: function(): State {
|
||||
return merge(this.touchableGetInitialState(), {animationID: null});
|
||||
return {
|
||||
...this.touchableGetInitialState(),
|
||||
scale: new Animated.Value(1),
|
||||
};
|
||||
},
|
||||
|
||||
bounceTo: function(
|
||||
value: number,
|
||||
velocity: number,
|
||||
bounciness: number,
|
||||
fromValue?: ?number,
|
||||
callback?: ?Function
|
||||
) {
|
||||
if (POPAnimation) {
|
||||
this.state.animationID && this.removeAnimation(this.state.animationID);
|
||||
var anim = {
|
||||
property: POPAnimation.Properties.scaleXY,
|
||||
dynamicsTension: 0,
|
||||
toValue: [value, value],
|
||||
velocity: [velocity, velocity],
|
||||
springBounciness: bounciness,
|
||||
fromValue: fromValue ? [fromValue, fromValue] : undefined,
|
||||
};
|
||||
this.state.animationID = POPAnimation.createSpringAnimation(anim);
|
||||
this.addAnimation(this.state.animationID, callback);
|
||||
} else {
|
||||
AnimationExperimental.startAnimation(
|
||||
{
|
||||
node: this,
|
||||
duration: 300,
|
||||
easing: 'easeOutBack',
|
||||
property: 'scaleXY',
|
||||
toValue: { x: value, y: value},
|
||||
},
|
||||
callback
|
||||
);
|
||||
}
|
||||
Animated.spring(this.state.scale, {
|
||||
toValue: value,
|
||||
velocity,
|
||||
bounciness,
|
||||
}).start(callback);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -109,13 +84,14 @@ var TouchableBounce = React.createClass({
|
||||
touchableHandlePress: function() {
|
||||
var onPressWithCompletion = this.props.onPressWithCompletion;
|
||||
if (onPressWithCompletion) {
|
||||
onPressWithCompletion(
|
||||
this.bounceTo.bind(this, 1, 10, 10, 0.93, this.props.onPressAnimationComplete)
|
||||
);
|
||||
onPressWithCompletion(() => {
|
||||
this.state.scale.setValue(0.93);
|
||||
this.bounceTo(1, 10, 10, this.props.onPressAnimationComplete);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.bounceTo(1, 10, 10, undefined, this.props.onPressAnimationComplete);
|
||||
this.bounceTo(1, 10, 10, this.props.onPressAnimationComplete);
|
||||
this.props.onPress && this.props.onPress();
|
||||
},
|
||||
|
||||
@@ -127,18 +103,21 @@ var TouchableBounce = React.createClass({
|
||||
return 0;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var child = onlyChild(this.props.children);
|
||||
return React.cloneElement(child, {
|
||||
accessible: true,
|
||||
testID: this.props.testID,
|
||||
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
||||
onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest,
|
||||
onResponderGrant: this.touchableHandleResponderGrant,
|
||||
onResponderMove: this.touchableHandleResponderMove,
|
||||
onResponderRelease: this.touchableHandleResponderRelease,
|
||||
onResponderTerminate: this.touchableHandleResponderTerminate
|
||||
});
|
||||
render: function(): ReactElement {
|
||||
return (
|
||||
<Animated.View
|
||||
style={[{transform: [{scale: this.state.scale}]}, this.props.style]}
|
||||
accessible={true}
|
||||
testID={this.props.testID}
|
||||
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
||||
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
|
||||
onResponderGrant={this.touchableHandleResponderGrant}
|
||||
onResponderMove={this.touchableHandleResponderMove}
|
||||
onResponderRelease={this.touchableHandleResponderRelease}
|
||||
onResponderTerminate={this.touchableHandleResponderTerminate}>
|
||||
{this.props.children}
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -181,6 +181,10 @@ var TouchableHighlight = React.createClass({
|
||||
},
|
||||
|
||||
_showUnderlay: function() {
|
||||
if (!this.isMounted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.refs[UNDERLAY_REF].setNativeProps(this.state.activeUnderlayProps);
|
||||
this.refs[CHILD_REF].setNativeProps(this.state.activeProps);
|
||||
this.props.onShowUnderlay && this.props.onShowUnderlay();
|
||||
@@ -202,6 +206,7 @@ var TouchableHighlight = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<View
|
||||
accessible={true}
|
||||
ref={UNDERLAY_REF}
|
||||
style={this.state.underlayStyle}
|
||||
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
||||
@@ -214,7 +219,6 @@ var TouchableHighlight = React.createClass({
|
||||
onlyChild(this.props.children),
|
||||
{
|
||||
ref: CHILD_REF,
|
||||
accessible: true,
|
||||
testID: this.props.testID,
|
||||
}
|
||||
)}
|
||||
|
||||
@@ -12,19 +12,16 @@
|
||||
|
||||
// Note (avik): add @flow when Flow supports spread properties in propTypes
|
||||
|
||||
var Animated = require('Animated');
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var POPAnimationMixin = require('POPAnimationMixin');
|
||||
var React = require('React');
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
var Touchable = require('Touchable');
|
||||
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
|
||||
|
||||
var cloneWithProps = require('cloneWithProps');
|
||||
var ensureComponentIsNative = require('ensureComponentIsNative');
|
||||
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
|
||||
var flattenStyle = require('flattenStyle');
|
||||
var keyOf = require('keyOf');
|
||||
var onlyChild = require('onlyChild');
|
||||
|
||||
/**
|
||||
* A wrapper for making views respond properly to touches.
|
||||
@@ -52,7 +49,7 @@ var onlyChild = require('onlyChild');
|
||||
*/
|
||||
|
||||
var TouchableOpacity = React.createClass({
|
||||
mixins: [TimerMixin, Touchable.Mixin, NativeMethodsMixin, POPAnimationMixin],
|
||||
mixins: [TimerMixin, Touchable.Mixin, NativeMethodsMixin],
|
||||
|
||||
propTypes: {
|
||||
...TouchableWithoutFeedback.propTypes,
|
||||
@@ -70,16 +67,17 @@ var TouchableOpacity = React.createClass({
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return this.touchableGetInitialState();
|
||||
return {
|
||||
...this.touchableGetInitialState(),
|
||||
anim: new Animated.Value(1),
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
ensurePositiveDelayProps(this.props);
|
||||
ensureComponentIsNative(this.refs[CHILD_REF]);
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
ensureComponentIsNative(this.refs[CHILD_REF]);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
@@ -87,22 +85,10 @@ var TouchableOpacity = React.createClass({
|
||||
},
|
||||
|
||||
setOpacityTo: function(value) {
|
||||
if (POPAnimationMixin) {
|
||||
// Reset with animation if POP is available
|
||||
this.stopAllAnimations();
|
||||
var anim = {
|
||||
type: this.AnimationTypes.linear,
|
||||
property: this.AnimationProperties.opacity,
|
||||
duration: 0.15,
|
||||
toValue: value,
|
||||
};
|
||||
this.startAnimation(CHILD_REF, anim);
|
||||
} else {
|
||||
// Reset immediately if POP is unavailable
|
||||
this.refs[CHILD_REF].setNativeProps({
|
||||
opacity: value
|
||||
});
|
||||
}
|
||||
Animated.timing(
|
||||
this.state.anim,
|
||||
{toValue: value, duration: 150}
|
||||
).start();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -161,25 +147,27 @@ var TouchableOpacity = React.createClass({
|
||||
_opacityInactive: function() {
|
||||
this.clearTimeout(this._hideTimeout);
|
||||
this._hideTimeout = null;
|
||||
var child = onlyChild(this.props.children);
|
||||
var childStyle = flattenStyle(child.props.style) || {};
|
||||
var childStyle = flattenStyle(this.props.style) || {};
|
||||
this.setOpacityTo(
|
||||
childStyle.opacity === undefined ? 1 : childStyle.opacity
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return cloneWithProps(onlyChild(this.props.children), {
|
||||
ref: CHILD_REF,
|
||||
accessible: true,
|
||||
testID: this.props.testID,
|
||||
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
||||
onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest,
|
||||
onResponderGrant: this.touchableHandleResponderGrant,
|
||||
onResponderMove: this.touchableHandleResponderMove,
|
||||
onResponderRelease: this.touchableHandleResponderRelease,
|
||||
onResponderTerminate: this.touchableHandleResponderTerminate,
|
||||
});
|
||||
return (
|
||||
<Animated.View
|
||||
accessible={true}
|
||||
style={[this.props.style, {opacity: this.state.anim}]}
|
||||
testID={this.props.testID}
|
||||
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
||||
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
|
||||
onResponderGrant={this.touchableHandleResponderGrant}
|
||||
onResponderMove={this.touchableHandleResponderMove}
|
||||
onResponderRelease={this.touchableHandleResponderRelease}
|
||||
onResponderTerminate={this.touchableHandleResponderTerminate}>
|
||||
{this.props.children}
|
||||
</Animated.View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -191,6 +179,5 @@ var TouchableOpacity = React.createClass({
|
||||
*/
|
||||
var PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
||||
|
||||
var CHILD_REF = keyOf({childRef: null});
|
||||
|
||||
module.exports = TouchableOpacity;
|
||||
|
||||
@@ -44,6 +44,13 @@ var AccessibilityTraits = [
|
||||
'pageTurn',
|
||||
];
|
||||
|
||||
|
||||
// <<<<< WARNING >>>>>
|
||||
// If adding any properties to View that could change the way layout-only status
|
||||
// works on iOS, make sure to update ReactNativeViewAttributes.js and
|
||||
// RCTShadowView.m (in the -[RCTShadowView isLayoutOnly] method).
|
||||
// <<<<< WARNING >>>>>
|
||||
|
||||
/**
|
||||
* The most fundamental component for building UI, `View` is a
|
||||
* container that supports layout with flexbox, style, some touch handling, and
|
||||
@@ -77,12 +84,6 @@ var View = React.createClass({
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
/**
|
||||
* When false, indicates that the view should not be collapsed, even if it is
|
||||
* layout-only. Defaults to true.
|
||||
*/
|
||||
collapsible: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* When true, indicates that the view is an accessibility element. By default,
|
||||
* all the touchable elements are accessible.
|
||||
@@ -96,6 +97,27 @@ var View = React.createClass({
|
||||
*/
|
||||
accessibilityLabel: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Indicates to accessibility services to treat UI component like a
|
||||
* native one. Works for Android only.
|
||||
*/
|
||||
accessibilityComponentType: PropTypes.oneOf([
|
||||
'none',
|
||||
'button',
|
||||
]),
|
||||
|
||||
/**
|
||||
* Indicates to accessibility services whether the user should be notified
|
||||
* when this view changes. Works for Android API >= 19 only.
|
||||
* See http://developer.android.com/reference/android/view/View.html#attr_android:accessibilityLiveRegion
|
||||
* for references.
|
||||
*/
|
||||
accessibilityLiveRegion: PropTypes.oneOf([
|
||||
'none',
|
||||
'polite',
|
||||
'assertive',
|
||||
]),
|
||||
|
||||
/**
|
||||
* Provides additional traits to screen reader. By default no traits are
|
||||
* provided unless specified otherwise in element
|
||||
@@ -118,7 +140,8 @@ var View = React.createClass({
|
||||
onMagicTap: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Used to locate this view in end-to-end tests.
|
||||
* Used to locate this view in end-to-end tests. NB: disables the 'layout-only
|
||||
* view removal' optimization for this view!
|
||||
*/
|
||||
testID: PropTypes.string,
|
||||
|
||||
@@ -203,8 +226,29 @@ var View = React.createClass({
|
||||
* different parameters. The downside is that this can use up limited video
|
||||
* memory, so this prop should be set back to false at the end of the
|
||||
* interaction/animation.
|
||||
* @platform android
|
||||
*/
|
||||
renderToHardwareTextureAndroid: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Whether this view should be rendered as a bitmap before compositing.
|
||||
*
|
||||
* On iOS, this is useful for animations and interactions that do not
|
||||
* modify this component's dimensions nor its children; for example, when
|
||||
* translating the position of a static view, rasterization allows the
|
||||
* renderer to reuse a cached bitmap of a static view and quickly composite
|
||||
* it during each frame.
|
||||
*
|
||||
* Rasterization incurs an off-screen drawing pass and the bitmap consumes
|
||||
* memory. Test and measure when using this property.
|
||||
* @platform ios
|
||||
*/
|
||||
shouldRasterizeIOS: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* @platform android
|
||||
*/
|
||||
collapsable: PropTypes.bool,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
||||
@@ -21,6 +21,7 @@ var TransformPropTypes = require('TransformPropTypes');
|
||||
var ViewStylePropTypes = {
|
||||
...LayoutPropTypes,
|
||||
...TransformPropTypes,
|
||||
backfaceVisibility: ReactPropTypes.oneOf(['visible', 'hidden']),
|
||||
backgroundColor: ReactPropTypes.string,
|
||||
borderColor: ReactPropTypes.string,
|
||||
borderTopColor: ReactPropTypes.string,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user