diff --git a/.flowconfig b/.flowconfig
index 28625456d..c4265b9b2 100644
--- a/.flowconfig
+++ b/.flowconfig
@@ -10,7 +10,8 @@
# Ignore react-tools where there are overlaps, but don't ignore anything that
# react-native relies on
.*/node_modules/react-tools/src/vendor/core/ExecutionEnvironment.js
-.*/node_modules/react-tools/src/browser/.*
+.*/node_modules/react-tools/src/browser/eventPlugins/ResponderEventPlugin.js
+.*/node_modules/react-tools/src/browser/ui/React.js
.*/node_modules/react-tools/src/core/ReactInstanceHandles.js
.*/node_modules/react-tools/src/event/EventPropagators.js
diff --git a/Examples/2048/Game2048.js b/Examples/2048/Game2048.js
index 9ffaf42ba..009ba34a7 100644
--- a/Examples/2048/Game2048.js
+++ b/Examples/2048/Game2048.js
@@ -233,7 +233,7 @@ var styles = StyleSheet.create({
tryAgainText: {
color: '#ffffff',
fontSize: 20,
- fontWeight: 'bold',
+ fontWeight: '500',
},
cell: {
width: CELL_SIZE,
@@ -259,7 +259,7 @@ var styles = StyleSheet.create({
fontSize: 24,
color: '#776666',
fontFamily: 'Verdana',
- fontWeight: 'bold',
+ fontWeight: '500',
},
tile2: {
backgroundColor: '#eeeeee',
diff --git a/Examples/Movies/MovieCell.js b/Examples/Movies/MovieCell.js
index 21c90ec24..42781ec24 100644
--- a/Examples/Movies/MovieCell.js
+++ b/Examples/Movies/MovieCell.js
@@ -62,7 +62,7 @@ var styles = StyleSheet.create({
movieTitle: {
flex: 1,
fontSize: 16,
- fontWeight: 'bold',
+ fontWeight: '500',
marginBottom: 2,
},
movieYear: {
diff --git a/Examples/Movies/MovieScreen.js b/Examples/Movies/MovieScreen.js
index 980d51065..7c3ed316b 100644
--- a/Examples/Movies/MovieScreen.js
+++ b/Examples/Movies/MovieScreen.js
@@ -109,7 +109,7 @@ var styles = StyleSheet.create({
movieTitle: {
flex: 1,
fontSize: 16,
- fontWeight: 'bold',
+ fontWeight: '500',
},
rating: {
marginTop: 10,
@@ -119,7 +119,7 @@ var styles = StyleSheet.create({
},
ratingValue: {
fontSize: 28,
- fontWeight: 'bold',
+ fontWeight: '500',
},
mpaaWrapper: {
alignSelf: 'flex-start',
@@ -131,7 +131,7 @@ var styles = StyleSheet.create({
mpaaText: {
fontFamily: 'Palatino',
fontSize: 13,
- fontWeight: 'bold',
+ fontWeight: '500',
},
mainSection: {
flexDirection: 'row',
@@ -148,7 +148,7 @@ var styles = StyleSheet.create({
marginVertical: 10,
},
castTitle: {
- fontWeight: 'bold',
+ fontWeight: '500',
marginBottom: 3,
},
castActor: {
diff --git a/Examples/UIExplorer/ActionSheetIOSExample.js b/Examples/UIExplorer/ActionSheetIOSExample.js
index b6425c507..00e6830af 100644
--- a/Examples/UIExplorer/ActionSheetIOSExample.js
+++ b/Examples/UIExplorer/ActionSheetIOSExample.js
@@ -101,7 +101,7 @@ var ShareActionSheetExample = React.createClass({
var style = StyleSheet.create({
button: {
marginBottom: 10,
- fontWeight: 'bold',
+ fontWeight: '500',
}
});
diff --git a/Examples/UIExplorer/AdSupportIOSExample.js b/Examples/UIExplorer/AdSupportIOSExample.js
index 3c5ada96c..952cb7c8f 100644
--- a/Examples/UIExplorer/AdSupportIOSExample.js
+++ b/Examples/UIExplorer/AdSupportIOSExample.js
@@ -94,6 +94,6 @@ var AdSupportIOSExample = React.createClass({
var styles = StyleSheet.create({
title: {
- fontWeight: 'bold',
+ fontWeight: '500',
},
});
diff --git a/Examples/UIExplorer/DatePickerExample.js b/Examples/UIExplorer/DatePickerExample.js
index 35fa869f3..cf4fa83d9 100644
--- a/Examples/UIExplorer/DatePickerExample.js
+++ b/Examples/UIExplorer/DatePickerExample.js
@@ -149,14 +149,14 @@ var styles = StyleSheet.create({
paddingVertical: 2,
},
label: {
- fontWeight: 'bold',
+ fontWeight: '500',
},
headingContainer: {
padding: 4,
backgroundColor: '#f6f7f8',
},
heading: {
- fontWeight: 'bold',
+ fontWeight: '500',
fontSize: 14,
},
});
diff --git a/Examples/UIExplorer/GeolocationExample.js b/Examples/UIExplorer/GeolocationExample.js
index 82f1c83eb..5d49f341e 100644
--- a/Examples/UIExplorer/GeolocationExample.js
+++ b/Examples/UIExplorer/GeolocationExample.js
@@ -74,6 +74,6 @@ var GeolocationExample = React.createClass({
var styles = StyleSheet.create({
title: {
- fontWeight: 'bold',
+ fontWeight: '500',
},
});
diff --git a/Examples/UIExplorer/NavigatorIOSExample.js b/Examples/UIExplorer/NavigatorIOSExample.js
index 56bb6ab78..fd05caf14 100644
--- a/Examples/UIExplorer/NavigatorIOSExample.js
+++ b/Examples/UIExplorer/NavigatorIOSExample.js
@@ -214,7 +214,7 @@ var styles = StyleSheet.create({
},
rowText: {
fontSize: 17,
- fontWeight: 'bold',
+ fontWeight: '500',
},
});
diff --git a/Examples/UIExplorer/ReactNavigator/BreadcrumbNavSample.js b/Examples/UIExplorer/ReactNavigator/BreadcrumbNavSample.js
index 451da6656..17be12f8d 100644
--- a/Examples/UIExplorer/ReactNavigator/BreadcrumbNavSample.js
+++ b/Examples/UIExplorer/ReactNavigator/BreadcrumbNavSample.js
@@ -254,7 +254,7 @@ var styles = StyleSheet.create({
fontSize: 18,
color: '#666666',
textAlign: 'center',
- fontWeight: 'bold',
+ fontWeight: '500',
lineHeight: 32,
},
filterText: {
diff --git a/Examples/UIExplorer/ReactNavigator/NavigationBarSample.js b/Examples/UIExplorer/ReactNavigator/NavigationBarSample.js
index ddb225552..5096cab71 100644
--- a/Examples/UIExplorer/ReactNavigator/NavigationBarSample.js
+++ b/Examples/UIExplorer/ReactNavigator/NavigationBarSample.js
@@ -107,7 +107,7 @@ var styles = StyleSheet.create({
},
navBarTitleText: {
color: cssVar('fbui-bluegray-60'),
- fontWeight: 'bold',
+ fontWeight: '500',
marginVertical: 9,
},
navBarButtonText: {
diff --git a/Examples/UIExplorer/ReactNavigator/NestedBreadcrumbNavSample.js b/Examples/UIExplorer/ReactNavigator/NestedBreadcrumbNavSample.js
index 263d8b738..20d857472 100644
--- a/Examples/UIExplorer/ReactNavigator/NestedBreadcrumbNavSample.js
+++ b/Examples/UIExplorer/ReactNavigator/NestedBreadcrumbNavSample.js
@@ -176,7 +176,7 @@ var styles = StyleSheet.create({
fontSize: 18,
color: '#666666',
textAlign: 'center',
- fontWeight: 'bold',
+ fontWeight: '500',
lineHeight: 32,
},
filterText: {
diff --git a/Examples/UIExplorer/SliderIOSExample.js b/Examples/UIExplorer/SliderIOSExample.js
index ae6aca144..13eee593a 100644
--- a/Examples/UIExplorer/SliderIOSExample.js
+++ b/Examples/UIExplorer/SliderIOSExample.js
@@ -47,7 +47,7 @@ var styles = StyleSheet.create({
text: {
fontSize: 14,
textAlign: 'center',
- fontWeight: 'bold',
+ fontWeight: '500',
margin: 10,
},
});
diff --git a/Examples/UIExplorer/TextExample.ios.js b/Examples/UIExplorer/TextExample.ios.js
index e29b07da4..e88bc4a0c 100644
--- a/Examples/UIExplorer/TextExample.ios.js
+++ b/Examples/UIExplorer/TextExample.ios.js
@@ -29,7 +29,7 @@ var Entity = React.createClass({
var AttributeToggler = React.createClass({
getInitialState: function() {
- return {fontWeight: 'bold', fontSize: 15};
+ return {fontWeight: '500', fontSize: 15};
},
increaseSize: function() {
this.setState({
@@ -129,9 +129,37 @@ exports.examples = [
title: 'Font Weight',
render: function() {
return (
-
- Move fast and be bold
-
+
+
+ Move fast and be ultralight
+
+
+ Move fast and be light
+
+
+ Move fast and be normal
+
+
+ Move fast and be bold
+
+
+ Move fast and be ultrabold
+
+
+ );
+ },
+}, {
+ title: 'Font Style',
+ render: function() {
+ return (
+
+
+ Normal text
+
+
+ Italic text
+
+
);
},
}, {
@@ -279,7 +307,7 @@ var styles = StyleSheet.create({
backgroundColor: 'rgba(100, 100, 100, 0.3)'
},
entity: {
- fontWeight: 'bold',
+ fontWeight: '500',
color: '#527fe4',
},
});
diff --git a/Examples/UIExplorer/TouchableExample.js b/Examples/UIExplorer/TouchableExample.js
index 35d86aaf8..6b3c611ed 100644
--- a/Examples/UIExplorer/TouchableExample.js
+++ b/Examples/UIExplorer/TouchableExample.js
@@ -187,7 +187,7 @@ var styles = StyleSheet.create({
backgroundColor: '#f9f9f9',
},
textBlock: {
- fontWeight: 'bold',
+ fontWeight: '500',
color: 'blue',
},
});
diff --git a/Examples/UIExplorer/UIExplorer/AppDelegate.m b/Examples/UIExplorer/UIExplorer/AppDelegate.m
index 9b6bc835f..c25414370 100644
--- a/Examples/UIExplorer/UIExplorer/AppDelegate.m
+++ b/Examples/UIExplorer/UIExplorer/AppDelegate.m
@@ -9,7 +9,6 @@
#import "AppDelegate.h"
-#import "RCTDevelopmentViewController.h"
#import "RCTRootView.h"
@implementation AppDelegate
@@ -42,7 +41,7 @@
rootView.moduleName = @"UIExplorerApp";
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
- UIViewController *rootViewController = [[RCTDevelopmentViewController alloc] init];
+ UIViewController *rootViewController = [[UIViewController alloc] init];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
diff --git a/Examples/UIExplorer/UIExplorerBlock.js b/Examples/UIExplorer/UIExplorerBlock.js
index 66222fd05..7d2de002f 100644
--- a/Examples/UIExplorer/UIExplorerBlock.js
+++ b/Examples/UIExplorer/UIExplorerBlock.js
@@ -76,7 +76,7 @@ var styles = StyleSheet.create({
},
titleText: {
fontSize: 14,
- fontWeight: 'bold',
+ fontWeight: '500',
},
descriptionText: {
fontSize: 14,
diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js
index 73f016cdd..a13da34dc 100644
--- a/Examples/UIExplorer/UIExplorerList.js
+++ b/Examples/UIExplorer/UIExplorerList.js
@@ -204,7 +204,7 @@ var styles = StyleSheet.create({
backgroundColor: 'white',
},
sectionHeaderTitle: {
- fontWeight: 'bold',
+ fontWeight: '500',
fontSize: 11,
},
row: {
@@ -220,7 +220,7 @@ var styles = StyleSheet.create({
},
rowTitleText: {
fontSize: 17,
- fontWeight: 'bold',
+ fontWeight: '500',
},
rowDetailText: {
fontSize: 15,
diff --git a/Examples/UIExplorer/UIExplorerTitle.js b/Examples/UIExplorer/UIExplorerTitle.js
index 2016ee085..7a8895413 100644
--- a/Examples/UIExplorer/UIExplorerTitle.js
+++ b/Examples/UIExplorer/UIExplorerTitle.js
@@ -42,7 +42,7 @@ var styles = StyleSheet.create({
},
text: {
fontSize: 19,
- fontWeight: 'bold',
+ fontWeight: '500',
},
});
diff --git a/Examples/UIExplorer/WebViewExample.js b/Examples/UIExplorer/WebViewExample.js
index 3dcf7c8b7..a471a7807 100644
--- a/Examples/UIExplorer/WebViewExample.js
+++ b/Examples/UIExplorer/WebViewExample.js
@@ -239,7 +239,7 @@ var styles = StyleSheet.create({
},
errorTextTitle: {
fontSize: 15,
- fontWeight: 'bold',
+ fontWeight: '500',
marginBottom: 10,
},
errorText: {
diff --git a/IntegrationTests/IntegrationTestsApp.js b/IntegrationTests/IntegrationTestsApp.js
index de1703528..dbb5dde83 100644
--- a/IntegrationTests/IntegrationTestsApp.js
+++ b/IntegrationTests/IntegrationTestsApp.js
@@ -81,7 +81,7 @@ var styles = StyleSheet.create({
padding: 10,
},
testName: {
- fontWeight: 'bold',
+ fontWeight: '500',
},
separator: {
height: 1,
diff --git a/Libraries/AppStateIOS/AppStateIOS.ios.js b/Libraries/AppStateIOS/AppStateIOS.ios.js
index af6893534..48db8415b 100644
--- a/Libraries/AppStateIOS/AppStateIOS.ios.js
+++ b/Libraries/AppStateIOS/AppStateIOS.ios.js
@@ -21,28 +21,92 @@ var DEVICE_APPSTATE_EVENT = 'appStateDidChange';
var _appStateHandlers = {};
-class AppStateIOS {
+/**
+ * `AppStateIOS` can tell you if the app is in the foreground or background,
+ * and notify you when the state changes.
+ *
+ * AppStateIOS is frequently used to determine the intent and proper behavior when
+ * handling push notifications.
+ *
+ * ### iOS App States
+ *
+ * - `active` - The app is running in the foreground
+ * - `background` - The app is running in the background. The user is either
+ * in another app or on the home screen
+ * - `inactive` - This is a transition state that currently never happens for
+ * typical React Native apps.
+ *
+ * For more information, see
+ * [Apple's documentation](https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html)
+ *
+ * ### Basic Usage
+ *
+ * To see the current state, you can check `AppStateIOS.currentState`, which
+ * will be kept up-to-date. However, `currentState` will be null at launch
+ * while `AppStateIOS` retrieves it over the bridge.
+ *
+ * ```
+ * getInitialState: function() {
+ * return {
+ * currentAppState: AppStateIOS.currentState,
+ * };
+ * },
+ * componentDidMount: function() {
+ * AppStateIOS.addEventListener('change', this._handleAppStateChange);
+ * },
+ * componentWillUnmount: function() {
+ * AppStateIOS.removeEventListener('change', this._handleAppStateChange);
+ * },
+ * _handleAppStateChange: function(currentAppState) {
+ * this.setState({ currentAppState, });
+ * },
+ * render: function() {
+ * return (
+ * Current state is: {this.state.currentAppState}
+ * );
+ * },
+ * ```
+ *
+ * This example will only ever appear to say "Current state is: active" because
+ * the app is only visible to the user when in the `active` state, and the null
+ * state will happen only momentarily.
+ */
- static addEventListener(type, handler) {
+var AppStateIOS = {
+
+ /**
+ * Add a handler to AppState changes by listening to the `change` event type
+ * and providing the handler
+ */
+ addEventListener: function(
+ type: string,
+ handler: Function
+ ) {
_appStateHandlers[handler] = RCTDeviceEventEmitter.addListener(
DEVICE_APPSTATE_EVENT,
(appStateData) => {
handler(appStateData.app_state);
}
);
- }
+ },
- static removeEventListener(type, handler) {
+ /**
+ * Remove a handler by passing the `change` event type and the handler
+ */
+ removeEventListener: function(
+ type: string,
+ handler: Function
+ ) {
if (!_appStateHandlers[handler]) {
return;
}
_appStateHandlers[handler].remove();
_appStateHandlers[handler] = null;
- }
+ },
-}
+ currentState: (null : ?String),
-AppStateIOS.currentState = null;
+};
RCTDeviceEventEmitter.addListener(
DEVICE_APPSTATE_EVENT,
diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js
index 882adf420..e34c13883 100644
--- a/Libraries/Components/View/View.js
+++ b/Libraries/Components/View/View.js
@@ -7,7 +7,6 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule View
- * @flow
*/
'use strict';
@@ -40,18 +39,8 @@ var stylePropType = StyleSheetPropType(ViewStylePropTypes);
*
* ```
*
- * By default, `View`s have a primary flex direction of 'column', so children
- * will stack up vertically by default. `View`s also expand to fill the parent
- * in the direction of the parent's flex direction by default, so in the case of
- * a default parent (flexDirection: 'column'), the children will fill the width,
- * but not the height.
- *
- * Many library components can be treated like plain `Views` in many cases, for
- * example passing them children, setting style, etc.
- *
* `View`s are designed to be used with `StyleSheet`s for clarity and
- * performance, although inline styles are also supported. It is common for
- * `StyleSheet`s to be combined dynamically. See `StyleSheet.js` for more info.
+ * performance, although inline styles are also supported.
*/
var View = React.createClass({
mixins: [NativeMethodsMixin],
@@ -67,10 +56,18 @@ var View = React.createClass({
propTypes: {
/**
- * When true, indicates that the view is an accessibility element
+ * When true, indicates that the view is an accessibility element. By default,
+ * all the touchable elements are accessible.
*/
accessible: PropTypes.bool,
+ /**
+ * Overrides the text that's read by the screen reader when the user interacts
+ * with the element. By default, the label is constructed by traversing all the
+ * children and accumulating all the Text nodes separated by space.
+ */
+ accessibilityLabel: PropTypes.string,
+
/**
* Used to locate this view in end-to-end tests.
*/
@@ -78,16 +75,16 @@ var View = React.createClass({
/**
* For most touch interactions, you'll simply want to wrap your component in
- * `TouchableHighlight.js`. Check out `Touchable.js` and
- * `ScrollResponder.js` for more discussion.
+ * `TouchableHighlight` or `TouchableOpacity`. Check out `Touchable.js`,
+ * `ScrollResponder.js` and `ResponderEventPlugin.js` for more discussion.
*/
+ onMoveShouldSetResponder: PropTypes.func,
onResponderGrant: PropTypes.func,
- onResponderReject: PropTypes.func,
onResponderMove: PropTypes.func,
+ onResponderReject: PropTypes.func,
onResponderRelease: PropTypes.func,
onResponderTerminate: PropTypes.func,
onResponderTerminationRequest: PropTypes.func,
- onMoveShouldSetResponder: PropTypes.func,
onStartShouldSetResponder: PropTypes.func,
onStartShouldSetResponderCapture: PropTypes.func,
@@ -95,12 +92,25 @@ var View = React.createClass({
* 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:
*
- * .cantTouchThis * {
- * pointer-events: auto;
- * }
- * .cantTouchThis {
- * pointer-events: none;
- * }
+ * ```
+ * .box-none {
+ * pointer-events: none;
+ * }
+ * .box-none * {
+ * pointer-events: all;
+ * }
+ * ```
+ *
+ * `box-only` is the equivalent of
+ *
+ * ```
+ * .box-only {
+ * pointer-events: all;
+ * }
+ * .box-only * {
+ * pointer-events: none;
+ * }
+ * ```
*
* But since `pointerEvents` does not affect layout/appearance, and we are
* already deviating from the spec by adding additional modes, we opt to not
@@ -114,11 +124,6 @@ var View = React.createClass({
'box-only',
'auto',
]),
-
- /**
- * Used to style and layout the `View`. See `StyleSheet.js` and
- * `ViewStylePropTypes.js` for more info.
- */
style: stylePropType,
/**
diff --git a/Libraries/Network/NetInfo.js b/Libraries/Network/NetInfo.js
index 3b15cf541..d98b997ca 100644
--- a/Libraries/Network/NetInfo.js
+++ b/Libraries/Network/NetInfo.js
@@ -21,18 +21,25 @@ type ChangeEventName = $Enum<{
change: string;
}>;
+type ReachabilityStateIOS = $Enum<{
+ cell: string;
+ none: string;
+ unknown: string;
+ wifi: string;
+}>;
+
/**
* NetInfo exposes info about online/offline status
*
- * == iOS Reachability
+ * ### reachabilityIOS
*
* Asyncronously determine if the device is online and on a cellular network.
*
- * - "none" - device is offline
- * - "wifi" - device is online and connected via wifi, or is the iOS simulator
- * - "cell" - device is connected via Edge, 3G, WiMax, or LTE
- * - "unknown" - error case and the network status is unknown
+ * - `none` - device is offline
+ * - `wifi` - device is online and connected via wifi, or is the iOS simulator
+ * - `cell` - device is connected via Edge, 3G, WiMax, or LTE
+ * - `unknown` - error case and the network status is unknown
*
* ```
* NetInfo.reachabilityIOS.fetch().done((reach) => {
@@ -50,11 +57,37 @@ type ChangeEventName = $Enum<{
* handleFirstReachabilityChange
* );
* ```
+ *
+ * ### isConnected
+ *
+ * Available on all platforms. Asyncronously fetch a boolean to determine
+ * internet connectivity.
+ *
+ * ```
+ * NetInfo.isConnected.fetch().done((isConnected) => {
+ * console.log('First, is ' + (isConnected ? 'online' : 'offline'));
+ * });
+ * function handleFirstConnectivityChange(isConnected) {
+ * console.log('Then, is ' + (isConnected ? 'online' : 'offline'));
+ * NetInfo.isConnected.removeEventListener(
+ * 'change',
+ * handleFirstConnectivityChange
+ * );
+ * }
+ * NetInfo.isConnected.addEventListener(
+ * 'change',
+ * handleFirstConnectivityChange
+ * );
+ * ```
*/
var NetInfo = {};
if (RCTReachability) {
+
+ // RCTReachability is exposed, so this is an iOS-like environment and we will
+ // expose reachabilityIOS
+
var _reachabilitySubscriptions = {};
NetInfo.reachabilityIOS = {
@@ -84,7 +117,7 @@ if (RCTReachability) {
fetch: function(): Promise {
return new Promise((resolve, reject) => {
RCTReachability.getCurrentReachability(
- (resp) => {
+ function(resp) {
resolve(resp.network_reachability);
},
reject
@@ -93,53 +126,42 @@ if (RCTReachability) {
},
};
- /**
- *
- * == NetInfo.isConnected
- *
- * Available on all platforms. Asyncronously fetch a boolean to determine
- * internet connectivity.
- *
- * ```
- * NetInfo.isConnected.fetch().done((isConnected) => {
- * console.log('First, is ' + (isConnected ? 'online' : 'offline'));
- * });
- * function handleFirstConnectivityChange(isConnected) {
- * console.log('Then, is ' + (isConnected ? 'online' : 'offline'));
- * NetInfo.isConnected.removeEventListener(
- * 'change',
- * handleFirstConnectivityChange
- * );
- * }
- * NetInfo.isConnected.addEventListener(
- * 'change',
- * handleFirstConnectivityChange
- * );
- * ```
- *
- */
var _isConnectedSubscriptions = {};
+
+ var _iosReachabilityIsConnected = function(
+ reachability: ReachabilityStateIOS
+ ): bool {
+ return reachability !== 'none' &&
+ reachability !== 'unknown';
+ };
+
NetInfo.isConnected = {
addEventListener: function (
eventName: ChangeEventName,
handler: Function
): void {
_isConnectedSubscriptions[handler] = (reachability) => {
- handler(reachability !== 'none');
+ handler(_iosReachabilityIsConnected(reachability));
};
- NetInfo.reachabilityIOS.addEventListener(eventName, _isConnectedSubscriptions[handler]);
+ NetInfo.reachabilityIOS.addEventListener(
+ eventName,
+ _isConnectedSubscriptions[handler]
+ );
},
removeEventListener: function(
eventName: ChangeEventName,
handler: Function
): void {
- NetInfo.reachabilityIOS.removeEventListener(eventName, _isConnectedSubscriptions[handler]);
+ NetInfo.reachabilityIOS.removeEventListener(
+ eventName,
+ _isConnectedSubscriptions[handler]
+ );
},
fetch: function(): Promise {
return NetInfo.reachabilityIOS.fetch().then(
- (reachability) => reachability !== 'none'
+ (reachability) => _iosReachabilityIsConnected(reachability)
);
},
};
diff --git a/Libraries/ReactIOS/IOSDefaultEventPluginOrder.js b/Libraries/ReactIOS/IOSDefaultEventPluginOrder.js
index b23366920..80098fc41 100644
--- a/Libraries/ReactIOS/IOSDefaultEventPluginOrder.js
+++ b/Libraries/ReactIOS/IOSDefaultEventPluginOrder.js
@@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule IOSDefaultEventPluginOrder
+ * @flow
*/
'use strict';
diff --git a/Libraries/ReactIOS/IOSNativeBridgeEventPlugin.js b/Libraries/ReactIOS/IOSNativeBridgeEventPlugin.js
index c41ae82ab..b585db5a7 100644
--- a/Libraries/ReactIOS/IOSNativeBridgeEventPlugin.js
+++ b/Libraries/ReactIOS/IOSNativeBridgeEventPlugin.js
@@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule IOSNativeBridgeEventPlugin
+ * @flow
*/
"use strict";
@@ -51,10 +52,11 @@ var IOSNativeBridgeEventPlugin = {
* @see {EventPluginHub.extractEvents}
*/
extractEvents: function(
- topLevelType,
- topLevelTarget,
- topLevelTargetID,
- nativeEvent) {
+ topLevelType: string,
+ topLevelTarget: EventTarget,
+ topLevelTargetID: string,
+ nativeEvent: Event
+ ): ?Object {
var bubbleDispatchConfig = customBubblingEventTypes[topLevelType];
var directDispatchConfig = customDirectEventTypes[topLevelType];
var event = SyntheticEvent.getPooled(
diff --git a/Libraries/ReactIOS/NativeMethodsMixin.js b/Libraries/ReactIOS/NativeMethodsMixin.js
index 3dbe233ee..ec72a0b4f 100644
--- a/Libraries/ReactIOS/NativeMethodsMixin.js
+++ b/Libraries/ReactIOS/NativeMethodsMixin.js
@@ -7,10 +7,10 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NativeMethodsMixin
+ * @flow
*/
'use strict';
-var NativeModules = require('NativeModules');
var NativeModules = require('NativeModules');
var RCTPOPAnimationManager = NativeModules.POPAnimationManager;
var RCTUIManager = NativeModules.UIManager;
@@ -20,7 +20,26 @@ var flattenStyle = require('flattenStyle');
var invariant = require('invariant');
var mergeFast = require('mergeFast');
-var animationIDInvariant = function(funcName, anim) {
+type MeasureOnSuccessCallback = (
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+ pageX: number,
+ pageY: number
+) => void
+
+type MeasureLayoutOnSuccessCallback = (
+ left: number,
+ top: number,
+ width: number,
+ height: number
+) => void
+
+var animationIDInvariant = function(
+ funcName: string,
+ anim: number
+) {
invariant(
anim,
funcName + ' must be called with a valid animation ID returned from' +
@@ -29,21 +48,25 @@ var animationIDInvariant = function(funcName, anim) {
};
var NativeMethodsMixin = {
- addAnimation: function(anim, callback) {
+ addAnimation: function(anim: number, callback?: (finished: bool) => void) {
animationIDInvariant('addAnimation', anim);
RCTPOPAnimationManager.addAnimation(this.getNodeHandle(), anim, callback);
},
- removeAnimation: function(anim) {
+ removeAnimation: function(anim: number) {
animationIDInvariant('removeAnimation', anim);
RCTPOPAnimationManager.removeAnimation(this.getNodeHandle(), anim);
},
- measure: function(callback) {
+ measure: function(callback: MeasureOnSuccessCallback) {
RCTUIManager.measure(this.getNodeHandle(), callback);
},
- measureLayout: function(relativeToNativeNode, onSuccess, onFail) {
+ measureLayout: function(
+ relativeToNativeNode: number,
+ onSuccess: MeasureLayoutOnSuccessCallback,
+ onFail: () => void /* currently unused */
+ ) {
RCTUIManager.measureLayout(
this.getNodeHandle(),
relativeToNativeNode,
@@ -57,7 +80,7 @@ var NativeMethodsMixin = {
* in future diff process, this means that if you do not include them in the
* next render, they will remain active.
*/
- setNativeProps: function(nativeProps) {
+ setNativeProps: function(nativeProps: Object) {
// nativeProps contains a style attribute that's going to be flattened
// and all the attributes expanded in place. In order to make this
// process do as few allocations and copies as possible, we return
@@ -111,15 +134,19 @@ function throwOnStylesProp(component, props) {
}
}
if (__DEV__) {
+ // hide this from Flow since we can't define these properties outside of
+ // __DEV__ without actually implementing them (setting them to undefined
+ // isn't allowed by ReactClass)
+ var NativeMethodsMixin_DEV = (NativeMethodsMixin: any);
invariant(
- !NativeMethodsMixin.componentWillMount &&
- !NativeMethodsMixin.componentWillReceiveProps,
+ !NativeMethodsMixin_DEV.componentWillMount &&
+ !NativeMethodsMixin_DEV.componentWillReceiveProps,
'Do not override existing functions.'
);
- NativeMethodsMixin.componentWillMount = function () {
+ NativeMethodsMixin_DEV.componentWillMount = function () {
throwOnStylesProp(this, this.props);
};
- NativeMethodsMixin.componentWillReceiveProps = function (newProps) {
+ NativeMethodsMixin_DEV.componentWillReceiveProps = function (newProps) {
throwOnStylesProp(this, newProps);
};
}
diff --git a/Libraries/ReactIOS/React.js b/Libraries/ReactIOS/React.js
index 34f190138..ce9de0852 100644
--- a/Libraries/ReactIOS/React.js
+++ b/Libraries/ReactIOS/React.js
@@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule React
+ * @flow
*/
"use strict";
diff --git a/Libraries/ReactIOS/ReactIOS.js b/Libraries/ReactIOS/ReactIOS.js
index e1c948065..028577429 100644
--- a/Libraries/ReactIOS/ReactIOS.js
+++ b/Libraries/ReactIOS/ReactIOS.js
@@ -7,8 +7,8 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactIOS
+ * @flow
*/
-
"use strict";
var ReactChildren = require('ReactChildren');
@@ -51,7 +51,7 @@ var resolveDefaultProps = function(element) {
};
// Experimental optimized element creation
-var augmentElement = function(element) {
+var augmentElement = function(element: ReactElement) {
if (__DEV__) {
invariant(
false,
@@ -67,7 +67,7 @@ var augmentElement = function(element) {
return element;
};
-var render = function(component, mountInto) {
+var render = function(component: ReactComponent, mountInto: number) {
ReactIOSMount.renderComponent(component, mountInto);
};
diff --git a/Libraries/ReactIOS/ReactIOSComponentEnvironment.js b/Libraries/ReactIOS/ReactIOSComponentEnvironment.js
index 664ebb6ec..0dc0b1d1c 100644
--- a/Libraries/ReactIOS/ReactIOSComponentEnvironment.js
+++ b/Libraries/ReactIOS/ReactIOSComponentEnvironment.js
@@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactIOSComponentEnvironment
+ * @flow
*/
'use strict';
diff --git a/Libraries/ReactIOS/ReactIOSComponentMixin.js b/Libraries/ReactIOS/ReactIOSComponentMixin.js
index d8044e5eb..94c708a43 100644
--- a/Libraries/ReactIOS/ReactIOSComponentMixin.js
+++ b/Libraries/ReactIOS/ReactIOSComponentMixin.js
@@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactIOSComponentMixin
+ * @flow
*/
'use strict';
diff --git a/Libraries/ReactIOS/ReactIOSDOMIDOperations.js b/Libraries/ReactIOS/ReactIOSDOMIDOperations.js
index 7e266530a..7d421442f 100644
--- a/Libraries/ReactIOS/ReactIOSDOMIDOperations.js
+++ b/Libraries/ReactIOS/ReactIOSDOMIDOperations.js
@@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactIOSDOMIDOperations
- * @typechecks static-only
+ * @flow
*/
"use strict";
diff --git a/Libraries/ReactIOS/ReactIOSDefaultInjection.js b/Libraries/ReactIOS/ReactIOSDefaultInjection.js
index dd67987cc..d6b81da2c 100644
--- a/Libraries/ReactIOS/ReactIOSDefaultInjection.js
+++ b/Libraries/ReactIOS/ReactIOSDefaultInjection.js
@@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactIOSDefaultInjection
+ * @flow
*/
"use strict";
diff --git a/Libraries/ReactIOS/createReactIOSNativeComponentClass.js b/Libraries/ReactIOS/createReactIOSNativeComponentClass.js
index 19217cb72..5a5af04dc 100644
--- a/Libraries/ReactIOS/createReactIOSNativeComponentClass.js
+++ b/Libraries/ReactIOS/createReactIOSNativeComponentClass.js
@@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule createReactIOSNativeComponentClass
+ * @flow
*/
"use strict";
@@ -14,11 +15,19 @@
var ReactElement = require('ReactElement');
var ReactIOSNativeComponent = require('ReactIOSNativeComponent');
+// See also ReactIOSNativeComponent
+type ReactIOSNativeComponentViewConfig = {
+ validAttributes: Object;
+ uiViewClassName: string;
+}
+
/**
* @param {string} config iOS View configuration.
* @private
*/
-var createReactIOSNativeComponentClass = function(viewConfig) {
+var createReactIOSNativeComponentClass = function(
+ viewConfig: ReactIOSNativeComponentViewConfig
+): Function { // returning Function is lossy :/
var Constructor = function(element) {
this._currentElement = element;
diff --git a/Libraries/ReactIOS/diffRawProperties.js b/Libraries/ReactIOS/diffRawProperties.js
index 2be542aaa..3a5de284f 100644
--- a/Libraries/ReactIOS/diffRawProperties.js
+++ b/Libraries/ReactIOS/diffRawProperties.js
@@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule diffRawProperties
+ * @flow
*/
'use strict';
@@ -21,34 +22,41 @@
* previous. These properties are as supplied to component construction.
* @return {?object}
*/
-function diffRawProperties(updatePayload, prevProps, nextProps, validAttributes) {
+function diffRawProperties(
+ updatePayload: ?Object,
+ prevProps: ?Object,
+ nextProps: ?Object,
+ validAttributes: Object
+): ?Object {
var validAttributeConfig;
var nextProp;
var prevProp;
var isScalar;
var shouldUpdate;
- for (var propKey in nextProps) {
- validAttributeConfig = validAttributes[propKey];
- if (!validAttributeConfig) {
- continue; // not a valid native prop
- }
- prevProp = prevProps && prevProps[propKey];
- nextProp = nextProps[propKey];
- if (prevProp !== nextProp) {
- // 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
- // creation with any attribute that is not scalar, but we should
- // eventually even reject those unless they are properly configured.
- isScalar = typeof nextProp !== 'object' || nextProp === null;
- shouldUpdate = isScalar ||
- !prevProp ||
- validAttributeConfig.diff &&
- validAttributeConfig.diff(prevProp, nextProp);
+ if (nextProps) {
+ for (var propKey in nextProps) {
+ validAttributeConfig = validAttributes[propKey];
+ if (!validAttributeConfig) {
+ continue; // not a valid native prop
+ }
+ prevProp = prevProps && prevProps[propKey];
+ nextProp = nextProps[propKey];
+ if (prevProp !== nextProp) {
+ // 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
+ // creation with any attribute that is not scalar, but we should
+ // eventually even reject those unless they are properly configured.
+ isScalar = typeof nextProp !== 'object' || nextProp === null;
+ shouldUpdate = isScalar ||
+ !prevProp ||
+ validAttributeConfig.diff &&
+ validAttributeConfig.diff(prevProp, nextProp);
- if (shouldUpdate) {
- updatePayload = updatePayload || {};
- updatePayload[propKey] = nextProp;
+ if (shouldUpdate) {
+ updatePayload = updatePayload || {};
+ updatePayload[propKey] = nextProp;
+ }
}
}
}
@@ -56,31 +64,33 @@ function diffRawProperties(updatePayload, prevProps, nextProps, validAttributes)
// Also iterate through all the previous props to catch any that have been
// removed and make sure native gets the signal so it can reset them to the
// default.
- for (var propKey in prevProps) {
- validAttributeConfig = validAttributes[propKey];
- if (!validAttributeConfig) {
- continue; // not a valid native prop
- }
- if (updatePayload && updatePayload[propKey] !== undefined) {
- continue; // Prop already specified
- }
- prevProp = prevProps[propKey];
- nextProp = nextProps && nextProps[propKey];
- if (prevProp !== nextProp) {
- if (nextProp === undefined) {
- nextProp = null; // null is a sentinel we explicitly send to native
+ if (prevProps) {
+ for (var propKey in prevProps) {
+ validAttributeConfig = validAttributes[propKey];
+ if (!validAttributeConfig) {
+ continue; // not a valid native prop
}
- // 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
- // creation with any attribute that is not scalar, but we should
- // eventually even reject those unless they are properly configured.
- isScalar = typeof nextProp !== 'object' || nextProp === null;
- shouldUpdate = isScalar && prevProp !== nextProp ||
- validAttributeConfig.diff &&
- validAttributeConfig.diff(prevProp, nextProp);
- if (shouldUpdate) {
- updatePayload = updatePayload || {};
- updatePayload[propKey] = nextProp;
+ if (updatePayload && updatePayload[propKey] !== undefined) {
+ continue; // Prop already specified
+ }
+ prevProp = prevProps[propKey];
+ nextProp = nextProps && nextProps[propKey];
+ if (prevProp !== nextProp) {
+ if (nextProp === undefined) {
+ nextProp = null; // null is a sentinel we explicitly send to native
+ }
+ // 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
+ // creation with any attribute that is not scalar, but we should
+ // eventually even reject those unless they are properly configured.
+ isScalar = typeof nextProp !== 'object' || nextProp === null;
+ shouldUpdate = isScalar && prevProp !== nextProp ||
+ validAttributeConfig.diff &&
+ validAttributeConfig.diff(prevProp, nextProp);
+ if (shouldUpdate) {
+ updatePayload = updatePayload || {};
+ updatePayload[propKey] = nextProp;
+ }
}
}
}
diff --git a/Libraries/Text/RCTShadowText.h b/Libraries/Text/RCTShadowText.h
index 61c8f2e73..b14a623c8 100644
--- a/Libraries/Text/RCTShadowText.h
+++ b/Libraries/Text/RCTShadowText.h
@@ -21,6 +21,7 @@ extern NSString *const RCTReactTagAttributeName;
@property (nonatomic, copy) NSString *fontFamily;
@property (nonatomic, assign) CGFloat fontSize;
@property (nonatomic, copy) NSString *fontWeight;
+@property (nonatomic, copy) NSString *fontStyle;
@property (nonatomic, assign) BOOL isHighlighted;
@property (nonatomic, assign) CGFloat lineHeight;
@property (nonatomic, assign) NSInteger maxNumberOfLines;
diff --git a/Libraries/Text/RCTShadowText.m b/Libraries/Text/RCTShadowText.m
index 8899fea87..f00350205 100644
--- a/Libraries/Text/RCTShadowText.m
+++ b/Libraries/Text/RCTShadowText.m
@@ -50,12 +50,14 @@ static css_dim_t RCTMeasure(void *context, float width)
{
return [self _attributedStringWithFontFamily:nil
fontSize:0
- fontWeight:nil];
+ fontWeight:nil
+ fontStyle:nil];
}
- (NSAttributedString *)_attributedStringWithFontFamily:(NSString *)fontFamily
fontSize:(CGFloat)fontSize
fontWeight:(NSString *)fontWeight
+ fontStyle:(NSString *)fontStyle
{
if (![self isTextDirty] && _cachedAttributedString) {
return _cachedAttributedString;
@@ -67,6 +69,9 @@ static css_dim_t RCTMeasure(void *context, float width)
if (_fontWeight) {
fontWeight = _fontWeight;
}
+ if (_fontStyle) {
+ fontStyle = _fontStyle;
+ }
if (_fontFamily) {
fontFamily = _fontFamily;
}
@@ -75,7 +80,7 @@ static css_dim_t RCTMeasure(void *context, float width)
for (RCTShadowView *child in [self reactSubviews]) {
if ([child isKindOfClass:[RCTShadowText class]]) {
RCTShadowText *shadowText = (RCTShadowText *)child;
- [attributedString appendAttributedString:[shadowText _attributedStringWithFontFamily:fontFamily fontSize:fontSize fontWeight:fontWeight]];
+ [attributedString appendAttributedString:[shadowText _attributedStringWithFontFamily:fontFamily fontSize:fontSize fontWeight:fontWeight fontStyle:fontStyle]];
} else if ([child isKindOfClass:[RCTShadowRawText class]]) {
RCTShadowRawText *shadowRawText = (RCTShadowRawText *)child;
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:[shadowRawText text] ?: @""]];
@@ -96,7 +101,7 @@ static css_dim_t RCTMeasure(void *context, float width)
[self _addAttribute:NSBackgroundColorAttributeName withValue:self.textBackgroundColor toAttributedString:attributedString];
}
- _font = [RCTConvert UIFont:nil withFamily:fontFamily size:@(fontSize) weight:fontWeight];
+ _font = [RCTConvert UIFont:nil withFamily:fontFamily size:@(fontSize) weight:fontWeight style:fontStyle];
[self _addAttribute:NSFontAttributeName withValue:_font toAttributedString:attributedString];
[self _addAttribute:RCTReactTagAttributeName withValue:self.reactTag toAttributedString:attributedString];
[self _setParagraphStyleOnAttributedString:attributedString];
@@ -110,7 +115,7 @@ static css_dim_t RCTMeasure(void *context, float width)
- (UIFont *)font
{
- return _font ?: [RCTConvert UIFont:nil withFamily:_fontFamily size:@(_fontSize) weight:_fontWeight];
+ return _font ?: [RCTConvert UIFont:nil withFamily:_fontFamily size:@(_fontSize) weight:_fontWeight style:_fontStyle];
}
- (void)_addAttribute:(NSString *)attribute withValue:(id)attributeValue toAttributedString:(NSMutableAttributedString *)attributedString
diff --git a/Libraries/react-native/react-native-interface.js b/Libraries/react-native/react-native-interface.js
index 2ace79292..6408526b8 100644
--- a/Libraries/react-native/react-native-interface.js
+++ b/Libraries/react-native/react-native-interface.js
@@ -12,3 +12,7 @@
// see also react-native.js
declare var __DEV__: boolean;
+
+declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: any; /*?{
+ inject: ?((stuff: Object) => void)
+};*/
diff --git a/ReactKit/Base/RCTConvert.h b/ReactKit/Base/RCTConvert.h
index f26dea827..fb4f51883 100644
--- a/ReactKit/Base/RCTConvert.h
+++ b/ReactKit/Base/RCTConvert.h
@@ -12,6 +12,7 @@
#import "Layout.h"
#import "RCTAnimationType.h"
+#import "RCTLog.h"
#import "RCTPointerEvents.h"
/**
@@ -69,8 +70,13 @@
+ (UIFont *)UIFont:(UIFont *)font withSize:(id)json;
+ (UIFont *)UIFont:(UIFont *)font withWeight:(id)json;
++ (UIFont *)UIFont:(UIFont *)font withStyle:(id)json;
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json;
-+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json size:(id)json weight:(id)json;
++ (UIFont *)UIFont:(UIFont *)font
+ withFamily:(id)family
+ size:(id)size
+ weight:(id)weight
+ style:(id)style;
+ (NSArray *)NSStringArray:(id)json;
+ (NSArray *)NSURLArray:(id)json;
diff --git a/ReactKit/Base/RCTConvert.m b/ReactKit/Base/RCTConvert.m
index dd46fb4af..01300e306 100644
--- a/ReactKit/Base/RCTConvert.m
+++ b/ReactKit/Base/RCTConvert.m
@@ -11,8 +11,6 @@
#import
-#import "RCTLog.h"
-
@implementation RCTConvert
RCT_CONVERTER(BOOL, BOOL, boolValue)
@@ -135,7 +133,9 @@ RCT_CGSTRUCT_CONVERTER(CATransform3D, (@[
@"m41", @"m42", @"m43", @"m44"
]), nil)
-RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"]), nil)
+RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[
+ @"a", @"b", @"c", @"d", @"tx", @"ty"
+]), nil)
+ (UIColor *)UIColor:(id)json
{
@@ -364,7 +364,8 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty
} else if (json && ![json isKindOfClass:[NSNull class]]) {
- RCTLogError(@"Expected NSArray, NSDictionary or NSString for UIColor, received %@: %@", [json class], json);
+ RCTLogError(@"Expected NSArray, NSDictionary or NSString for UIColor, \
+ received %@: %@", [json class], json);
}
// Default color
@@ -418,100 +419,163 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty
return [self UIImage:json].CGImage;
}
+#ifndef __IPHONE_8_2
+
+// These constants are defined in iPhone SDK 8.2
+// They'll work fine in earlier iOS versions, but the app cannot be built with
+// an SDK version < 8.2 unless we redefine them here. This will be removed
+// in a future version of ReactKit, once 8.2 is more widely adopted.
+
+static const CGFloat UIFontWeightUltraLight = -0.8;
+static const CGFloat UIFontWeightThin = -0.6;
+static const CGFloat UIFontWeightLight = -0.4;
+static const CGFloat UIFontWeightRegular = 0;
+static const CGFloat UIFontWeightMedium = 0.23;
+static const CGFloat UIFontWeightSemibold = 0.3;
+static const CGFloat UIFontWeightBold = 0.4;
+static const CGFloat UIFontWeightHeavy = 0.56;
+static const CGFloat UIFontWeightBlack = 0.62;
+
+#endif
+
+typedef CGFloat RCTFontWeight;
+RCT_ENUM_CONVERTER(RCTFontWeight, (@{
+ @"normal": @(UIFontWeightRegular),
+ @"bold": @(UIFontWeightBold),
+ @"100": @(UIFontWeightUltraLight),
+ @"200": @(UIFontWeightThin),
+ @"300": @(UIFontWeightLight),
+ @"400": @(UIFontWeightRegular),
+ @"500": @(UIFontWeightMedium),
+ @"600": @(UIFontWeightSemibold),
+ @"700": @(UIFontWeightBold),
+ @"800": @(UIFontWeightHeavy),
+ @"900": @(UIFontWeightBlack),
+}), UIFontWeightRegular, doubleValue)
+
+typedef BOOL RCTFontStyle;
+RCT_ENUM_CONVERTER(RCTFontStyle, (@{
+ @"normal": @NO,
+ @"italic": @YES,
+ @"oblique": @YES,
+}), NO, boolValue)
+
+static RCTFontWeight RCTWeightOfFont(UIFont *font)
+{
+ NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
+ return [traits[UIFontWeightTrait] doubleValue];
+}
+
+static BOOL RCTFontIsItalic(UIFont *font)
+{
+ NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
+ UIFontDescriptorSymbolicTraits symbolicTraits = [traits[UIFontSymbolicTrait] unsignedIntValue];
+ return (symbolicTraits & UIFontDescriptorTraitItalic) != 0;
+}
+
+static BOOL RCTFontIsCondensed(UIFont *font)
+{
+ NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
+ UIFontDescriptorSymbolicTraits symbolicTraits = [traits[UIFontSymbolicTrait] unsignedIntValue];
+ return (symbolicTraits & UIFontDescriptorTraitCondensed) != 0;
+}
+
+ (UIFont *)UIFont:(UIFont *)font withSize:(id)json
{
- return [self UIFont:font withFamily:nil size:json weight:nil];
+ return [self UIFont:font withFamily:nil size:json weight:nil style:nil];
}
+ (UIFont *)UIFont:(UIFont *)font withWeight:(id)json
{
- return [self UIFont:font withFamily:nil size:nil weight:json];
+ return [self UIFont:font withFamily:nil size:nil weight:json style:nil];
+}
+
++ (UIFont *)UIFont:(UIFont *)font withStyle:(id)json
+{
+ return [self UIFont:font withFamily:nil size:nil weight:nil style:json];
}
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json
{
- return [self UIFont:font withFamily:json size:nil weight:nil];
+ return [self UIFont:font withFamily:json size:nil weight:nil style:nil];
}
-+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)family size:(id)size weight:(id)weight
++ (UIFont *)UIFont:(UIFont *)font
+ withFamily:(id)family
+ size:(id)size
+ weight:(id)weight
+ style:(id)style
{
- CGFloat const RCTDefaultFontSize = 14;
- NSString *const RCTDefaultFontName = @"HelveticaNeue";
- NSString *const RCTDefaultFontWeight = @"normal";
- NSString *const RCTBoldFontWeight = @"bold";
+ // Defaults
+ NSString *const RCTDefaultFontFamily = @"Helvetica Neue";
+ const RCTFontWeight RCTDefaultFontWeight = UIFontWeightRegular;
+ const CGFloat RCTDefaultFontSize = 14;
- // Create descriptor
- UIFontDescriptor *fontDescriptor = font.fontDescriptor ?: [UIFontDescriptor fontDescriptorWithName:RCTDefaultFontName size:RCTDefaultFontSize];
-
- // Get font size
- CGFloat fontSize = [self CGFloat:size];
- if (fontSize && !isnan(fontSize)) {
- fontDescriptor = [fontDescriptor fontDescriptorWithSize:fontSize];
- }
-
- // Get font family
- NSString *familyName = [self NSString:family];
- if (familyName) {
- if ([UIFont fontNamesForFamilyName:familyName].count == 0) {
- font = [UIFont fontWithName:familyName size:fontDescriptor.pointSize];
- if (font) {
- // It's actually a font name, not a font family name,
- // but we'll do what was meant, not what was said.
- familyName = font.familyName;
- fontDescriptor = font.fontDescriptor;
- } else {
- // Not a valid font or family
- RCTLogError(@"Unrecognized font family '%@'", familyName);
- familyName = [UIFont fontWithDescriptor:fontDescriptor size:0].familyName;
- }
- } else {
- // Set font family
- fontDescriptor = [fontDescriptor fontDescriptorWithFamily:familyName];
- }
- } else {
- familyName = [UIFont fontWithDescriptor:fontDescriptor size:0].familyName;
+ // Get existing properties
+ BOOL isItalic = NO;
+ BOOL isCondensed = NO;
+ RCTFontWeight fontWeight = RCTDefaultFontWeight;
+ if (font) {
+ family = font.familyName;
+ fontWeight = RCTWeightOfFont(font);
+ isItalic = RCTFontIsItalic(font);
+ isCondensed = RCTFontIsCondensed(font);
}
// Get font weight
- NSString *fontWeight = [self NSString:weight];
- if (fontWeight) {
+ if (weight) {
+ fontWeight = [self RCTFontWeight:weight];
+ }
- static NSSet *values;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- values = [NSSet setWithObjects:RCTDefaultFontWeight, RCTBoldFontWeight, nil];
- });
+ // Get font style
+ if (style) {
+ isItalic = [self RCTFontStyle:style];
+ }
- if (fontWeight && ![values containsObject:fontWeight]) {
- RCTLogError(@"Unrecognized font weight '%@', must be one of %@", fontWeight, values);
- fontWeight = RCTDefaultFontWeight;
+ // Get font size
+ CGFloat fontSize = [self CGFloat:size] ?: RCTDefaultFontSize;
+
+ // Get font family
+ NSString *familyName = [self NSString:family] ?: RCTDefaultFontFamily;
+ if ([UIFont fontNamesForFamilyName:familyName].count == 0) {
+ font = [UIFont fontWithName:familyName size:fontSize];
+ if (font) {
+ // It's actually a font name, not a font family name,
+ // but we'll do what was meant, not what was said.
+ familyName = font.familyName;
+ NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
+ fontWeight = [traits[UIFontWeightTrait] doubleValue];
+ } else {
+ // Not a valid font or family
+ RCTLogError(@"Unrecognized font family '%@'", familyName);
+ familyName = RCTDefaultFontFamily;
}
+ }
- // this is hacky. we are appending the string -Medium because most fonts we currently use
- // just need to have -Medium appended to get the bold we want. we're going to revamp this
- // to make it easier to know which options are available in JS. t4996115
- if ([fontWeight isEqualToString:RCTBoldFontWeight]) {
- font = nil;
- for (NSString *fontName in [UIFont fontNamesForFamilyName:familyName]) {
- if ([fontName hasSuffix:@"-Medium"]) {
- font = [UIFont fontWithName:fontName size:fontDescriptor.pointSize];
- break;
- }
- if ([fontName hasSuffix:@"-Bold"]) {
- font = [UIFont fontWithName:fontName size:fontDescriptor.pointSize];
- // But keep searching in case there's a medium option
- }
- }
- if (font) {
- fontDescriptor = font.fontDescriptor;
+ // Get closest match
+ UIFont *bestMatch = font;
+ CGFloat closestWeight = font ? RCTWeightOfFont(font) : INFINITY;
+ for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) {
+ UIFont *match = [UIFont fontWithName:name size:fontSize];
+ if (isItalic == RCTFontIsItalic(match) &&
+ isCondensed == RCTFontIsCondensed(match)) {
+ CGFloat testWeight = RCTWeightOfFont(match);
+ if (ABS(testWeight - fontWeight) < ABS(closestWeight - fontWeight)) {
+ bestMatch = match;
+ closestWeight = testWeight;
}
}
}
- // TODO: font style
+ // Safety net
+ if (!bestMatch) {
+ RCTLogError(@"Could not find font with family: '%@', size: %@, \
+ weight: %@, style: %@", family, size, weight, style);
+ bestMatch = [UIFont fontWithName:[[UIFont fontNamesForFamilyName:familyName] firstObject]
+ size:fontSize];
+ }
- // Create font
- return [UIFont fontWithDescriptor:fontDescriptor size:0];
+ return bestMatch;
}
RCT_ARRAY_CONVERTER(NSString)
diff --git a/ReactKit/Base/RCTDevelopmentViewController.h b/ReactKit/Base/RCTDevMenu.h
similarity index 72%
rename from ReactKit/Base/RCTDevelopmentViewController.h
rename to ReactKit/Base/RCTDevMenu.h
index 029f45f60..e7d3b8b30 100644
--- a/ReactKit/Base/RCTDevelopmentViewController.h
+++ b/ReactKit/Base/RCTDevMenu.h
@@ -9,6 +9,11 @@
#import
-@interface RCTDevelopmentViewController : UIViewController
+@class RCTRootView;
+
+@interface RCTDevMenu : NSObject
+
+- (instancetype)initWithRootView:(RCTRootView *)rootView;
+- (void)show;
@end
diff --git a/ReactKit/Base/RCTDevMenu.m b/ReactKit/Base/RCTDevMenu.m
new file mode 100644
index 000000000..77ce73935
--- /dev/null
+++ b/ReactKit/Base/RCTDevMenu.m
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+#import "RCTDevMenu.h"
+
+#import "RCTRedBox.h"
+#import "RCTRootView.h"
+
+@interface RCTDevMenu () {
+ BOOL _liveReload;
+}
+
+@property (nonatomic, weak) RCTRootView *view;
+
+@end
+
+@implementation RCTDevMenu
+
+- (instancetype)initWithRootView:(RCTRootView *)rootView
+{
+ if (self = [super init]) {
+ self.view = rootView;
+ }
+ return self;
+}
+
+- (void)show
+{
+ NSString *debugTitle = self.view.executorClass == nil ? @"Enable Debugging" : @"Disable Debugging";
+ NSString *liveReloadTitle = _liveReload ? @"Disable Live Reload" : @"Enable Live Reload";
+ UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"React Native: Development"
+ delegate:self
+ cancelButtonTitle:@"Cancel"
+ destructiveButtonTitle:nil
+ otherButtonTitles:@"Reload", debugTitle, liveReloadTitle, nil];
+ actionSheet.actionSheetStyle = UIBarStyleBlack;
+ [actionSheet showInView:self.view];
+}
+
+- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
+{
+ if (buttonIndex == 0) {
+ [self.view reload];
+ } else if (buttonIndex == 1) {
+ self.view.executorClass = self.view.executorClass == nil ? NSClassFromString(@"RCTWebSocketExecutor") : nil;
+ [self.view reload];
+ } else if (buttonIndex == 2) {
+ _liveReload = !_liveReload;
+ [self _pollAndReload];
+ }
+}
+
+- (void)_pollAndReload
+{
+ if (_liveReload) {
+ NSURL *url = [self.view scriptURL];
+ NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:url];
+ [self performSelectorInBackground:@selector(_checkForUpdates:) withObject:longPollURL];
+ }
+}
+
+- (void)_checkForUpdates:(NSURL *)URL
+{
+ NSMutableURLRequest *longPollRequest = [NSMutableURLRequest requestWithURL:URL];
+ longPollRequest.timeoutInterval = 30;
+ NSHTTPURLResponse *response;
+ [NSURLConnection sendSynchronousRequest:longPollRequest returningResponse:&response error:nil];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (_liveReload && response.statusCode == 205) {
+ [[RCTRedBox sharedInstance] dismiss];
+ [self.view reload];
+ }
+ [self _pollAndReload];
+ });
+}
+
+@end
diff --git a/ReactKit/Base/RCTDevelopmentViewController.m b/ReactKit/Base/RCTDevelopmentViewController.m
deleted file mode 100644
index 9b414b14f..000000000
--- a/ReactKit/Base/RCTDevelopmentViewController.m
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
-
-#import "RCTDevelopmentViewController.h"
-
-#import "RCTRedBox.h"
-#import "RCTRootView.h"
-
-@interface RCTDevelopmentViewController () {
- BOOL _liveReload;
-}
-
-@property (nonatomic, readonly) RCTRootView *RCTView;
-
-@end
-
-@implementation RCTDevelopmentViewController
-
-- (BOOL)canBecomeFirstResponder
-{
- return YES;
-}
-
-- (RCTRootView *)RCTView
-{
- return (RCTRootView *)self.view;
-}
-
-- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
-{
- if (motion == UIEventSubtypeMotionShake)
- {
- NSString *debugTitle = self.RCTView.executorClass == nil ? @"Enable Debugging" : @"Disable Debugging";
- NSString *liveReloadTitle = _liveReload ? @"Disable Live Reload" : @"Enable Live Reload";
- UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"React Native: Development"
- delegate:self
- cancelButtonTitle:@"Cancel"
- destructiveButtonTitle:nil
- otherButtonTitles:@"Reload", debugTitle, liveReloadTitle, nil];
- actionSheet.actionSheetStyle = UIBarStyleBlack;
- [actionSheet showInView:self.view];
- }
-}
-
-- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
-{
- if (buttonIndex == 0) {
- [self.RCTView reload];
- } else if (buttonIndex == 1) {
- self.RCTView.executorClass = self.RCTView.executorClass == nil ? NSClassFromString(@"RCTWebSocketExecutor") : nil;
- [self.RCTView reload];
- } else if (buttonIndex == 2) {
- _liveReload = !_liveReload;
- [self _pollAndReload];
- }
-}
-
-- (void)_pollAndReload
-{
- if (_liveReload) {
- NSURL *url = [self.RCTView scriptURL];
- NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:url];
- [self performSelectorInBackground:@selector(_checkForUpdates:) withObject:longPollURL];
- }
-}
-
-- (void)_checkForUpdates:(NSURL *)URL
-{
- NSMutableURLRequest *longPollRequest = [NSMutableURLRequest requestWithURL:URL];
- longPollRequest.timeoutInterval = 30;
- NSHTTPURLResponse *response;
- [NSURLConnection sendSynchronousRequest:longPollRequest returningResponse:&response error:nil];
-
- dispatch_async(dispatch_get_main_queue(), ^{
- if (_liveReload && response.statusCode == 205) {
- [[RCTRedBox sharedInstance] dismiss];
- [self.RCTView reload];
- }
- [self _pollAndReload];
- });
-}
-
-@end
diff --git a/ReactKit/Base/RCTRootView.h b/ReactKit/Base/RCTRootView.h
index 15f99fdee..ef72d374b 100644
--- a/ReactKit/Base/RCTRootView.h
+++ b/ReactKit/Base/RCTRootView.h
@@ -48,6 +48,12 @@
*/
@property (nonatomic, strong) Class executorClass;
+/**
+ * If YES will watch for shake gestures and show development menu
+ * with options like "Reload", "Enable Debugging", etc.
+ */
+@property (nonatomic, assign) BOOL enableDevMenu;
+
/**
* Reload this root view, or all root views, respectively.
*/
diff --git a/ReactKit/Base/RCTRootView.m b/ReactKit/Base/RCTRootView.m
index 843d00dda..7375b77ba 100644
--- a/ReactKit/Base/RCTRootView.m
+++ b/ReactKit/Base/RCTRootView.m
@@ -11,6 +11,7 @@
#import "RCTBridge.h"
#import "RCTContextExecutor.h"
+#import "RCTDevMenu.h"
#import "RCTEventDispatcher.h"
#import "RCTKeyCommands.h"
#import "RCTLog.h"
@@ -26,6 +27,7 @@ NSString *const RCTReloadNotification = @"RCTReloadNotification";
@implementation RCTRootView
{
+ RCTDevMenu *_devMenu;
RCTBridge *_bridge;
RCTTouchHandler *_touchHandler;
id _executor;
@@ -84,6 +86,9 @@ static Class _globalExecutorClass;
// Numbering of these tags goes from 1, 11, 21, 31, etc
static NSInteger rootViewTag = 1;
self.reactTag = @(rootViewTag);
+#ifdef DEBUG
+ self.enableDevMenu = YES;
+#endif
rootViewTag += 10;
// Add reload observer
@@ -93,6 +98,21 @@ static Class _globalExecutorClass;
object:nil];
}
+- (BOOL)canBecomeFirstResponder
+{
+ return YES;
+}
+
+- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
+{
+ if (motion == UIEventSubtypeMotionShake && self.enableDevMenu) {
+ if (!_devMenu) {
+ _devMenu = [[RCTDevMenu alloc] initWithRootView:self];
+ }
+ [_devMenu show];
+ }
+}
+
+ (NSArray *)JSMethods
{
return @[
diff --git a/ReactKit/Base/RCTUtils.m b/ReactKit/Base/RCTUtils.m
index 70ad4479a..300f6d721 100644
--- a/ReactKit/Base/RCTUtils.m
+++ b/ReactKit/Base/RCTUtils.m
@@ -50,11 +50,11 @@ NSString *RCTMD5Hash(NSString *string)
CC_MD5(str, (CC_LONG)strlen(str), result);
return [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
- result[0], result[1], result[2], result[3],
- result[4], result[5], result[6], result[7],
- result[8], result[9], result[10], result[11],
- result[12], result[13], result[14], result[15]
- ];
+ result[0], result[1], result[2], result[3],
+ result[4], result[5], result[6], result[7],
+ result[8], result[9], result[10], result[11],
+ result[12], result[13], result[14], result[15]
+ ];
}
CGFloat RCTScreenScale()
diff --git a/ReactKit/ReactKit.xcodeproj/project.pbxproj b/ReactKit/ReactKit.xcodeproj/project.pbxproj
index c742711d8..b7c68ccd5 100644
--- a/ReactKit/ReactKit.xcodeproj/project.pbxproj
+++ b/ReactKit/ReactKit.xcodeproj/project.pbxproj
@@ -8,7 +8,7 @@
/* Begin PBXBuildFile section */
000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; };
- 00C1A2B31AC0B7E000E89A1C /* RCTDevelopmentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C1A2B21AC0B7E000E89A1C /* RCTDevelopmentViewController.m */; };
+ 00C1A2B31AC0B7E000E89A1C /* RCTDevMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */; };
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; };
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; };
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */; };
@@ -77,8 +77,8 @@
/* Begin PBXFileReference section */
000E6CE91AB0E97F000CDF4D /* RCTSourceCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSourceCode.h; sourceTree = ""; };
000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSourceCode.m; sourceTree = ""; };
- 00C1A2B11AC0B7E000E89A1C /* RCTDevelopmentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDevelopmentViewController.h; sourceTree = ""; };
- 00C1A2B21AC0B7E000E89A1C /* RCTDevelopmentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDevelopmentViewController.m; sourceTree = ""; };
+ 00C1A2B11AC0B7E000E89A1C /* RCTDevMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDevMenu.h; sourceTree = ""; };
+ 00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDevMenu.m; sourceTree = ""; };
13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = ""; };
13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = ""; };
13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = ""; };
@@ -368,8 +368,8 @@
83CBBA591A601E9000E9B192 /* RCTRedBox.m */,
830A229C1A66C68A008503DA /* RCTRootView.h */,
830A229D1A66C68A008503DA /* RCTRootView.m */,
- 00C1A2B11AC0B7E000E89A1C /* RCTDevelopmentViewController.h */,
- 00C1A2B21AC0B7E000E89A1C /* RCTDevelopmentViewController.m */,
+ 00C1A2B11AC0B7E000E89A1C /* RCTDevMenu.h */,
+ 00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */,
83BEE46C1A6D19BC00B5863B /* RCTSparseArray.h */,
83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */,
83CBBA961A6020BB00E9B192 /* RCTTouchHandler.h */,
@@ -499,7 +499,7 @@
13B0801A1A69489C00A75B9A /* RCTNavigator.m in Sources */,
830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */,
137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */,
- 00C1A2B31AC0B7E000E89A1C /* RCTDevelopmentViewController.m in Sources */,
+ 00C1A2B31AC0B7E000E89A1C /* RCTDevMenu.m in Sources */,
14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */,
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */,
13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */,
diff --git a/ReactKit/Views/RCTScrollView.h b/ReactKit/Views/RCTScrollView.h
index 7e69affe6..db6aa7edd 100644
--- a/ReactKit/Views/RCTScrollView.h
+++ b/ReactKit/Views/RCTScrollView.h
@@ -22,19 +22,25 @@
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
/**
- * If the `contentSize` is not provided, then the `contentSize` will
- * automatically be determined by the size of the `RKScrollView` subview.
- *
* The `RCTScrollView` may have at most one single subview. This will ensure
* that the scroll view's `contentSize` will be efficiently set to the size of
* the single subview's frame. That frame size will be determined somewhat
* efficiently since it will have already been computed by the off-main-thread
* layout system.
*/
-@property (nonatomic, readonly) UIScrollView *scrollView;
@property (nonatomic, readonly) UIView *contentView;
+/**
+ * If the `contentSize` is not specified (or is specified as {0, 0}, then the
+ * `contentSize` will automatically be determined by the size of the subview.
+ */
@property (nonatomic, assign) CGSize contentSize;
+
+/**
+ * The underlying scrollView (TODO: can we remove this?)
+ */
+@property (nonatomic, readonly) UIScrollView *scrollView;
+
@property (nonatomic, assign) UIEdgeInsets contentInset;
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
@property (nonatomic, assign) NSUInteger throttleScrollCallbackMS;
diff --git a/ReactKit/Views/RCTScrollView.m b/ReactKit/Views/RCTScrollView.m
index ff98252a9..8c3db22be 100644
--- a/ReactKit/Views/RCTScrollView.m
+++ b/ReactKit/Views/RCTScrollView.m
@@ -253,7 +253,6 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
@implementation RCTScrollView
{
RCTEventDispatcher *_eventDispatcher;
- BOOL _contentSizeManuallySet;
RCTCustomScrollView *_scrollView;
UIView *_contentView;
NSTimeInterval _lastScrollDispatchTime;
@@ -273,6 +272,7 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
_scrollView.delaysContentTouches = NO;
_automaticallyAdjustContentInsets = YES;
_contentInset = UIEdgeInsetsZero;
+ _contentSize = CGSizeZero;
_throttleScrollCallbackMS = 0;
_lastScrollDispatchTime = CACurrentMediaTime();
@@ -319,16 +319,6 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
_scrollView.stickyHeaderIndices = headerIndices;
}
-/**
- * Once you set the `contentSize`, it's assumed to be managed by you forever
- * and we'll never automatically compute the size for you.
- */
-- (void)setContentSize:(CGSize)contentSize
-{
- _contentSize = contentSize;
- _contentSizeManuallySet = YES;
-}
-
- (void)dealloc
{
_scrollView.delegate = nil;
@@ -556,31 +546,42 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, RCTScrollEventTypeMove)
return newOffset;
}
-- (void)reactBridgeDidFinishTransaction
+/**
+ * Once you set the `contentSize`, to a nonzero value, it is assumed to be
+ * managed by you, and we'll never automatically compute the size for you,
+ * unless you manually reset it back to {0, 0}
+ */
+- (CGSize)contentSize
{
- if (_contentSizeManuallySet) {
- _scrollView.contentSize = _contentSize;
+ if (!CGSizeEqualToSize(_contentSize, CGSizeZero)) {
+ return _contentSize;
} else if (!_contentView) {
- _scrollView.contentSize = CGSizeZero;
+ return CGSizeZero;
} else {
CGSize singleSubviewSize = _contentView.frame.size;
CGPoint singleSubviewPosition = _contentView.frame.origin;
- CGSize fittedSize = {
+ return (CGSize){
singleSubviewSize.width + singleSubviewPosition.x,
singleSubviewSize.height + singleSubviewPosition.y
};
- if (!CGSizeEqualToSize(_scrollView.contentSize, fittedSize)) {
- // When contentSize is set manually, ScrollView internals will reset contentOffset to 0,0. Since
- // we potentially set contentSize whenever anything in the ScrollView updates, we workaround this
- // issue by manually adjusting contentOffset whenever this happens
- CGPoint newOffset = [self calculateOffsetForContentSize:fittedSize];
- _scrollView.contentSize = fittedSize;
- _scrollView.contentOffset = newOffset;
- }
- [_scrollView dockClosestSectionHeader];
}
}
+- (void)reactBridgeDidFinishTransaction
+{
+ CGSize contentSize = self.contentSize;
+ if (!CGSizeEqualToSize(_scrollView.contentSize, contentSize)) {
+ // When contentSize is set manually, ScrollView internals will reset
+ // contentOffset to {0, 0}. Since we potentially set contentSize whenever
+ // anything in the ScrollView updates, we workaround this issue by manually
+ // adjusting contentOffset whenever this happens
+ CGPoint newOffset = [self calculateOffsetForContentSize:contentSize];
+ _scrollView.contentSize = contentSize;
+ _scrollView.contentOffset = newOffset;
+ }
+ [_scrollView dockClosestSectionHeader];
+}
+
// Note: setting several properties of UIScrollView has the effect of
// resetting its contentOffset to {0, 0}. To prevent this, we generate
// setters here that will record the contentOffset beforehand, and
diff --git a/ReactKit/Views/RCTTextFieldManager.m b/ReactKit/Views/RCTTextFieldManager.m
index 041886e55..8418c8858 100644
--- a/ReactKit/Views/RCTTextFieldManager.m
+++ b/ReactKit/Views/RCTTextFieldManager.m
@@ -41,7 +41,11 @@ RCT_CUSTOM_VIEW_PROPERTY(fontSize, RCTTextField)
}
RCT_CUSTOM_VIEW_PROPERTY(fontWeight, RCTTextField)
{
- view.font = [RCTConvert UIFont:view.font withWeight:json]; // TODO: default value
+ view.font = [RCTConvert UIFont:view.font withWeight:json]; // defaults to normal
+}
+RCT_CUSTOM_VIEW_PROPERTY(fontStyle, RCTTextField)
+{
+ view.font = [RCTConvert UIFont:view.font withStyle:json]; // defaults to normal
}
RCT_CUSTOM_VIEW_PROPERTY(fontFamily, RCTTextField)
{
diff --git a/packager/README.md b/packager/README.md
new file mode 100644
index 000000000..8f9f649bf
--- /dev/null
+++ b/packager/README.md
@@ -0,0 +1,138 @@
+React Native Packager
+--------------------
+
+React Native Packager is a project similar in scope to browserify or
+webpack, it provides a CommonJS-like module system, JavaScript
+compilation (ES6, Flow, JSX), bundling, and asset loading.
+
+The main difference is the Packager's focus on compilation and
+bundling speed. We aim for a sub-second edit-reload
+cycles. Additionally, we don't want users -- with large code bases --
+to wait more than a few seconds after starting the packager.
+
+The main deviation from the node module system is the support for our
+proprietary module format known as `@providesModule`. However, we
+discourage people to use this module format because going forward, we
+want to completely separate our infrastructure from React Native and
+provide an experience most JavaScript developers are familiar with,
+namely the node module format. We want to even go further, and let you
+choose your own packager and asset pipeline or even integrate into
+your existing infrastructure.
+
+React Native users need not to understand how the packager work,
+however, this documentation might be useful for advanced users and
+people who want to fix bugs or add features to the packager (patches
+welcome!).
+
+## HTTP interface
+
+The main way you'd interact with the packager is via the HTTP
+interface. The following is the list of endpoints and their respective
+functions.
+
+### /path/to/moduleName.bundle
+
+Does the following in order:
+
+* parse out `path/to/moduleName`
+* add a `.js` suffix to the path
+* looks in your project root(s) for the file
+* recursively collects all the dependencies from an in memory graph
+* runs the modules through the transformer (might just be cached)
+* concatenate the modules' content into a bundle
+* responds to the client with the bundle (and a SourceMap URL)
+
+### /path/to/moduleName.map
+
+* if the package has been previously generated via the `.bundle`
+ endpoint then the source map will be generated from that package
+* if the package has not been previously asked for, this will go
+ through the same steps outlined in the `.bundle` endpoint then
+ generate the source map.
+
+Note that source map generation currently assumes that the code has
+been compiled with jstransform, which preserves line and column
+numbers which allows us to generate source maps super fast.
+
+### /path/to/moduleName.(map|bundle) query params
+
+You can pass options for the bundle creation through the query params,
+if the option is boolean `1/0` or `true/false` is accepted.
+
+Here are the current options the packager accepts:
+
+* `dev` boolean, defaults to true: sets a global `__DEV__` variable
+ which will effect how the React Nativeg core libraries behave.
+* `minify` boolean, defaults to false: whether to minify the bundle.
+* `runModule` boolean, defaults to true: whether to require your entry
+ point module. So if you requested `moduleName`, this option will add
+ a `require('moduleName')` the end of your bundle.
+* `inlineSourceMap` boolean, defaults to false: whether to inline
+ source maps.
+
+### /debug
+
+This is a page used for debugging, it has links to two pages:
+
+* Cached Packages: which shows you the packages that's been already
+ generated and cached
+* Dependency Graph: is the in-memory graph of all the modules and
+ their dependencies
+
+## Programmatic API
+
+The packager is made of two things:
+
+* The core packager (which we're calling ReactPackager)
+* The scripts, devtools launcher, server run etc.
+
+ReactPackager is how you mainly interact with the API.
+
+```js
+var ReactPackager = require('./react-packager');
+```
+
+### ReactPackager.middleware(options)
+
+Returns a function that can be used in a connect-like
+middleware. Takes the following options:
+
+* `projectRoots` array (required): Is the roots where your JavaScript
+ file will exist
+* `blacklistRE` regexp: Is a patter to ignore certain paths from the
+ packager
+* `polyfillModuleName` array: Paths to polyfills you want to be
+ included at the start of the bundle
+* `cacheVersion` string: used in creating the cache file
+* `resetCache` boolean, defaults to false: whether to use the cache on
+ disk
+* `transformModulePath` string: Path to the module used as a
+ JavaScript transformer
+* `nonPersistent` boolean, defaults to false: Whether the server
+ should be used as a persistent deamon to watch files and update
+ itself
+* `assetRoots` array: Where should the packager look for assets
+
+### ReactPackager.buildPackageFromUrl(options, url)
+
+Build a package from a url (see the `.bundle` endpoint). `options` is
+the same options that is passed to `ReactPackager.middleware`
+
+### ReactPackager.getDependencies(options, main)
+
+Given an entry point module. Recursively collect all the dependent
+modules and return it as an array. `options` is the same options that
+is passed to `ReactPackager.middleware`
+
+## FAQ
+
+### Can I use this in my own non-React Native project?
+
+Yes. It's not really tied to React Native, however feature development
+is informed by React Native needs.
+
+### Why didn't you use webpack?
+
+We love webpack, however, when we tried on our codebase it was slower
+than our developers would like it to be. You find can more discussion about
+the subject [here](https://github.com/facebook/react-native/issues/5)