commit
62b90cfcc5
|
@ -15,6 +15,12 @@
|
||||||
.*/node_modules/react-tools/src/core/ReactInstanceHandles.js
|
.*/node_modules/react-tools/src/core/ReactInstanceHandles.js
|
||||||
.*/node_modules/react-tools/src/event/EventPropagators.js
|
.*/node_modules/react-tools/src/event/EventPropagators.js
|
||||||
|
|
||||||
|
# Ignore commoner tests
|
||||||
|
.*/node_modules/react-tools/node_modules/commoner/test/.*
|
||||||
|
|
||||||
|
# See https://github.com/facebook/flow/issues/442
|
||||||
|
.*/react-tools/node_modules/commoner/lib/reader.js
|
||||||
|
|
||||||
# Ignore jest
|
# Ignore jest
|
||||||
.*/react-native/node_modules/jest-cli/.*
|
.*/react-native/node_modules/jest-cli/.*
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,6 @@ var TimerMixin = require('react-timer-mixin');
|
||||||
var MovieCell = require('./MovieCell');
|
var MovieCell = require('./MovieCell');
|
||||||
var MovieScreen = require('./MovieScreen');
|
var MovieScreen = require('./MovieScreen');
|
||||||
|
|
||||||
var fetch = require('fetch');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is for demo purposes only, and rate limited.
|
* This is for demo purposes only, and rate limited.
|
||||||
* In case you want to use the Rotten Tomatoes' API on a real app you should
|
* In case you want to use the Rotten Tomatoes' API on a real app you should
|
||||||
|
|
|
@ -15,6 +15,12 @@
|
||||||
.*/node_modules/react-tools/src/core/ReactInstanceHandles.js
|
.*/node_modules/react-tools/src/core/ReactInstanceHandles.js
|
||||||
.*/node_modules/react-tools/src/event/EventPropagators.js
|
.*/node_modules/react-tools/src/event/EventPropagators.js
|
||||||
|
|
||||||
|
# Ignore commoner tests
|
||||||
|
.*/node_modules/react-tools/node_modules/commoner/test/.*
|
||||||
|
|
||||||
|
# See https://github.com/facebook/flow/issues/442
|
||||||
|
.*/react-tools/node_modules/commoner/lib/reader.js
|
||||||
|
|
||||||
# Ignore jest
|
# Ignore jest
|
||||||
.*/react-native/node_modules/jest-cli/.*
|
.*/react-native/node_modules/jest-cli/.*
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<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">
|
<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">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6736"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
|
||||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
|
|
|
@ -39,3 +39,8 @@ declare module 'image!uie_thumb_selected' {
|
||||||
declare var uri: string;
|
declare var uri: string;
|
||||||
declare var isStatic: boolean;
|
declare var isStatic: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module 'image!NavBarButtonPlus' {
|
||||||
|
declare var uri: string;
|
||||||
|
declare var isStatic: boolean;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
LayoutAnimation,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
} = React;
|
||||||
|
|
||||||
|
type LayoutEvent = {
|
||||||
|
nativeEvent: {
|
||||||
|
layout: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var LayoutEventExample = React.createClass({
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
viewStyle: {
|
||||||
|
margin: 20,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
animateViewLayout: function() {
|
||||||
|
LayoutAnimation.configureNext(
|
||||||
|
LayoutAnimation.Presets.spring,
|
||||||
|
() => {
|
||||||
|
console.log('layout animation done.');
|
||||||
|
this.addWrapText();
|
||||||
|
},
|
||||||
|
(error) => { throw new Error(JSON.stringify(error)); }
|
||||||
|
);
|
||||||
|
this.setState({
|
||||||
|
viewStyle: {
|
||||||
|
margin: this.state.viewStyle.margin > 20 ? 20 : 60,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addWrapText: function() {
|
||||||
|
this.setState(
|
||||||
|
{extraText: ' And a bunch more text to wrap around a few lines.'},
|
||||||
|
this.changeContainer
|
||||||
|
);
|
||||||
|
},
|
||||||
|
changeContainer: function() {
|
||||||
|
this.setState({containerStyle: {width: 280}});
|
||||||
|
},
|
||||||
|
onViewLayout: function(e: LayoutEvent) {
|
||||||
|
console.log('received view layout event\n', e.nativeEvent);
|
||||||
|
this.setState({viewLayout: e.nativeEvent.layout});
|
||||||
|
},
|
||||||
|
onTextLayout: function(e: LayoutEvent) {
|
||||||
|
console.log('received text layout event\n', e.nativeEvent);
|
||||||
|
this.setState({textLayout: e.nativeEvent.layout});
|
||||||
|
},
|
||||||
|
onImageLayout: function(e: LayoutEvent) {
|
||||||
|
console.log('received image layout event\n', e.nativeEvent);
|
||||||
|
this.setState({imageLayout: e.nativeEvent.layout});
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
var viewStyle = [styles.view, this.state.viewStyle];
|
||||||
|
var textLayout = this.state.textLayout || {width: '?', height: '?'};
|
||||||
|
var imageLayout = this.state.imageLayout || {x: '?', y: '?'};
|
||||||
|
return (
|
||||||
|
<View style={this.state.containerStyle}>
|
||||||
|
<Text>
|
||||||
|
onLayout events are called on mount and whenever layout is updated,
|
||||||
|
including after layout animations complete.{' '}
|
||||||
|
<Text style={styles.pressText} onPress={this.animateViewLayout}>
|
||||||
|
Press here to change layout.
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
<View ref="view" onLayout={this.onViewLayout} style={viewStyle}>
|
||||||
|
<Image
|
||||||
|
ref="img"
|
||||||
|
onLayout={this.onImageLayout}
|
||||||
|
style={styles.image}
|
||||||
|
source={{uri: 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851561_767334496626293_1958532586_n.png'}}
|
||||||
|
/>
|
||||||
|
<Text>
|
||||||
|
ViewLayout: {JSON.stringify(this.state.viewLayout, null, ' ') + '\n\n'}
|
||||||
|
</Text>
|
||||||
|
<Text ref="txt" onLayout={this.onTextLayout} style={styles.text}>
|
||||||
|
A simple piece of text.{this.state.extraText}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
{'\n'}
|
||||||
|
Text w/h: {textLayout.width}/{textLayout.height + '\n'}
|
||||||
|
Image x/y: {imageLayout.x}/{imageLayout.y}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var styles = StyleSheet.create({
|
||||||
|
view: {
|
||||||
|
padding: 12,
|
||||||
|
borderColor: 'black',
|
||||||
|
borderWidth: 0.5,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
alignSelf: 'flex-start',
|
||||||
|
borderColor: 'rgba(0, 0, 255, 0.2)',
|
||||||
|
borderWidth: 0.5,
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
marginBottom: 10,
|
||||||
|
alignSelf: 'center',
|
||||||
|
},
|
||||||
|
pressText: {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.title = 'onLayout';
|
||||||
|
exports.description = 'Layout events can be used to measure view size and position.';
|
||||||
|
exports.examples = [
|
||||||
|
{
|
||||||
|
title: 'onLayout',
|
||||||
|
render: function(): ReactElement {
|
||||||
|
return <LayoutEventExample />;
|
||||||
|
},
|
||||||
|
}];
|
|
@ -19,6 +19,7 @@ var React = require('react-native');
|
||||||
var ViewExample = require('./ViewExample');
|
var ViewExample = require('./ViewExample');
|
||||||
var createExamplePage = require('./createExamplePage');
|
var createExamplePage = require('./createExamplePage');
|
||||||
var {
|
var {
|
||||||
|
AlertIOS,
|
||||||
PixelRatio,
|
PixelRatio,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
|
@ -92,6 +93,30 @@ var NavigatorIOSExample = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})}
|
})}
|
||||||
|
{this._renderRow('Custom Left & Right Icons', () => {
|
||||||
|
this.props.navigator.push({
|
||||||
|
title: NavigatorIOSExample.title,
|
||||||
|
component: EmptyPage,
|
||||||
|
leftButtonTitle: 'Custom Left',
|
||||||
|
onLeftButtonPress: () => this.props.navigator.pop(),
|
||||||
|
rightButtonIcon: require('image!NavBarButtonPlus'),
|
||||||
|
onRightButtonPress: () => {
|
||||||
|
AlertIOS.alert(
|
||||||
|
'Bar Button Action',
|
||||||
|
'Recognized a tap on the bar button icon',
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: 'OK',
|
||||||
|
onPress: () => console.log('Tapped OK'),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
passProps: {
|
||||||
|
text: 'This page has an icon for the right button in the nav bar',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})}
|
||||||
{this._renderRow('Pop', () => {
|
{this._renderRow('Pop', () => {
|
||||||
this.props.navigator.pop();
|
this.props.navigator.pop();
|
||||||
})}
|
})}
|
||||||
|
|
21
Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/Contents.json
vendored
Normal file
21
Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "NavBarButtonPlus@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
BIN
Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/NavBarButtonPlus@3x.png
vendored
Normal file
BIN
Examples/UIExplorer/UIExplorer/Images.xcassets/NavBarButtonPlus.imageset/NavBarButtonPlus@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 166 B |
|
@ -64,6 +64,7 @@ var APIS = [
|
||||||
require('./BorderExample'),
|
require('./BorderExample'),
|
||||||
require('./CameraRollExample.ios'),
|
require('./CameraRollExample.ios'),
|
||||||
require('./GeolocationExample'),
|
require('./GeolocationExample'),
|
||||||
|
require('./LayoutEventsExample'),
|
||||||
require('./LayoutExample'),
|
require('./LayoutExample'),
|
||||||
require('./NetInfoExample'),
|
require('./NetInfoExample'),
|
||||||
require('./PanResponderExample'),
|
require('./PanResponderExample'),
|
||||||
|
|
|
@ -25,6 +25,7 @@ var TESTS = [
|
||||||
require('./IntegrationTestHarnessTest'),
|
require('./IntegrationTestHarnessTest'),
|
||||||
require('./TimersTest'),
|
require('./TimersTest'),
|
||||||
require('./AsyncStorageTest'),
|
require('./AsyncStorageTest'),
|
||||||
|
require('./LayoutEventsTest'),
|
||||||
require('./SimpleSnapshotTest'),
|
require('./SimpleSnapshotTest'),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,11 @@
|
||||||
[_runner runTest:_cmd module:@"AsyncStorageTest"];
|
[_runner runTest:_cmd module:@"AsyncStorageTest"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)testLayoutEvents
|
||||||
|
{
|
||||||
|
[_runner runTest:_cmd module:@"LayoutEventsTest"];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark Snapshot Tests
|
#pragma mark Snapshot Tests
|
||||||
|
|
||||||
- (void)testSimpleSnapshot
|
- (void)testSimpleSnapshot
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
/**
|
||||||
|
* 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 LayoutEventsTest
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react-native');
|
||||||
|
var {
|
||||||
|
Image,
|
||||||
|
LayoutAnimation,
|
||||||
|
NativeModules,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
} = React;
|
||||||
|
var TestModule = NativeModules.TestModule || NativeModules.SnapshotTestManager;
|
||||||
|
|
||||||
|
var deepDiffer = require('deepDiffer');
|
||||||
|
|
||||||
|
function debug() {
|
||||||
|
//console.log.apply(null, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
type LayoutEvent = {
|
||||||
|
nativeEvent: {
|
||||||
|
layout: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var LayoutEventsTest = React.createClass({
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
didAnimation: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
animateViewLayout: function() {
|
||||||
|
LayoutAnimation.configureNext(
|
||||||
|
LayoutAnimation.Presets.spring,
|
||||||
|
() => {
|
||||||
|
debug('layout animation done.');
|
||||||
|
this.checkLayout(this.addWrapText);
|
||||||
|
},
|
||||||
|
(error) => { throw new Error(JSON.stringify(error)); }
|
||||||
|
);
|
||||||
|
this.setState({viewStyle: {margin: 60}});
|
||||||
|
},
|
||||||
|
addWrapText: function() {
|
||||||
|
this.setState(
|
||||||
|
{extraText: ' And a bunch more text to wrap around a few lines.'},
|
||||||
|
() => this.checkLayout(this.changeContainer)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
changeContainer: function() {
|
||||||
|
this.setState(
|
||||||
|
{containerStyle: {width: 280}},
|
||||||
|
() => this.checkLayout(TestModule.markTestCompleted)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
checkLayout: function(next?: ?Function) {
|
||||||
|
if (!this.isMounted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.refs.view.measure((x, y, width, height) => {
|
||||||
|
this.compare('view', {x, y, width, height}, this.state.viewLayout);
|
||||||
|
if (typeof next === 'function') {
|
||||||
|
next();
|
||||||
|
} else if (!this.state.didAnimation) {
|
||||||
|
// Trigger first state change after onLayout fires
|
||||||
|
this.animateViewLayout();
|
||||||
|
this.state.didAnimation = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.refs.txt.measure((x, y, width, height) => {
|
||||||
|
this.compare('txt', {x, y, width, height}, this.state.textLayout);
|
||||||
|
});
|
||||||
|
this.refs.img.measure((x, y, width, height) => {
|
||||||
|
this.compare('img', {x, y, width, height}, this.state.imageLayout);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
compare: function(node: string, measured: any, onLayout: any): void {
|
||||||
|
if (deepDiffer(measured, onLayout)) {
|
||||||
|
var data = {measured, onLayout};
|
||||||
|
throw new Error(
|
||||||
|
node + ' onLayout mismatch with measure ' +
|
||||||
|
JSON.stringify(data, null, ' ')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onViewLayout: function(e: LayoutEvent) {
|
||||||
|
debug('received view layout event\n', e.nativeEvent);
|
||||||
|
this.setState({viewLayout: e.nativeEvent.layout}, this.checkLayout);
|
||||||
|
},
|
||||||
|
onTextLayout: function(e: LayoutEvent) {
|
||||||
|
debug('received text layout event\n', e.nativeEvent);
|
||||||
|
this.setState({textLayout: e.nativeEvent.layout}, this.checkLayout);
|
||||||
|
},
|
||||||
|
onImageLayout: function(e: LayoutEvent) {
|
||||||
|
debug('received image layout event\n', e.nativeEvent);
|
||||||
|
this.setState({imageLayout: e.nativeEvent.layout}, this.checkLayout);
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
var viewStyle = [styles.view, this.state.viewStyle];
|
||||||
|
var textLayout = this.state.textLayout || {width: '?', height: '?'};
|
||||||
|
var imageLayout = this.state.imageLayout || {x: '?', y: '?'};
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, this.state.containerStyle]}>
|
||||||
|
<View ref="view" onLayout={this.onViewLayout} style={viewStyle}>
|
||||||
|
<Image
|
||||||
|
ref="img"
|
||||||
|
onLayout={this.onImageLayout}
|
||||||
|
style={styles.image}
|
||||||
|
source={{uri: 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851561_767334496626293_1958532586_n.png'}}
|
||||||
|
/>
|
||||||
|
<Text>
|
||||||
|
ViewLayout: {JSON.stringify(this.state.viewLayout, null, ' ') + '\n\n'}
|
||||||
|
</Text>
|
||||||
|
<Text ref="txt" onLayout={this.onTextLayout} style={styles.text}>
|
||||||
|
A simple piece of text.{this.state.extraText}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
{'\n'}
|
||||||
|
Text w/h: {textLayout.width}/{textLayout.height + '\n'}
|
||||||
|
Image x/y: {imageLayout.x}/{imageLayout.y}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
margin: 40,
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
margin: 20,
|
||||||
|
padding: 12,
|
||||||
|
borderColor: 'black',
|
||||||
|
borderWidth: 0.5,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
alignSelf: 'flex-start',
|
||||||
|
borderColor: 'rgba(0, 0, 255, 0.2)',
|
||||||
|
borderWidth: 0.5,
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
marginBottom: 10,
|
||||||
|
alignSelf: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = LayoutEventsTest;
|
|
@ -12,6 +12,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var EventEmitter = require('EventEmitter');
|
var EventEmitter = require('EventEmitter');
|
||||||
|
var Image = require('Image');
|
||||||
var React = require('React');
|
var React = require('React');
|
||||||
var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
|
var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
|
||||||
var RCTNavigatorManager = require('NativeModules').NavigatorManager;
|
var RCTNavigatorManager = require('NativeModules').NavigatorManager;
|
||||||
|
@ -47,11 +48,16 @@ var RCTNavigatorItem = createReactIOSNativeComponentClass({
|
||||||
// NavigatorIOS does not use them all, because some are problematic
|
// NavigatorIOS does not use them all, because some are problematic
|
||||||
title: true,
|
title: true,
|
||||||
barTintColor: true,
|
barTintColor: true,
|
||||||
|
leftButtonIcon: true,
|
||||||
|
leftButtonTitle: true,
|
||||||
|
onNavLeftButtonTap: true,
|
||||||
|
rightButtonIcon: true,
|
||||||
rightButtonTitle: true,
|
rightButtonTitle: true,
|
||||||
onNavRightButtonTap: true,
|
onNavRightButtonTap: true,
|
||||||
|
backButtonIcon: true,
|
||||||
|
backButtonTitle: true,
|
||||||
tintColor: true,
|
tintColor: true,
|
||||||
navigationBarHidden: true,
|
navigationBarHidden: true,
|
||||||
backButtonTitle: true,
|
|
||||||
titleTextColor: true,
|
titleTextColor: true,
|
||||||
style: true,
|
style: true,
|
||||||
},
|
},
|
||||||
|
@ -79,7 +85,12 @@ type Route = {
|
||||||
title: string;
|
title: string;
|
||||||
passProps: Object;
|
passProps: Object;
|
||||||
backButtonTitle: string;
|
backButtonTitle: string;
|
||||||
|
backButtonIcon: Object;
|
||||||
|
leftButtonTitle: string;
|
||||||
|
leftButtonIcon: Object;
|
||||||
|
onLeftButtonPress: Function;
|
||||||
rightButtonTitle: string;
|
rightButtonTitle: string;
|
||||||
|
rightButtonIcon: Object;
|
||||||
onRightButtonPress: Function;
|
onRightButtonPress: Function;
|
||||||
wrapperStyle: any;
|
wrapperStyle: any;
|
||||||
};
|
};
|
||||||
|
@ -212,6 +223,13 @@ var NavigatorIOS = React.createClass({
|
||||||
*/
|
*/
|
||||||
passProps: PropTypes.object,
|
passProps: PropTypes.object,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, the left header button image will appear with this source. Note
|
||||||
|
* that this doesn't apply for the header of the current view, but the
|
||||||
|
* ones of the views that are pushed afterward.
|
||||||
|
*/
|
||||||
|
backButtonIcon: Image.propTypes.source,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set, the left header button will appear with this name. Note that
|
* If set, the left header button will appear with this name. Note that
|
||||||
* this doesn't apply for the header of the current view, but the ones
|
* this doesn't apply for the header of the current view, but the ones
|
||||||
|
@ -219,6 +237,26 @@ var NavigatorIOS = React.createClass({
|
||||||
*/
|
*/
|
||||||
backButtonTitle: PropTypes.string,
|
backButtonTitle: PropTypes.string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, the left header button image will appear with this source
|
||||||
|
*/
|
||||||
|
leftButtonIcon: Image.propTypes.source,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, the left header button will appear with this name
|
||||||
|
*/
|
||||||
|
leftButtonTitle: PropTypes.string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the left header button is pressed
|
||||||
|
*/
|
||||||
|
onLeftButtonPress: PropTypes.func,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, the right header button image will appear with this source
|
||||||
|
*/
|
||||||
|
rightButtonIcon: Image.propTypes.source,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set, the right header button will appear with this name
|
* If set, the right header button will appear with this name
|
||||||
*/
|
*/
|
||||||
|
@ -560,7 +598,12 @@ var NavigatorIOS = React.createClass({
|
||||||
this.props.itemWrapperStyle,
|
this.props.itemWrapperStyle,
|
||||||
route.wrapperStyle
|
route.wrapperStyle
|
||||||
]}
|
]}
|
||||||
|
backButtonIcon={this._imageNameFromSource(route.backButtonIcon)}
|
||||||
backButtonTitle={route.backButtonTitle}
|
backButtonTitle={route.backButtonTitle}
|
||||||
|
leftButtonIcon={this._imageNameFromSource(route.leftButtonIcon)}
|
||||||
|
leftButtonTitle={route.leftButtonTitle}
|
||||||
|
onNavLeftButtonTap={route.onLeftButtonPress}
|
||||||
|
rightButtonIcon={this._imageNameFromSource(route.rightButtonIcon)}
|
||||||
rightButtonTitle={route.rightButtonTitle}
|
rightButtonTitle={route.rightButtonTitle}
|
||||||
onNavRightButtonTap={route.onRightButtonPress}
|
onNavRightButtonTap={route.onRightButtonPress}
|
||||||
navigationBarHidden={this.props.navigationBarHidden}
|
navigationBarHidden={this.props.navigationBarHidden}
|
||||||
|
@ -577,6 +620,10 @@ var NavigatorIOS = React.createClass({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_imageNameFromSource: function(source: ?Object) {
|
||||||
|
return source ? source.uri : undefined;
|
||||||
|
},
|
||||||
|
|
||||||
renderNavigationStackItems: function() {
|
renderNavigationStackItems: function() {
|
||||||
var shouldRecurseToNavigator =
|
var shouldRecurseToNavigator =
|
||||||
this.state.makingNavigatorRequest ||
|
this.state.makingNavigatorRequest ||
|
||||||
|
|
|
@ -20,6 +20,7 @@ var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
|
||||||
|
|
||||||
var cloneWithProps = require('cloneWithProps');
|
var cloneWithProps = require('cloneWithProps');
|
||||||
var ensureComponentIsNative = require('ensureComponentIsNative');
|
var ensureComponentIsNative = require('ensureComponentIsNative');
|
||||||
|
var flattenStyle = require('flattenStyle');
|
||||||
var keyOf = require('keyOf');
|
var keyOf = require('keyOf');
|
||||||
var onlyChild = require('onlyChild');
|
var onlyChild = require('onlyChild');
|
||||||
|
|
||||||
|
@ -105,12 +106,13 @@ var TouchableOpacity = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
touchableHandleActivePressOut: function() {
|
touchableHandleActivePressOut: function() {
|
||||||
this.setOpacityTo(1.0);
|
var child = onlyChild(this.props.children);
|
||||||
|
var childStyle = flattenStyle(child.props.style) || {};
|
||||||
|
this.setOpacityTo(childStyle.opacity === undefined ? 1 : childStyle.opacity);
|
||||||
this.props.onPressOut && this.props.onPressOut();
|
this.props.onPressOut && this.props.onPressOut();
|
||||||
},
|
},
|
||||||
|
|
||||||
touchableHandlePress: function() {
|
touchableHandlePress: function() {
|
||||||
this.setOpacityTo(1.0);
|
|
||||||
this.props.onPress && this.props.onPress();
|
this.props.onPress && this.props.onPress();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,11 @@ var View = React.createClass({
|
||||||
onStartShouldSetResponder: PropTypes.func,
|
onStartShouldSetResponder: PropTypes.func,
|
||||||
onStartShouldSetResponderCapture: PropTypes.func,
|
onStartShouldSetResponderCapture: PropTypes.func,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked on mount and layout changes with {x, y, width, height}.
|
||||||
|
*/
|
||||||
|
onLayout: PropTypes.func,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In the absence of `auto` property, `none` is much like `CSS`'s `none`
|
* In the absence of `auto` property, `none` is much like `CSS`'s `none`
|
||||||
* value. `box-none` is as if you had applied the `CSS` class:
|
* value. `box-none` is as if you had applied the `CSS` class:
|
||||||
|
|
|
@ -47,6 +47,23 @@ var self = {};
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeName(name) {
|
||||||
|
if (typeof name !== 'string') {
|
||||||
|
name = name.toString();
|
||||||
|
}
|
||||||
|
if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) {
|
||||||
|
throw new TypeError('Invalid character in header field name')
|
||||||
|
}
|
||||||
|
return name.toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeValue(value) {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
value = value.toString();
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
function Headers(headers) {
|
function Headers(headers) {
|
||||||
this.map = {}
|
this.map = {}
|
||||||
|
|
||||||
|
@ -66,7 +83,8 @@ var self = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Headers.prototype.append = function(name, value) {
|
Headers.prototype.append = function(name, value) {
|
||||||
name = name.toLowerCase()
|
name = normalizeName(name)
|
||||||
|
value = normalizeValue(value)
|
||||||
var list = this.map[name]
|
var list = this.map[name]
|
||||||
if (!list) {
|
if (!list) {
|
||||||
list = []
|
list = []
|
||||||
|
@ -76,24 +94,24 @@ var self = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Headers.prototype['delete'] = function(name) {
|
Headers.prototype['delete'] = function(name) {
|
||||||
delete this.map[name.toLowerCase()]
|
delete this.map[normalizeName(name)]
|
||||||
}
|
}
|
||||||
|
|
||||||
Headers.prototype.get = function(name) {
|
Headers.prototype.get = function(name) {
|
||||||
var values = this.map[name.toLowerCase()]
|
var values = this.map[normalizeName(name)]
|
||||||
return values ? values[0] : null
|
return values ? values[0] : null
|
||||||
}
|
}
|
||||||
|
|
||||||
Headers.prototype.getAll = function(name) {
|
Headers.prototype.getAll = function(name) {
|
||||||
return this.map[name.toLowerCase()] || []
|
return this.map[normalizeName(name)] || []
|
||||||
}
|
}
|
||||||
|
|
||||||
Headers.prototype.has = function(name) {
|
Headers.prototype.has = function(name) {
|
||||||
return this.map.hasOwnProperty(name.toLowerCase())
|
return this.map.hasOwnProperty(normalizeName(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
Headers.prototype.set = function(name, value) {
|
Headers.prototype.set = function(name, value) {
|
||||||
this.map[name.toLowerCase()] = [value]
|
this.map[normalizeName(name)] = [normalizeValue(value)]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instead of iterable for now.
|
// Instead of iterable for now.
|
||||||
|
@ -134,22 +152,51 @@ var self = {};
|
||||||
return fileReaderReady(reader)
|
return fileReaderReady(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
var blobSupport = 'FileReader' in self && 'Blob' in self && (function() {
|
var support = {
|
||||||
try {
|
blob: 'FileReader' in self && 'Blob' in self && (function() {
|
||||||
new Blob();
|
try {
|
||||||
return true
|
new Blob();
|
||||||
} catch(e) {
|
return true
|
||||||
return false
|
} catch(e) {
|
||||||
}
|
return false
|
||||||
})();
|
}
|
||||||
|
})(),
|
||||||
|
formData: 'FormData' in self
|
||||||
|
}
|
||||||
|
|
||||||
function Body() {
|
function Body() {
|
||||||
this.bodyUsed = false
|
this.bodyUsed = false
|
||||||
|
|
||||||
if (blobSupport) {
|
|
||||||
|
this._initBody = function(body) {
|
||||||
|
this._bodyInit = body
|
||||||
|
if (typeof body === 'string') {
|
||||||
|
this._bodyText = body
|
||||||
|
} else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
|
||||||
|
this._bodyBlob = body
|
||||||
|
} else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
|
||||||
|
this._bodyFormData = body
|
||||||
|
} else if (!body) {
|
||||||
|
this._bodyText = ''
|
||||||
|
} else {
|
||||||
|
throw new Error('unsupported BodyInit type')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (support.blob) {
|
||||||
this.blob = function() {
|
this.blob = function() {
|
||||||
var rejected = consumed(this)
|
var rejected = consumed(this)
|
||||||
return rejected ? rejected : Promise.resolve(this._bodyBlob)
|
if (rejected) {
|
||||||
|
return rejected
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._bodyBlob) {
|
||||||
|
return Promise.resolve(this._bodyBlob)
|
||||||
|
} else if (this._bodyFormData) {
|
||||||
|
throw new Error('could not read FormData body as blob')
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(new Blob([this._bodyText]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.arrayBuffer = function() {
|
this.arrayBuffer = function() {
|
||||||
|
@ -157,7 +204,18 @@ var self = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.text = function() {
|
this.text = function() {
|
||||||
return this.blob().then(readBlobAsText)
|
var rejected = consumed(this)
|
||||||
|
if (rejected) {
|
||||||
|
return rejected
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._bodyBlob) {
|
||||||
|
return readBlobAsText(this._bodyBlob)
|
||||||
|
} else if (this._bodyFormData) {
|
||||||
|
throw new Error('could not read FormData body as text')
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(this._bodyText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.text = function() {
|
this.text = function() {
|
||||||
|
@ -166,7 +224,7 @@ var self = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('FormData' in self) {
|
if (support.formData) {
|
||||||
this.formData = function() {
|
this.formData = function() {
|
||||||
return this.text().then(decode)
|
return this.text().then(decode)
|
||||||
}
|
}
|
||||||
|
@ -190,12 +248,17 @@ var self = {};
|
||||||
function Request(url, options) {
|
function Request(url, options) {
|
||||||
options = options || {}
|
options = options || {}
|
||||||
this.url = url
|
this.url = url
|
||||||
this._body = options.body
|
|
||||||
this.credentials = options.credentials || 'omit'
|
this.credentials = options.credentials || 'omit'
|
||||||
this.headers = new Headers(options.headers)
|
this.headers = new Headers(options.headers)
|
||||||
this.method = normalizeMethod(options.method || 'GET')
|
this.method = normalizeMethod(options.method || 'GET')
|
||||||
this.mode = options.mode || null
|
this.mode = options.mode || null
|
||||||
this.referrer = null
|
this.referrer = null
|
||||||
|
|
||||||
|
if ((this.method === 'GET' || this.method === 'HEAD') && options.body) {
|
||||||
|
throw new TypeError('Body not allowed for GET or HEAD requests')
|
||||||
|
}
|
||||||
|
this._initBody(options.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
function decode(body) {
|
function decode(body) {
|
||||||
|
@ -223,11 +286,43 @@ var self = {};
|
||||||
return head
|
return head
|
||||||
}
|
}
|
||||||
|
|
||||||
Request.prototype.fetch = function() {
|
Body.call(Request.prototype)
|
||||||
var self = this
|
|
||||||
|
function Response(bodyInit, options) {
|
||||||
|
if (!options) {
|
||||||
|
options = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._initBody(bodyInit)
|
||||||
|
this.type = 'default'
|
||||||
|
this.url = null
|
||||||
|
this.status = options.status
|
||||||
|
this.ok = this.status >= 200 && this.status < 300
|
||||||
|
this.statusText = options.statusText
|
||||||
|
this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers)
|
||||||
|
this.url = options.url || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
Body.call(Response.prototype)
|
||||||
|
|
||||||
|
self.Headers = Headers;
|
||||||
|
self.Request = Request;
|
||||||
|
self.Response = Response;
|
||||||
|
|
||||||
|
self.fetch = function(input, init) {
|
||||||
|
// TODO: Request constructor should accept input, init
|
||||||
|
var request
|
||||||
|
if (Request.prototype.isPrototypeOf(input) && !init) {
|
||||||
|
request = input
|
||||||
|
} else {
|
||||||
|
request = new Request(input, init)
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
var xhr = new XMLHttpRequest()
|
var xhr = new XMLHttpRequest()
|
||||||
|
if (request.credentials === 'cors') {
|
||||||
|
xhr.withCredentials = true;
|
||||||
|
}
|
||||||
|
|
||||||
function responseURL() {
|
function responseURL() {
|
||||||
if ('responseURL' in xhr) {
|
if ('responseURL' in xhr) {
|
||||||
|
@ -262,57 +357,24 @@ var self = {};
|
||||||
reject(new TypeError('Network request failed'))
|
reject(new TypeError('Network request failed'))
|
||||||
}
|
}
|
||||||
|
|
||||||
xhr.open(self.method, self.url)
|
xhr.open(request.method, request.url, true)
|
||||||
if ('responseType' in xhr && blobSupport) {
|
|
||||||
|
if ('responseType' in xhr && support.blob) {
|
||||||
xhr.responseType = 'blob'
|
xhr.responseType = 'blob'
|
||||||
}
|
}
|
||||||
|
|
||||||
self.headers.forEach(function(name, values) {
|
request.headers.forEach(function(name, values) {
|
||||||
values.forEach(function(value) {
|
values.forEach(function(value) {
|
||||||
xhr.setRequestHeader(name, value)
|
xhr.setRequestHeader(name, value)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
xhr.send((self._body === undefined) ? null : self._body)
|
xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Body.call(Request.prototype)
|
|
||||||
|
|
||||||
function Response(bodyInit, options) {
|
|
||||||
if (!options) {
|
|
||||||
options = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blobSupport) {
|
|
||||||
if (typeof bodyInit === 'string') {
|
|
||||||
this._bodyBlob = new Blob([bodyInit])
|
|
||||||
} else {
|
|
||||||
this._bodyBlob = bodyInit
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._bodyText = bodyInit
|
|
||||||
}
|
|
||||||
this.type = 'default'
|
|
||||||
this.url = null
|
|
||||||
this.status = options.status
|
|
||||||
this.statusText = options.statusText
|
|
||||||
this.headers = options.headers
|
|
||||||
this.url = options.url || ''
|
|
||||||
}
|
|
||||||
|
|
||||||
Body.call(Response.prototype)
|
|
||||||
|
|
||||||
self.Headers = Headers;
|
|
||||||
self.Request = Request;
|
|
||||||
self.Response = Response;
|
|
||||||
|
|
||||||
self.fetch = function (url, options) {
|
|
||||||
return new Request(url, options).fetch()
|
|
||||||
}
|
|
||||||
self.fetch.polyfill = true
|
self.fetch.polyfill = true
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/** End of the third-party code */
|
/** End of the third-party code */
|
||||||
|
|
||||||
module.exports = self.fetch;
|
module.exports = self;
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* @providesModule AssetRegistry
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var assets = [];
|
||||||
|
|
||||||
|
function registerAsset(asset) {
|
||||||
|
// `push` returns new array length, so the first asset will
|
||||||
|
// get id 1 (not 0) to make the value truthy
|
||||||
|
return assets.push(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAssetByID(assetId) {
|
||||||
|
return assets[assetId - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { registerAsset, getAssetByID };
|
|
@ -123,8 +123,7 @@ var Image = React.createClass({
|
||||||
'not be set directly on Image.');
|
'not be set directly on Image.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var source = resolveAssetSource(this.props.source);
|
var source = resolveAssetSource(this.props.source) || {};
|
||||||
invariant(source, 'source must be initialized');
|
|
||||||
|
|
||||||
var {width, height} = source;
|
var {width, height} = source;
|
||||||
var style = flattenStyle([{width, height}, styles.base, this.props.style]);
|
var style = flattenStyle([{width, height}, styles.base, this.props.style]);
|
||||||
|
|
|
@ -8,19 +8,24 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
jest.dontMock('../resolveAssetSource');
|
jest
|
||||||
|
.dontMock('AssetRegistry')
|
||||||
|
.dontMock('../resolveAssetSource');
|
||||||
|
|
||||||
var resolveAssetSource;
|
var resolveAssetSource;
|
||||||
var SourceCode;
|
var SourceCode;
|
||||||
|
var AssetRegistry;
|
||||||
|
|
||||||
function expectResolvesAsset(input, expectedSource) {
|
function expectResolvesAsset(input, expectedSource) {
|
||||||
expect(resolveAssetSource(input)).toEqual(expectedSource);
|
var assetId = AssetRegistry.registerAsset(input);
|
||||||
|
expect(resolveAssetSource(assetId)).toEqual(expectedSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('resolveAssetSource', () => {
|
describe('resolveAssetSource', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetModuleRegistry();
|
jest.resetModuleRegistry();
|
||||||
SourceCode = require('NativeModules').SourceCode;
|
SourceCode = require('NativeModules').SourceCode;
|
||||||
|
AssetRegistry = require('AssetRegistry');
|
||||||
resolveAssetSource = require('../resolveAssetSource');
|
resolveAssetSource = require('../resolveAssetSource');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -32,6 +37,22 @@ describe('resolveAssetSource', () => {
|
||||||
expect(resolveAssetSource(source2)).toBe(source2);
|
expect(resolveAssetSource(source2)).toBe(source2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not change deprecated assets', () => {
|
||||||
|
expect(resolveAssetSource({
|
||||||
|
isStatic: true,
|
||||||
|
deprecated: true,
|
||||||
|
width: 100,
|
||||||
|
height: 200,
|
||||||
|
uri: 'logo',
|
||||||
|
})).toEqual({
|
||||||
|
isStatic: true,
|
||||||
|
deprecated: true,
|
||||||
|
width: 100,
|
||||||
|
height: 200,
|
||||||
|
uri: 'logo',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('ignores any weird data', () => {
|
it('ignores any weird data', () => {
|
||||||
expect(resolveAssetSource(null)).toBe(null);
|
expect(resolveAssetSource(null)).toBe(null);
|
||||||
expect(resolveAssetSource(42)).toBe(null);
|
expect(resolveAssetSource(42)).toBe(null);
|
||||||
|
@ -81,25 +102,6 @@ describe('resolveAssetSource', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not change deprecated assets', () => {
|
|
||||||
expectResolvesAsset({
|
|
||||||
__packager_asset: true,
|
|
||||||
deprecated: true,
|
|
||||||
fileSystemLocation: '/root/app/module/a',
|
|
||||||
httpServerLocation: '/assets/module/a',
|
|
||||||
width: 100,
|
|
||||||
height: 200,
|
|
||||||
scales: [1],
|
|
||||||
hash: '5b6f00f',
|
|
||||||
name: 'logo',
|
|
||||||
type: 'png',
|
|
||||||
}, {
|
|
||||||
isStatic: true,
|
|
||||||
width: 100,
|
|
||||||
height: 200,
|
|
||||||
uri: 'logo',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('bundle was loaded from file', () => {
|
describe('bundle was loaded from file', () => {
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var AssetRegistry = require('AssetRegistry');
|
||||||
var PixelRatio = require('PixelRatio');
|
var PixelRatio = require('PixelRatio');
|
||||||
var SourceCode = require('NativeModules').SourceCode;
|
var SourceCode = require('NativeModules').SourceCode;
|
||||||
|
|
||||||
|
@ -44,58 +45,47 @@ function pickScale(scales, deviceScale) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveAssetSource(source) {
|
function resolveAssetSource(source) {
|
||||||
if (!source || typeof source !== 'object') {
|
if (typeof source === 'object') {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!source.__packager_asset) {
|
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated assets are managed by Xcode for now,
|
var asset = AssetRegistry.getAssetByID(source);
|
||||||
// just returning image name as `uri`
|
if (asset) {
|
||||||
// Examples:
|
return assetToImageSource(asset);
|
||||||
// require('image!deprecatd_logo_example')
|
|
||||||
// require('./new-hotness-logo-example.png')
|
|
||||||
if (source.deprecated) {
|
|
||||||
return {
|
|
||||||
width: source.width,
|
|
||||||
height: source.height,
|
|
||||||
isStatic: true,
|
|
||||||
uri: source.name || source.uri, // TODO(frantic): remove uri
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assetToImageSource(asset) {
|
||||||
// TODO(frantic): currently httpServerLocation is used both as
|
// TODO(frantic): currently httpServerLocation is used both as
|
||||||
// path in http URL and path within IPA. Should we have zipArchiveLocation?
|
// path in http URL and path within IPA. Should we have zipArchiveLocation?
|
||||||
var path = source.httpServerLocation;
|
var path = asset.httpServerLocation;
|
||||||
if (path[0] === '/') {
|
if (path[0] === '/') {
|
||||||
path = path.substr(1);
|
path = path.substr(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var scale = pickScale(source.scales, PixelRatio.get());
|
var scale = pickScale(asset.scales, PixelRatio.get());
|
||||||
var scaleSuffix = scale === 1 ? '' : '@' + scale + 'x';
|
var scaleSuffix = scale === 1 ? '' : '@' + scale + 'x';
|
||||||
|
|
||||||
var fileName = source.name + scaleSuffix + '.' + source.type;
|
var fileName = asset.name + scaleSuffix + '.' + asset.type;
|
||||||
var serverURL = getServerURL();
|
var serverURL = getServerURL();
|
||||||
if (serverURL) {
|
if (serverURL) {
|
||||||
return {
|
return {
|
||||||
width: source.width,
|
width: asset.width,
|
||||||
height: source.height,
|
height: asset.height,
|
||||||
uri: serverURL + path + '/' + fileName +
|
uri: serverURL + path + '/' + fileName +
|
||||||
'?hash=' + source.hash,
|
'?hash=' + asset.hash,
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
width: source.width,
|
width: asset.width,
|
||||||
height: source.height,
|
height: asset.height,
|
||||||
uri: path + '/' + fileName,
|
uri: path + '/' + fileName,
|
||||||
isStatic: true,
|
isStatic: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return source;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = resolveAssetSource;
|
module.exports = resolveAssetSource;
|
||||||
|
|
|
@ -135,7 +135,12 @@ function setupXHR() {
|
||||||
// The native XMLHttpRequest in Chrome dev tools is CORS aware and won't
|
// The native XMLHttpRequest in Chrome dev tools is CORS aware and won't
|
||||||
// let you fetch anything from the internet
|
// let you fetch anything from the internet
|
||||||
GLOBAL.XMLHttpRequest = require('XMLHttpRequest');
|
GLOBAL.XMLHttpRequest = require('XMLHttpRequest');
|
||||||
GLOBAL.fetch = require('fetch');
|
|
||||||
|
var fetchPolyfill = require('fetch');
|
||||||
|
GLOBAL.fetch = fetchPolyfill.fetch;
|
||||||
|
GLOBAL.Headers = fetchPolyfill.Headers;
|
||||||
|
GLOBAL.Request = fetchPolyfill.Request;
|
||||||
|
GLOBAL.Response = fetchPolyfill.Response;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupGeolocation() {
|
function setupGeolocation() {
|
||||||
|
|
|
@ -17,8 +17,6 @@ var RCTSourceCode = require('NativeModules').SourceCode;
|
||||||
var SourceMapConsumer = require('SourceMap').SourceMapConsumer;
|
var SourceMapConsumer = require('SourceMap').SourceMapConsumer;
|
||||||
var SourceMapURL = require('./source-map-url');
|
var SourceMapURL = require('./source-map-url');
|
||||||
|
|
||||||
var fetch = require('fetch');
|
|
||||||
|
|
||||||
function loadSourceMap(): Promise {
|
function loadSourceMap(): Promise {
|
||||||
return fetchSourceMap()
|
return fetchSourceMap()
|
||||||
.then(map => new SourceMapConsumer(map));
|
.then(map => new SourceMapConsumer(map));
|
||||||
|
|
|
@ -9,8 +9,7 @@
|
||||||
* @providesModule ReactIOSViewAttributes
|
* @providesModule ReactIOSViewAttributes
|
||||||
* @flow
|
* @flow
|
||||||
*/
|
*/
|
||||||
|
'use strict';
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var merge = require('merge');
|
var merge = require('merge');
|
||||||
|
|
||||||
|
@ -21,6 +20,7 @@ ReactIOSViewAttributes.UIView = {
|
||||||
accessible: true,
|
accessible: true,
|
||||||
accessibilityLabel: true,
|
accessibilityLabel: true,
|
||||||
testID: true,
|
testID: true,
|
||||||
|
onLayout: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
ReactIOSViewAttributes.RCTView = merge(
|
ReactIOSViewAttributes.RCTView = merge(
|
||||||
|
@ -31,7 +31,7 @@ ReactIOSViewAttributes.RCTView = merge(
|
||||||
// For this property to be effective, it must be applied to a view that contains
|
// For this property to be effective, it must be applied to a view that contains
|
||||||
// many subviews that extend outside its bound. The subviews must also have
|
// many subviews that extend outside its bound. The subviews must also have
|
||||||
// overflow: hidden, as should the containing view (or one of its superviews).
|
// overflow: hidden, as should the containing view (or one of its superviews).
|
||||||
removeClippedSubviews: true
|
removeClippedSubviews: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = ReactIOSViewAttributes;
|
module.exports = ReactIOSViewAttributes;
|
||||||
|
|
|
@ -42,6 +42,16 @@ function diffRawProperties(
|
||||||
}
|
}
|
||||||
prevProp = prevProps && prevProps[propKey];
|
prevProp = prevProps && prevProps[propKey];
|
||||||
nextProp = nextProps[propKey];
|
nextProp = nextProps[propKey];
|
||||||
|
|
||||||
|
// functions are converted to booleans as markers that the associated
|
||||||
|
// events should be sent from native.
|
||||||
|
if (typeof prevProp === 'function') {
|
||||||
|
prevProp = true;
|
||||||
|
}
|
||||||
|
if (typeof nextProp === 'function') {
|
||||||
|
nextProp = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (prevProp !== nextProp) {
|
if (prevProp !== nextProp) {
|
||||||
// If you want a property's diff to be detected, you must configure it
|
// If you want a property's diff to be detected, you must configure it
|
||||||
// to be so - *or* it must be a scalar property. For now, we'll allow
|
// to be so - *or* it must be a scalar property. For now, we'll allow
|
||||||
|
@ -75,6 +85,16 @@ function diffRawProperties(
|
||||||
}
|
}
|
||||||
prevProp = prevProps[propKey];
|
prevProp = prevProps[propKey];
|
||||||
nextProp = nextProps && nextProps[propKey];
|
nextProp = nextProps && nextProps[propKey];
|
||||||
|
|
||||||
|
// functions are converted to booleans as markers that the associated
|
||||||
|
// events should be sent from native.
|
||||||
|
if (typeof prevProp === 'function') {
|
||||||
|
prevProp = true;
|
||||||
|
}
|
||||||
|
if (typeof nextProp === 'function') {
|
||||||
|
nextProp = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (prevProp !== nextProp) {
|
if (prevProp !== nextProp) {
|
||||||
if (nextProp === undefined) {
|
if (nextProp === undefined) {
|
||||||
nextProp = null; // null is a sentinel we explicitly send to native
|
nextProp = null; // null is a sentinel we explicitly send to native
|
||||||
|
|
|
@ -16,3 +16,8 @@ declare var __DEV__: boolean;
|
||||||
declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: any; /*?{
|
declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: any; /*?{
|
||||||
inject: ?((stuff: Object) => void)
|
inject: ?((stuff: Object) => void)
|
||||||
};*/
|
};*/
|
||||||
|
|
||||||
|
declare var fetch: any;
|
||||||
|
declare var Headers: any;
|
||||||
|
declare var Request: any;
|
||||||
|
declare var Response: any;
|
||||||
|
|
|
@ -28,6 +28,11 @@ extern NSString *const RCTReloadNotification;
|
||||||
*/
|
*/
|
||||||
extern NSString *const RCTJavaScriptDidLoadNotification;
|
extern NSString *const RCTJavaScriptDidLoadNotification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This notification fires when the bridge failed to load.
|
||||||
|
*/
|
||||||
|
extern NSString *const RCTJavaScriptDidFailToLoadNotification;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This block can be used to instantiate modules that require additional
|
* This block can be used to instantiate modules that require additional
|
||||||
* init parameters, or additional configuration prior to being used.
|
* init parameters, or additional configuration prior to being used.
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
NSString *const RCTReloadNotification = @"RCTReloadNotification";
|
NSString *const RCTReloadNotification = @"RCTReloadNotification";
|
||||||
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
|
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
|
||||||
|
NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailToLoadNotification";
|
||||||
|
|
||||||
dispatch_queue_t const RCTJSThread = nil;
|
dispatch_queue_t const RCTJSThread = nil;
|
||||||
|
|
||||||
|
@ -867,6 +868,11 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
return _batchedBridge.modules;
|
return _batchedBridge.modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (RCTEventDispatcher *)eventDispatcher
|
||||||
|
{
|
||||||
|
return _eventDispatcher ?: _batchedBridge.eventDispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
#define RCT_BRIDGE_WARN(...) \
|
#define RCT_BRIDGE_WARN(...) \
|
||||||
- (void)__VA_ARGS__ \
|
- (void)__VA_ARGS__ \
|
||||||
{ \
|
{ \
|
||||||
|
@ -1082,16 +1088,18 @@ RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)me
|
||||||
|
|
||||||
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self];
|
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self];
|
||||||
[loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) {
|
[loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) {
|
||||||
|
|
||||||
_loading = NO;
|
_loading = NO;
|
||||||
if (!self.isValid) {
|
if (!self.isValid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||||
sourceCodeModule.scriptURL = bundleURL;
|
sourceCodeModule.scriptURL = bundleURL;
|
||||||
sourceCodeModule.scriptText = script;
|
sourceCodeModule.scriptText = script;
|
||||||
if (error != nil) {
|
if (error) {
|
||||||
|
|
||||||
NSArray *stack = [[error userInfo] objectForKey:@"stack"];
|
NSArray *stack = [error userInfo][@"stack"];
|
||||||
if (stack) {
|
if (stack) {
|
||||||
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
|
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
|
||||||
withStack:stack];
|
withStack:stack];
|
||||||
|
@ -1100,10 +1108,17 @@ RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)me
|
||||||
withDetails:[error localizedFailureReason]];
|
withDetails:[error localizedFailureReason]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSDictionary *userInfo = @{@"error": error};
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
|
||||||
|
object:self
|
||||||
|
userInfo:userInfo];
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
[self enqueueApplicationScript:script url:bundleURL onComplete:^(NSError *loadError) {
|
[self enqueueApplicationScript:script url:bundleURL onComplete:^(NSError *loadError) {
|
||||||
|
|
||||||
if (!loadError) {
|
if (!loadError) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the display link to start sending js calls after everything
|
* Register the display link to start sending js calls after everything
|
||||||
* is setup
|
* is setup
|
||||||
|
@ -1144,18 +1159,11 @@ RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)me
|
||||||
_latestJSExecutor = nil;
|
_latestJSExecutor = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void (^mainThreadInvalidate)(void) = ^{
|
||||||
* Main Thread deallocations
|
|
||||||
*/
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
||||||
[_mainDisplayLink invalidate];
|
|
||||||
|
|
||||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
/**
|
[_mainDisplayLink invalidate];
|
||||||
* JS Thread deallocations
|
_mainDisplayLink = nil;
|
||||||
*/
|
|
||||||
[_javaScriptExecutor invalidate];
|
|
||||||
[_jsDisplayLink invalidate];
|
|
||||||
|
|
||||||
// Invalidate modules
|
// Invalidate modules
|
||||||
for (id target in _modulesByID.allObjects) {
|
for (id target in _modulesByID.allObjects) {
|
||||||
|
@ -1165,11 +1173,35 @@ RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)me
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release modules (breaks retain cycle if module has strong bridge reference)
|
// Release modules (breaks retain cycle if module has strong bridge reference)
|
||||||
_javaScriptExecutor = nil;
|
|
||||||
_frameUpdateObservers = nil;
|
_frameUpdateObservers = nil;
|
||||||
_modulesByID = nil;
|
_modulesByID = nil;
|
||||||
_queuesByID = nil;
|
_queuesByID = nil;
|
||||||
_modulesByName = nil;
|
_modulesByName = nil;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!_javaScriptExecutor) {
|
||||||
|
|
||||||
|
// No JS thread running
|
||||||
|
mainThreadInvalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS Thread deallocations
|
||||||
|
*/
|
||||||
|
[_javaScriptExecutor invalidate];
|
||||||
|
_javaScriptExecutor = nil;
|
||||||
|
|
||||||
|
[_jsDisplayLink invalidate];
|
||||||
|
_jsDisplayLink = nil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main Thread deallocations
|
||||||
|
*/
|
||||||
|
mainThreadInvalidate();
|
||||||
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1300,48 +1332,6 @@ RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)me
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Event deduping
|
|
||||||
*
|
|
||||||
* Right now we make a lot of assumptions about the arguments structure
|
|
||||||
* so just iterate if it's a `callFunctionReturnFlushedQueue()`
|
|
||||||
*/
|
|
||||||
if ([method isEqualToString:@"callFunctionReturnFlushedQueue"]) {
|
|
||||||
NSString *moduleName = RCTLocalModuleNames[[args[0] integerValue]];
|
|
||||||
/**
|
|
||||||
* Keep going if it any event emmiter, e.g. RCT(Device|NativeApp)?EventEmitter
|
|
||||||
*/
|
|
||||||
if ([moduleName hasSuffix:@"EventEmitter"]) {
|
|
||||||
for (NSDictionary *call in [strongSelf->_scheduledCalls copy]) {
|
|
||||||
NSArray *callArgs = call[@"args"];
|
|
||||||
/**
|
|
||||||
* If it's the same module && method call on the bridge &&
|
|
||||||
* the same EventEmitter module && method
|
|
||||||
*/
|
|
||||||
if (
|
|
||||||
[call[@"module"] isEqualToString:module] &&
|
|
||||||
[call[@"method"] isEqualToString:method] &&
|
|
||||||
[callArgs[0] isEqual:args[0]] &&
|
|
||||||
[callArgs[1] isEqual:args[1]]
|
|
||||||
) {
|
|
||||||
/**
|
|
||||||
* args[2] contains the actual arguments for the event call, where
|
|
||||||
* args[2][0] is the target for RCTEventEmitter or the eventName
|
|
||||||
* for the other EventEmitters
|
|
||||||
* if RCTEventEmitter we need to compare args[2][1] that will be
|
|
||||||
* the eventName
|
|
||||||
*/
|
|
||||||
if (
|
|
||||||
[args[2][0] isEqual:callArgs[2][0]] &&
|
|
||||||
(![moduleName isEqualToString:@"RCTEventEmitter"] || [args[2][1] isEqual:callArgs[2][1]])
|
|
||||||
) {
|
|
||||||
[strongSelf->_scheduledCalls removeObject:call];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
id call = @{
|
id call = @{
|
||||||
@"module": module,
|
@"module": module,
|
||||||
@"method": method,
|
@"method": method,
|
||||||
|
@ -1495,17 +1485,13 @@ RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)me
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!RCT_DEBUG) {
|
@try {
|
||||||
[method invokeWithBridge:strongSelf module:module arguments:params context:context];
|
[method invokeWithBridge:strongSelf module:module arguments:params context:context];
|
||||||
} else {
|
}
|
||||||
@try {
|
@catch (NSException *exception) {
|
||||||
[method invokeWithBridge:strongSelf module:module arguments:params context:context];
|
RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, module, params, exception);
|
||||||
}
|
if (!RCT_DEBUG && [exception.name rangeOfString:@"Unhandled JS Exception"].location != NSNotFound) {
|
||||||
@catch (NSException *exception) {
|
@throw exception;
|
||||||
RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, module, params, exception);
|
|
||||||
if ([exception.name rangeOfString:@"Unhandled JS Exception"].location != NSNotFound) {
|
|
||||||
@throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,9 +73,6 @@ RCT_EXPORT_MODULE()
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
|
|
||||||
_defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
[self updateSettings];
|
|
||||||
|
|
||||||
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
||||||
|
|
||||||
[notificationCenter addObserver:self
|
[notificationCenter addObserver:self
|
||||||
|
@ -93,19 +90,27 @@ RCT_EXPORT_MODULE()
|
||||||
name:RCTJavaScriptDidLoadNotification
|
name:RCTJavaScriptDidLoadNotification
|
||||||
object:nil];
|
object:nil];
|
||||||
|
|
||||||
|
_defaults = [NSUserDefaults standardUserDefaults];
|
||||||
|
_settings = [[NSMutableDictionary alloc] init];
|
||||||
|
|
||||||
|
// Delay setup until after Bridge init
|
||||||
|
__weak RCTDevMenu *weakSelf = self;
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[weakSelf updateSettings];
|
||||||
|
});
|
||||||
|
|
||||||
#if TARGET_IPHONE_SIMULATOR
|
#if TARGET_IPHONE_SIMULATOR
|
||||||
|
|
||||||
__weak RCTDevMenu *weakSelf = self;
|
|
||||||
RCTKeyCommands *commands = [RCTKeyCommands sharedInstance];
|
RCTKeyCommands *commands = [RCTKeyCommands sharedInstance];
|
||||||
|
|
||||||
// toggle debug menu
|
// Toggle debug menu
|
||||||
[commands registerKeyCommandWithInput:@"d"
|
[commands registerKeyCommandWithInput:@"d"
|
||||||
modifierFlags:UIKeyModifierCommand
|
modifierFlags:UIKeyModifierCommand
|
||||||
action:^(UIKeyCommand *command) {
|
action:^(UIKeyCommand *command) {
|
||||||
[weakSelf toggle];
|
[weakSelf toggle];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// reload in normal mode
|
// Reload in normal mode
|
||||||
[commands registerKeyCommandWithInput:@"n"
|
[commands registerKeyCommandWithInput:@"n"
|
||||||
modifierFlags:UIKeyModifierCommand
|
modifierFlags:UIKeyModifierCommand
|
||||||
action:^(UIKeyCommand *command) {
|
action:^(UIKeyCommand *command) {
|
||||||
|
@ -117,22 +122,23 @@ RCT_EXPORT_MODULE()
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (dispatch_queue_t)methodQueue
|
||||||
|
{
|
||||||
|
return dispatch_get_main_queue();
|
||||||
|
}
|
||||||
|
|
||||||
- (void)updateSettings
|
- (void)updateSettings
|
||||||
{
|
{
|
||||||
__weak RCTDevMenu *weakSelf = self;
|
NSDictionary *settings = [_defaults objectForKey:RCTDevMenuSettingsKey];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
if ([settings isEqualToDictionary:_settings]) {
|
||||||
RCTDevMenu *strongSelf = weakSelf;
|
return;
|
||||||
if (!strongSelf) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
strongSelf->_settings = [NSMutableDictionary dictionaryWithDictionary:[strongSelf->_defaults objectForKey:RCTDevMenuSettingsKey]];
|
[_settings setDictionary:settings];
|
||||||
|
self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue];
|
||||||
strongSelf.shakeToShow = [strongSelf->_settings[@"shakeToShow"] ?: @YES boolValue];
|
self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue];
|
||||||
strongSelf.profilingEnabled = [strongSelf->_settings[@"profilingEnabled"] ?: @NO boolValue];
|
self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue];
|
||||||
strongSelf.liveReloadEnabled = [strongSelf->_settings[@"liveReloadEnabled"] ?: @NO boolValue];
|
self.executorClass = NSClassFromString(_settings[@"executorClass"]);
|
||||||
strongSelf.executorClass = NSClassFromString(strongSelf->_settings[@"executorClass"]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)jsLoaded
|
- (void)jsLoaded
|
||||||
|
@ -161,6 +167,11 @@ RCT_EXPORT_MODULE()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)isValid
|
||||||
|
{
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
[_updateTask cancel];
|
[_updateTask cancel];
|
||||||
|
@ -170,6 +181,10 @@ RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
- (void)updateSetting:(NSString *)name value:(id)value
|
- (void)updateSetting:(NSString *)name value:(id)value
|
||||||
{
|
{
|
||||||
|
id currentValue = _settings[name];
|
||||||
|
if (currentValue == value || [currentValue isEqual:value]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (value) {
|
if (value) {
|
||||||
_settings[name] = value;
|
_settings[name] = value;
|
||||||
} else {
|
} else {
|
||||||
|
@ -239,6 +254,9 @@ RCT_EXPORT_METHOD(reload)
|
||||||
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
|
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
|
||||||
{
|
{
|
||||||
_actionSheet = nil;
|
_actionSheet = nil;
|
||||||
|
if (buttonIndex == actionSheet.cancelButtonIndex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (buttonIndex) {
|
switch (buttonIndex) {
|
||||||
case 0: {
|
case 0: {
|
||||||
|
|
|
@ -76,10 +76,27 @@
|
||||||
reloadButton.frame = CGRectMake(buttonWidth, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight);
|
reloadButton.frame = CGRectMake(buttonWidth, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight);
|
||||||
[_rootView addSubview:dismissButton];
|
[_rootView addSubview:dismissButton];
|
||||||
[_rootView addSubview:reloadButton];
|
[_rootView addSubview:reloadButton];
|
||||||
|
|
||||||
|
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
||||||
|
|
||||||
|
[notificationCenter addObserver:self
|
||||||
|
selector:@selector(dismiss)
|
||||||
|
name:RCTReloadNotification
|
||||||
|
object:nil];
|
||||||
|
|
||||||
|
[notificationCenter addObserver:self
|
||||||
|
selector:@selector(dismiss)
|
||||||
|
name:RCTJavaScriptDidLoadNotification
|
||||||
|
object:nil];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)openStackFrameInEditor:(NSDictionary *)stackFrame
|
- (void)openStackFrameInEditor:(NSDictionary *)stackFrame
|
||||||
{
|
{
|
||||||
NSData *stackFrameJSON = [RCTJSONStringify(stackFrame, nil) dataUsingEncoding:NSUTF8StringEncoding];
|
NSData *stackFrameJSON = [RCTJSONStringify(stackFrame, nil) dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
@ -125,7 +142,6 @@
|
||||||
- (void)reload
|
- (void)reload
|
||||||
{
|
{
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil userInfo:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil userInfo:nil];
|
||||||
[self dismiss];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - TableView
|
#pragma mark - TableView
|
||||||
|
|
|
@ -43,6 +43,7 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
|
||||||
UIWebView *_webView;
|
UIWebView *_webView;
|
||||||
NSMutableDictionary *_objectsToInject;
|
NSMutableDictionary *_objectsToInject;
|
||||||
NSRegularExpression *_commentsRegex;
|
NSRegularExpression *_commentsRegex;
|
||||||
|
NSRegularExpression *_scriptTagsRegex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@synthesize valid = _valid;
|
@synthesize valid = _valid;
|
||||||
|
@ -52,7 +53,8 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_objectsToInject = [[NSMutableDictionary alloc] init];
|
_objectsToInject = [[NSMutableDictionary alloc] init];
|
||||||
_webView = webView ?: [[UIWebView alloc] init];
|
_webView = webView ?: [[UIWebView alloc] init];
|
||||||
_commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]+?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL];
|
_commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]*?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL],
|
||||||
|
_scriptTagsRegex = [NSRegularExpression regularExpressionWithPattern:@"<(\\/?script[^>]*?)>" options:0 error:NULL],
|
||||||
_webView.delegate = self;
|
_webView.delegate = self;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
@ -139,11 +141,6 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
|
||||||
onComplete(error);
|
onComplete(error);
|
||||||
};
|
};
|
||||||
|
|
||||||
script = [_commentsRegex stringByReplacingMatchesInString:script
|
|
||||||
options:0
|
|
||||||
range:NSMakeRange(0, script.length)
|
|
||||||
withTemplate:@""];
|
|
||||||
|
|
||||||
if (_objectsToInject.count > 0) {
|
if (_objectsToInject.count > 0) {
|
||||||
NSMutableString *scriptWithInjections = [[NSMutableString alloc] initWithString:@"/* BEGIN NATIVELY INJECTED OBJECTS */\n"];
|
NSMutableString *scriptWithInjections = [[NSMutableString alloc] initWithString:@"/* BEGIN NATIVELY INJECTED OBJECTS */\n"];
|
||||||
[_objectsToInject enumerateKeysAndObjectsUsingBlock:^(NSString *objectName, NSString *blockScript, BOOL *stop) {
|
[_objectsToInject enumerateKeysAndObjectsUsingBlock:^(NSString *objectName, NSString *blockScript, BOOL *stop) {
|
||||||
|
@ -158,6 +155,15 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
|
||||||
script = scriptWithInjections;
|
script = scriptWithInjections;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
script = [_commentsRegex stringByReplacingMatchesInString:script
|
||||||
|
options:0
|
||||||
|
range:NSMakeRange(0, script.length)
|
||||||
|
withTemplate:@""];
|
||||||
|
script = [_scriptTagsRegex stringByReplacingMatchesInString:script
|
||||||
|
options:0
|
||||||
|
range:NSMakeRange(0, script.length)
|
||||||
|
withTemplate:@"\\\\<$1\\\\>"];
|
||||||
|
|
||||||
NSString *runScript =
|
NSString *runScript =
|
||||||
[NSString
|
[NSString
|
||||||
stringWithFormat:@"<html><head></head><body><script type='text/javascript'>%@</script></body></html>",
|
stringWithFormat:@"<html><head></head><body><script type='text/javascript'>%@</script></body></html>",
|
||||||
|
|
|
@ -44,50 +44,45 @@ RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if RCT_DEBUG // Red box is only available in debug mode
|
|
||||||
|
|
||||||
[[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
|
[[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
|
||||||
|
|
||||||
#else
|
if (!RCT_DEBUG) {
|
||||||
|
|
||||||
static NSUInteger reloadRetries = 0;
|
static NSUInteger reloadRetries = 0;
|
||||||
const NSUInteger maxMessageLength = 75;
|
const NSUInteger maxMessageLength = 75;
|
||||||
|
|
||||||
if (reloadRetries < _maxReloadAttempts) {
|
if (reloadRetries < _maxReloadAttempts) {
|
||||||
|
|
||||||
reloadRetries++;
|
reloadRetries++;
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification
|
||||||
object:nil];
|
object:nil];
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (message.length > maxMessageLength) {
|
if (message.length > maxMessageLength) {
|
||||||
message = [[message substringToIndex:maxMessageLength] stringByAppendingString:@"..."];
|
message = [[message substringToIndex:maxMessageLength] stringByAppendingString:@"..."];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableString *prettyStack = [NSMutableString stringWithString:@"\n"];
|
||||||
|
for (NSDictionary *frame in stack) {
|
||||||
|
[prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:message];
|
||||||
|
[NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableString *prettyStack = [NSMutableString stringWithString:@"\n"];
|
|
||||||
for (NSDictionary *frame in stack) {
|
|
||||||
[prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:message];
|
|
||||||
[NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message
|
RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message
|
||||||
stack:(NSArray *)stack)
|
stack:(NSArray *)stack)
|
||||||
{
|
{
|
||||||
|
if (_delegate) {
|
||||||
|
[_delegate unhandledJSExceptionWithMessage:message stack:stack];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#if RCT_DEBUG // Red box is only available in debug mode
|
[[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack];
|
||||||
|
|
||||||
[[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
#import "RCTConvert.h"
|
#import "RCTConvert.h"
|
||||||
#import "RCTDefines.h"
|
#import "RCTDefines.h"
|
||||||
|
#import "RCTEventDispatcher.h"
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
#import "RCTProfile.h"
|
#import "RCTProfile.h"
|
||||||
#import "RCTRootView.h"
|
#import "RCTRootView.h"
|
||||||
|
@ -420,17 +421,31 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
||||||
[rootShadowView collectRootUpdatedFrames:viewsWithNewFrames
|
[rootShadowView collectRootUpdatedFrames:viewsWithNewFrames
|
||||||
parentConstraint:(CGSize){CSS_UNDEFINED, CSS_UNDEFINED}];
|
parentConstraint:(CGSize){CSS_UNDEFINED, CSS_UNDEFINED}];
|
||||||
|
|
||||||
// Parallel arrays
|
// Parallel arrays are built and then handed off to main thread
|
||||||
NSMutableArray *frameReactTags = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
NSMutableArray *frameReactTags = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||||
NSMutableArray *frames = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
NSMutableArray *frames = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||||
NSMutableArray *areNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
NSMutableArray *areNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||||
NSMutableArray *parentsAreNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
NSMutableArray *parentsAreNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||||
|
NSMutableArray *onLayoutEvents = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||||
|
|
||||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||||
[frameReactTags addObject:shadowView.reactTag];
|
[frameReactTags addObject:shadowView.reactTag];
|
||||||
[frames addObject:[NSValue valueWithCGRect:shadowView.frame]];
|
[frames addObject:[NSValue valueWithCGRect:shadowView.frame]];
|
||||||
[areNew addObject:@(shadowView.isNewView)];
|
[areNew addObject:@(shadowView.isNewView)];
|
||||||
[parentsAreNew addObject:@(shadowView.superview.isNewView)];
|
[parentsAreNew addObject:@(shadowView.superview.isNewView)];
|
||||||
|
id event = [NSNull null];
|
||||||
|
if (shadowView.hasOnLayout) {
|
||||||
|
event = @{
|
||||||
|
@"target": shadowView.reactTag,
|
||||||
|
@"layout": @{
|
||||||
|
@"x": @(shadowView.frame.origin.x),
|
||||||
|
@"y": @(shadowView.frame.origin.y),
|
||||||
|
@"width": @(shadowView.frame.size.width),
|
||||||
|
@"height": @(shadowView.frame.size.height),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
[onLayoutEvents addObject:event];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||||
|
@ -448,20 +463,30 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
||||||
// Perform layout (possibly animated)
|
// Perform layout (possibly animated)
|
||||||
NSNumber *rootViewTag = rootShadowView.reactTag;
|
NSNumber *rootViewTag = rootShadowView.reactTag;
|
||||||
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
|
RCTResponseSenderBlock callback = self->_layoutAnimation.callback;
|
||||||
|
__block NSInteger completionsCalled = 0;
|
||||||
for (NSUInteger ii = 0; ii < frames.count; ii++) {
|
for (NSUInteger ii = 0; ii < frames.count; ii++) {
|
||||||
NSNumber *reactTag = frameReactTags[ii];
|
NSNumber *reactTag = frameReactTags[ii];
|
||||||
UIView *view = viewRegistry[reactTag];
|
UIView *view = viewRegistry[reactTag];
|
||||||
CGRect frame = [frames[ii] CGRectValue];
|
CGRect frame = [frames[ii] CGRectValue];
|
||||||
|
id event = onLayoutEvents[ii];
|
||||||
|
|
||||||
|
BOOL isNew = [areNew[ii] boolValue];
|
||||||
|
RCTAnimation *updateAnimation = isNew ? nil : _layoutAnimation.updateAnimation;
|
||||||
|
BOOL shouldAnimateCreation = isNew && ![parentsAreNew[ii] boolValue];
|
||||||
|
RCTAnimation *createAnimation = shouldAnimateCreation ? _layoutAnimation.createAnimation : nil;
|
||||||
|
|
||||||
void (^completion)(BOOL finished) = ^(BOOL finished) {
|
void (^completion)(BOOL finished) = ^(BOOL finished) {
|
||||||
if (self->_layoutAnimation.callback) {
|
completionsCalled++;
|
||||||
self->_layoutAnimation.callback(@[@(finished)]);
|
if (event != [NSNull null]) {
|
||||||
|
[self.bridge.eventDispatcher sendInputEventWithName:@"topLayout" body:event];
|
||||||
|
}
|
||||||
|
if (callback && completionsCalled == frames.count - 1) {
|
||||||
|
callback(@[@(finished)]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Animate view update
|
// Animate view update
|
||||||
BOOL isNew = [areNew[ii] boolValue];
|
|
||||||
RCTAnimation *updateAnimation = isNew ? nil: _layoutAnimation.updateAnimation;
|
|
||||||
if (updateAnimation) {
|
if (updateAnimation) {
|
||||||
[updateAnimation performAnimations:^{
|
[updateAnimation performAnimations:^{
|
||||||
[view reactSetFrame:frame];
|
[view reactSetFrame:frame];
|
||||||
|
@ -478,9 +503,7 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animate view creation
|
// Animate view creation
|
||||||
BOOL shouldAnimateCreation = isNew && ![parentsAreNew[ii] boolValue];
|
if (createAnimation) {
|
||||||
RCTAnimation *createAnimation = _layoutAnimation.createAnimation;
|
|
||||||
if (shouldAnimateCreation && createAnimation) {
|
|
||||||
if ([createAnimation.property isEqualToString:@"scaleXY"]) {
|
if ([createAnimation.property isEqualToString:@"scaleXY"]) {
|
||||||
view.layer.transform = CATransform3DMakeScale(0, 0, 0);
|
view.layer.transform = CATransform3DMakeScale(0, 0, 0);
|
||||||
} else if ([createAnimation.property isEqualToString:@"opacity"]) {
|
} else if ([createAnimation.property isEqualToString:@"opacity"]) {
|
||||||
|
@ -1159,6 +1182,12 @@ RCT_EXPORT_METHOD(clearJSResponder)
|
||||||
@"captured": @"onNavigationCompleteCapture"
|
@"captured": @"onNavigationCompleteCapture"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@"topNavLeftButtonTap": @{
|
||||||
|
@"phasedRegistrationNames": @{
|
||||||
|
@"bubbled": @"onNavLeftButtonTap",
|
||||||
|
@"captured": @"onNavLefttButtonTapCapture"
|
||||||
|
}
|
||||||
|
},
|
||||||
@"topNavRightButtonTap": @{
|
@"topNavRightButtonTap": @{
|
||||||
@"phasedRegistrationNames": @{
|
@"phasedRegistrationNames": @{
|
||||||
@"bubbled": @"onNavRightButtonTap",
|
@"bubbled": @"onNavRightButtonTap",
|
||||||
|
@ -1256,6 +1285,9 @@ RCT_EXPORT_METHOD(clearJSResponder)
|
||||||
@"topScrollAnimationEnd": @{
|
@"topScrollAnimationEnd": @{
|
||||||
@"registrationName": @"onScrollAnimationEnd"
|
@"registrationName": @"onScrollAnimationEnd"
|
||||||
},
|
},
|
||||||
|
@"topLayout": @{
|
||||||
|
@"registrationName": @"onLayout"
|
||||||
|
},
|
||||||
@"topSelectionChange": @{
|
@"topSelectionChange": @{
|
||||||
@"registrationName": @"onSelectionChange"
|
@"registrationName": @"onSelectionChange"
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,7 +21,7 @@ extern const CGFloat RCTMapZoomBoundBuffer;
|
||||||
@interface RCTMap: MKMapView
|
@interface RCTMap: MKMapView
|
||||||
|
|
||||||
@property (nonatomic, assign) BOOL followUserLocation;
|
@property (nonatomic, assign) BOOL followUserLocation;
|
||||||
@property (nonatomic, assign) BOOL hasStartedLoading;
|
@property (nonatomic, assign) BOOL hasStartedRendering;
|
||||||
@property (nonatomic, assign) CGFloat minDelta;
|
@property (nonatomic, assign) CGFloat minDelta;
|
||||||
@property (nonatomic, assign) CGFloat maxDelta;
|
@property (nonatomic, assign) CGFloat maxDelta;
|
||||||
@property (nonatomic, assign) UIEdgeInsets legalLabelInsets;
|
@property (nonatomic, assign) UIEdgeInsets legalLabelInsets;
|
||||||
|
|
|
@ -27,7 +27,7 @@ const CGFloat RCTMapZoomBoundBuffer = 0.01;
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
|
|
||||||
_hasStartedLoading = NO;
|
_hasStartedRendering = NO;
|
||||||
|
|
||||||
// Find Apple link label
|
// Find Apple link label
|
||||||
for (UIView *subview in self.subviews) {
|
for (UIView *subview in self.subviews) {
|
||||||
|
|
|
@ -84,15 +84,15 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
|
||||||
[self _regionChanged:mapView];
|
[self _regionChanged:mapView];
|
||||||
|
|
||||||
// Don't send region did change events until map has
|
// Don't send region did change events until map has
|
||||||
// started loading, as these won't represent the final location
|
// started rendering, as these won't represent the final location
|
||||||
if (mapView.hasStartedLoading) {
|
if (mapView.hasStartedRendering) {
|
||||||
[self _emitRegionChangeEvent:mapView continuous:NO];
|
[self _emitRegionChangeEvent:mapView continuous:NO];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mapViewWillStartLoadingMap:(RCTMap *)mapView
|
- (void)mapViewWillStartRenderingMap:(RCTMap *)mapView
|
||||||
{
|
{
|
||||||
mapView.hasStartedLoading = YES;
|
mapView.hasStartedRendering = YES;
|
||||||
[self _emitRegionChangeEvent:mapView continuous:NO];
|
[self _emitRegionChangeEvent:mapView continuous:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,19 @@
|
||||||
@interface RCTNavItem : UIView
|
@interface RCTNavItem : UIView
|
||||||
|
|
||||||
@property (nonatomic, copy) NSString *title;
|
@property (nonatomic, copy) NSString *title;
|
||||||
|
@property (nonatomic, strong) UIImage *leftButtonIcon;
|
||||||
|
@property (nonatomic, copy) NSString *leftButtonTitle;
|
||||||
|
@property (nonatomic, strong) UIImage *rightButtonIcon;
|
||||||
@property (nonatomic, copy) NSString *rightButtonTitle;
|
@property (nonatomic, copy) NSString *rightButtonTitle;
|
||||||
|
@property (nonatomic, strong) UIImage *backButtonIcon;
|
||||||
@property (nonatomic, copy) NSString *backButtonTitle;
|
@property (nonatomic, copy) NSString *backButtonTitle;
|
||||||
@property (nonatomic, assign) BOOL navigationBarHidden;
|
@property (nonatomic, assign) BOOL navigationBarHidden;
|
||||||
@property (nonatomic, copy) UIColor *tintColor;
|
@property (nonatomic, strong) UIColor *tintColor;
|
||||||
@property (nonatomic, copy) UIColor *barTintColor;
|
@property (nonatomic, strong) UIColor *barTintColor;
|
||||||
@property (nonatomic, copy) UIColor *titleTextColor;
|
@property (nonatomic, strong) UIColor *titleTextColor;
|
||||||
|
|
||||||
|
@property (nonatomic, readonly) UIBarButtonItem *backButtonItem;
|
||||||
|
@property (nonatomic, readonly) UIBarButtonItem *leftButtonItem;
|
||||||
|
@property (nonatomic, readonly) UIBarButtonItem *rightButtonItem;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -11,5 +11,104 @@
|
||||||
|
|
||||||
@implementation RCTNavItem
|
@implementation RCTNavItem
|
||||||
|
|
||||||
@end
|
@synthesize backButtonItem = _backButtonItem;
|
||||||
|
@synthesize leftButtonItem = _leftButtonItem;
|
||||||
|
@synthesize rightButtonItem = _rightButtonItem;
|
||||||
|
|
||||||
|
- (void)setBackButtonTitle:(NSString *)backButtonTitle
|
||||||
|
{
|
||||||
|
_backButtonTitle = backButtonTitle;
|
||||||
|
_backButtonItem = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setBackButtonIcon:(UIImage *)backButtonIcon
|
||||||
|
{
|
||||||
|
_backButtonIcon = backButtonIcon;
|
||||||
|
_backButtonItem = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIBarButtonItem *)backButtonItem
|
||||||
|
{
|
||||||
|
if (!_backButtonItem) {
|
||||||
|
if (_backButtonIcon) {
|
||||||
|
_backButtonItem = [[UIBarButtonItem alloc] initWithImage:_backButtonIcon
|
||||||
|
style:UIBarButtonItemStylePlain
|
||||||
|
target:nil
|
||||||
|
action:nil];
|
||||||
|
} else if (_backButtonTitle.length) {
|
||||||
|
_backButtonItem = [[UIBarButtonItem alloc] initWithTitle:_backButtonTitle
|
||||||
|
style:UIBarButtonItemStylePlain
|
||||||
|
target:nil
|
||||||
|
action:nil];
|
||||||
|
} else {
|
||||||
|
_backButtonItem = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _backButtonItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setLeftButtonTitle:(NSString *)leftButtonTitle
|
||||||
|
{
|
||||||
|
_leftButtonTitle = leftButtonTitle;
|
||||||
|
_leftButtonItem = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setLeftButtonIcon:(UIImage *)leftButtonIcon
|
||||||
|
{
|
||||||
|
_leftButtonIcon = leftButtonIcon;
|
||||||
|
_leftButtonIcon = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIBarButtonItem *)leftButtonItem
|
||||||
|
{
|
||||||
|
if (!_leftButtonItem) {
|
||||||
|
if (_leftButtonIcon) {
|
||||||
|
_leftButtonItem = [[UIBarButtonItem alloc] initWithImage:_leftButtonIcon
|
||||||
|
style:UIBarButtonItemStylePlain
|
||||||
|
target:nil
|
||||||
|
action:nil];
|
||||||
|
} else if (_leftButtonTitle.length) {
|
||||||
|
_leftButtonItem = [[UIBarButtonItem alloc] initWithTitle:_leftButtonTitle
|
||||||
|
style:UIBarButtonItemStylePlain
|
||||||
|
target:nil
|
||||||
|
action:nil];
|
||||||
|
} else {
|
||||||
|
_leftButtonItem = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _leftButtonItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setRightButtonTitle:(NSString *)rightButtonTitle
|
||||||
|
{
|
||||||
|
_rightButtonTitle = rightButtonTitle;
|
||||||
|
_rightButtonItem = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setRightButtonIcon:(UIImage *)rightButtonIcon
|
||||||
|
{
|
||||||
|
_rightButtonIcon = rightButtonIcon;
|
||||||
|
_rightButtonItem = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIBarButtonItem *)rightButtonItem
|
||||||
|
{
|
||||||
|
if (!_rightButtonItem) {
|
||||||
|
if (_rightButtonIcon) {
|
||||||
|
_rightButtonItem = [[UIBarButtonItem alloc] initWithImage:_rightButtonIcon
|
||||||
|
style:UIBarButtonItemStylePlain
|
||||||
|
target:nil
|
||||||
|
action:nil];
|
||||||
|
} else if (_rightButtonTitle.length) {
|
||||||
|
_rightButtonItem = [[UIBarButtonItem alloc] initWithTitle:_rightButtonTitle
|
||||||
|
style:UIBarButtonItemStylePlain
|
||||||
|
target:nil
|
||||||
|
action:nil];
|
||||||
|
} else {
|
||||||
|
_rightButtonItem = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _rightButtonItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
|
@ -21,12 +21,20 @@ RCT_EXPORT_MODULE()
|
||||||
return [[RCTNavItem alloc] init];
|
return [[RCTNavItem alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor)
|
||||||
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(title, NSString)
|
RCT_EXPORT_VIEW_PROPERTY(title, NSString)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString);
|
RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(backButtonTitle, NSString);
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(backButtonIcon, UIImage)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor);
|
RCT_EXPORT_VIEW_PROPERTY(backButtonTitle, NSString)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor);
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor);
|
RCT_EXPORT_VIEW_PROPERTY(leftButtonTitle, NSString)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(leftButtonIcon, UIImage)
|
||||||
|
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(rightButtonIcon, UIImage)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString)
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -315,8 +315,14 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
|
||||||
|
|
||||||
- (void)setContentInset:(UIEdgeInsets)contentInset
|
- (void)setContentInset:(UIEdgeInsets)contentInset
|
||||||
{
|
{
|
||||||
|
CGPoint contentOffset = _scrollView.contentOffset;
|
||||||
|
|
||||||
_contentInset = contentInset;
|
_contentInset = contentInset;
|
||||||
[self setNeedsLayout];
|
[RCTView autoAdjustInsetsForView:self
|
||||||
|
withScrollView:_scrollView
|
||||||
|
updateOffset:NO];
|
||||||
|
|
||||||
|
_scrollView.contentOffset = contentOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scrollToOffset:(CGPoint)offset
|
- (void)scrollToOffset:(CGPoint)offset
|
||||||
|
|
|
@ -41,6 +41,7 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *);
|
||||||
@property (nonatomic, assign) BOOL isBGColorExplicitlySet; // Used to propagate to children
|
@property (nonatomic, assign) BOOL isBGColorExplicitlySet; // Used to propagate to children
|
||||||
@property (nonatomic, strong) UIColor *backgroundColor; // Used to propagate to children
|
@property (nonatomic, strong) UIColor *backgroundColor; // Used to propagate to children
|
||||||
@property (nonatomic, assign) RCTUpdateLifecycle layoutLifecycle;
|
@property (nonatomic, assign) RCTUpdateLifecycle layoutLifecycle;
|
||||||
|
@property (nonatomic, assign) BOOL hasOnLayout;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* isNewView - Used to track the first time the view is introduced into the hierarchy. It is initialized YES, then is
|
* isNewView - Used to track the first time the view is introduced into the hierarchy. It is initialized YES, then is
|
||||||
|
|
|
@ -198,4 +198,6 @@ RCT_CUSTOM_SHADOW_PROPERTY(backgroundColor, UIColor, RCTShadowView)
|
||||||
view.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
view.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_REMAP_SHADOW_PROPERTY(onLayout, hasOnLayout, BOOL)
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -64,7 +64,6 @@
|
||||||
// TODO: find a way to make this less-tightly coupled to navigation controller
|
// TODO: find a way to make this less-tightly coupled to navigation controller
|
||||||
if ([self.parentViewController isKindOfClass:[UINavigationController class]])
|
if ([self.parentViewController isKindOfClass:[UINavigationController class]])
|
||||||
{
|
{
|
||||||
|
|
||||||
[self.navigationController
|
[self.navigationController
|
||||||
setNavigationBarHidden:_navItem.navigationBarHidden
|
setNavigationBarHidden:_navItem.navigationBarHidden
|
||||||
animated:animated];
|
animated:animated];
|
||||||
|
@ -73,33 +72,23 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.navigationItem.title = _navItem.title;
|
|
||||||
|
|
||||||
UINavigationBar *bar = self.navigationController.navigationBar;
|
UINavigationBar *bar = self.navigationController.navigationBar;
|
||||||
if (_navItem.barTintColor) {
|
bar.barTintColor = _navItem.barTintColor;
|
||||||
bar.barTintColor = _navItem.barTintColor;
|
bar.tintColor = _navItem.tintColor;
|
||||||
}
|
|
||||||
if (_navItem.tintColor) {
|
|
||||||
bar.tintColor = _navItem.tintColor;
|
|
||||||
}
|
|
||||||
if (_navItem.titleTextColor) {
|
if (_navItem.titleTextColor) {
|
||||||
[bar setTitleTextAttributes:@{NSForegroundColorAttributeName : _navItem.titleTextColor}];
|
[bar setTitleTextAttributes:@{NSForegroundColorAttributeName : _navItem.titleTextColor}];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_navItem.rightButtonTitle.length > 0) {
|
UINavigationItem *item = self.navigationItem;
|
||||||
self.navigationItem.rightBarButtonItem =
|
item.title = _navItem.title;
|
||||||
[[UIBarButtonItem alloc] initWithTitle:_navItem.rightButtonTitle
|
item.backBarButtonItem = _navItem.backButtonItem;
|
||||||
style:UIBarButtonItemStyleDone
|
if ((item.leftBarButtonItem = _navItem.leftButtonItem)) {
|
||||||
target:self
|
item.leftBarButtonItem.target = self;
|
||||||
action:@selector(handleNavRightButtonTapped)];
|
item.leftBarButtonItem.action = @selector(handleNavLeftButtonTapped);
|
||||||
}
|
}
|
||||||
|
if ((item.rightBarButtonItem = _navItem.rightButtonItem)) {
|
||||||
if (_navItem.backButtonTitle.length > 0) {
|
item.rightBarButtonItem.target = self;
|
||||||
self.navigationItem.backBarButtonItem =
|
item.rightBarButtonItem.action = @selector(handleNavRightButtonTapped);
|
||||||
[[UIBarButtonItem alloc] initWithTitle:_navItem.backButtonTitle
|
|
||||||
style:UIBarButtonItemStylePlain
|
|
||||||
target:nil
|
|
||||||
action:nil];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,6 +103,12 @@
|
||||||
self.view = _wrapperView;
|
self.view = _wrapperView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)handleNavLeftButtonTapped
|
||||||
|
{
|
||||||
|
[_eventDispatcher sendInputEventWithName:@"topNavLeftButtonTap"
|
||||||
|
body:@{@"target":_navItem.reactTag}];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)handleNavRightButtonTapped
|
- (void)handleNavRightButtonTapped
|
||||||
{
|
{
|
||||||
[_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap"
|
[_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap"
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
"graceful-fs": "^3.0.6",
|
"graceful-fs": "^3.0.6",
|
||||||
"image-size": "0.3.5",
|
"image-size": "0.3.5",
|
||||||
"joi": "~5.1.0",
|
"joi": "~5.1.0",
|
||||||
"jstransform": "10.1.0",
|
"jstransform": "11.0.1",
|
||||||
"module-deps": "3.5.6",
|
"module-deps": "3.5.6",
|
||||||
"optimist": "0.6.1",
|
"optimist": "0.6.1",
|
||||||
"promise": "^7.0.0",
|
"promise": "^7.0.0",
|
||||||
|
|
|
@ -166,12 +166,12 @@ describe('Packager', function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(p.addModule.mock.calls[3][0]).toEqual({
|
expect(p.addModule.mock.calls[3][0]).toEqual({
|
||||||
code: 'lol module.exports = ' +
|
code: 'lol module.exports = require("AssetRegistry").registerAsset(' +
|
||||||
JSON.stringify(imgModule) +
|
JSON.stringify(imgModule) +
|
||||||
'; lol',
|
'); lol',
|
||||||
sourceCode: 'module.exports = ' +
|
sourceCode: 'module.exports = require("AssetRegistry").registerAsset(' +
|
||||||
JSON.stringify(imgModule) +
|
JSON.stringify(imgModule) +
|
||||||
';',
|
');',
|
||||||
sourcePath: '/root/img/new_image.png'
|
sourcePath: '/root/img/new_image.png'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -219,7 +219,8 @@ Packager.prototype.generateAssetModule = function(ppackage, module) {
|
||||||
|
|
||||||
ppackage.addAsset(img);
|
ppackage.addAsset(img);
|
||||||
|
|
||||||
var code = 'module.exports = ' + JSON.stringify(img) + ';';
|
var ASSET_TEMPLATE = 'module.exports = require("AssetRegistry").registerAsset(%json);';
|
||||||
|
var code = ASSET_TEMPLATE.replace('%json', JSON.stringify(img));
|
||||||
|
|
||||||
return new ModuleTransport({
|
return new ModuleTransport({
|
||||||
code: code,
|
code: code,
|
||||||
|
|
Loading…
Reference in New Issue