Merge pull request #1184 from frantic/sync-2015-05-06
Updates from Wed 6 May
This commit is contained in:
commit
07937620c0
|
@ -17,6 +17,7 @@
|
|||
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
|
||||
00E356F31AD99517003FC87E /* SampleAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* SampleAppTests.m */; };
|
||||
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
|
||||
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
|
@ -82,6 +83,13 @@
|
|||
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
|
||||
remoteInfo = SampleApp;
|
||||
};
|
||||
139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RCTSettings;
|
||||
};
|
||||
146834031AC3E56700842450 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
|
||||
|
@ -117,6 +125,7 @@
|
|||
00E356EE1AD99517003FC87E /* SampleAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
00E356F21AD99517003FC87E /* SampleAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleAppTests.m; sourceTree = "<group>"; };
|
||||
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
|
||||
13B07F961A680F5B00A75B9A /* SampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = iOS/AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = iOS/AppDelegate.m; sourceTree = "<group>"; };
|
||||
|
@ -142,6 +151,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
146834051AC3E58100842450 /* libReact.a in Frameworks */,
|
||||
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */,
|
||||
00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */,
|
||||
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
|
||||
00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */,
|
||||
|
@ -230,6 +240,14 @@
|
|||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
139105B71AF99BAD00B5F7CC /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
13B07FAE1A68108700A75B9A /* SampleApp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -263,14 +281,15 @@
|
|||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
|
||||
146833FF1AC3E56700842450 /* React.xcodeproj */,
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
|
||||
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
|
||||
00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */,
|
||||
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
|
||||
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
|
||||
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */,
|
||||
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */,
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
|
||||
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
|
||||
00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */,
|
||||
);
|
||||
|
@ -395,6 +414,10 @@
|
|||
ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */;
|
||||
ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */;
|
||||
ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 832341B11AAA6A8300B99B32 /* Products */;
|
||||
ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
|
||||
|
@ -470,6 +493,13 @@
|
|||
remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTSettings.a;
|
||||
remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
146834041AC3E56700842450 /* libReact.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
|
|
|
@ -24,7 +24,7 @@ var SampleApp = React.createClass({
|
|||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
Press Cmd+R to reload,{'\n'}
|
||||
Cmd+Control+Z for dev menu
|
||||
Cmd+D or shake for dev menu
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -13,12 +13,14 @@
|
|||
|
||||
var LayoutPropTypes = require('LayoutPropTypes');
|
||||
var ReactPropTypes = require('ReactPropTypes');
|
||||
var TransformPropTypes = require('TransformPropTypes');
|
||||
|
||||
/**
|
||||
* Warning: Some of these properties may not be supported in all releases.
|
||||
*/
|
||||
var ViewStylePropTypes = {
|
||||
...LayoutPropTypes,
|
||||
...TransformPropTypes,
|
||||
backgroundColor: ReactPropTypes.string,
|
||||
borderColor: ReactPropTypes.string,
|
||||
borderTopColor: ReactPropTypes.string,
|
||||
|
@ -34,15 +36,6 @@ var ViewStylePropTypes = {
|
|||
),
|
||||
shadowOpacity: ReactPropTypes.number,
|
||||
shadowRadius: ReactPropTypes.number,
|
||||
transform: ReactPropTypes.arrayOf(ReactPropTypes.object),
|
||||
transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number),
|
||||
|
||||
// DEPRECATED
|
||||
rotation: ReactPropTypes.number,
|
||||
scaleX: ReactPropTypes.number,
|
||||
scaleY: ReactPropTypes.number,
|
||||
translateX: ReactPropTypes.number,
|
||||
translateY: ReactPropTypes.number,
|
||||
};
|
||||
|
||||
module.exports = ViewStylePropTypes;
|
||||
|
|
|
@ -227,15 +227,17 @@ var Navigator = React.createClass({
|
|||
renderScene: PropTypes.func.isRequired,
|
||||
|
||||
/**
|
||||
* Provide a single "route" to start on. A route is an arbitrary object
|
||||
* that the navigator will use to identify each scene before rendering.
|
||||
* Either initialRoute or initialRouteStack is required.
|
||||
* Specify a route to start on. A route is an object that the navigator
|
||||
* will use to identify each scene to render. `initialRoute` must be
|
||||
* a route in the `initialRouteStack` if both props are provided. The
|
||||
* `initialRoute` will default to the last item in the `initialRouteStack`.
|
||||
*/
|
||||
initialRoute: PropTypes.object,
|
||||
|
||||
/**
|
||||
* Provide a set of routes to initially mount the scenes for. Required if no
|
||||
* initialRoute is provided
|
||||
* Provide a set of routes to initially mount. Required if no initialRoute
|
||||
* is provided. Otherwise, it will default to an array containing only the
|
||||
* `initialRoute`
|
||||
*/
|
||||
initialRouteStack: PropTypes.arrayOf(PropTypes.object),
|
||||
|
||||
|
@ -295,21 +297,18 @@ var Navigator = React.createClass({
|
|||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var routeStack = this.props.initialRouteStack || [];
|
||||
var initialRouteIndex = 0;
|
||||
if (this.props.initialRoute && routeStack.length) {
|
||||
var routeStack = this.props.initialRouteStack || [this.props.initialRoute];
|
||||
invariant(
|
||||
routeStack.length >= 1,
|
||||
'Navigator requires props.initialRoute or props.initialRouteStack.'
|
||||
);
|
||||
var initialRouteIndex = routeStack.length - 1;
|
||||
if (this.props.initialRoute) {
|
||||
initialRouteIndex = routeStack.indexOf(this.props.initialRoute);
|
||||
invariant(
|
||||
initialRouteIndex !== -1,
|
||||
'initialRoute is not in initialRouteStack.'
|
||||
);
|
||||
} else if (this.props.initialRoute) {
|
||||
routeStack = [this.props.initialRoute];
|
||||
} else {
|
||||
invariant(
|
||||
routeStack.length >= 1,
|
||||
'Navigator requires props.initialRoute or props.initialRouteStack.'
|
||||
);
|
||||
}
|
||||
return {
|
||||
sceneConfigStack: routeStack.map(
|
||||
|
@ -689,7 +688,8 @@ var Navigator = React.createClass({
|
|||
var enabledSceneNativeProps = {
|
||||
left: sceneStyle.left,
|
||||
};
|
||||
if (sceneIndex !== this.state.transitionFromIndex) {
|
||||
if (sceneIndex !== this.state.transitionFromIndex &&
|
||||
sceneIndex !== this.state.presentedIndex) {
|
||||
// If we are not in a transition from this index, make sure opacity is 0
|
||||
// to prevent the enabled scene from flashing over the presented scene
|
||||
enabledSceneNativeProps.opacity = 0;
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
var ImageResizeMode = require('ImageResizeMode');
|
||||
var LayoutPropTypes = require('LayoutPropTypes');
|
||||
var ReactPropTypes = require('ReactPropTypes');
|
||||
var TransformPropTypes = require('TransformPropTypes');
|
||||
|
||||
var ImageStylePropTypes = {
|
||||
...LayoutPropTypes,
|
||||
...TransformPropTypes,
|
||||
resizeMode: ReactPropTypes.oneOf(Object.keys(ImageResizeMode)),
|
||||
backgroundColor: ReactPropTypes.string,
|
||||
borderColor: ReactPropTypes.string,
|
||||
|
|
|
@ -66,7 +66,7 @@ class XMLHttpRequestBase {
|
|||
|
||||
getResponseHeader(header: string): ?string {
|
||||
if (this.responseHeaders) {
|
||||
var value = this.responseHeaders[header];
|
||||
var value = this.responseHeaders[header.toLowerCase()];
|
||||
return value !== undefined ? value : null;
|
||||
}
|
||||
return null;
|
||||
|
@ -132,7 +132,12 @@ class XMLHttpRequestBase {
|
|||
return;
|
||||
}
|
||||
this.status = status;
|
||||
this.responseHeaders = responseHeaders || {};
|
||||
// Headers should be case-insensitive
|
||||
var lcResponseHeaders = {};
|
||||
for (var header in responseHeaders) {
|
||||
lcResponseHeaders[header.toLowerCase()] = responseHeaders[header];
|
||||
}
|
||||
this.responseHeaders = lcResponseHeaders;
|
||||
this.responseText = responseText;
|
||||
this._setReadyState(this.DONE);
|
||||
this._sendLoad();
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@implementation RCTSettingsManager
|
||||
{
|
||||
|
@ -53,13 +54,15 @@ RCT_EXPORT_MODULE()
|
|||
return;
|
||||
}
|
||||
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"settingsUpdated" body:[_defaults dictionaryRepresentation]];
|
||||
[_bridge.eventDispatcher
|
||||
sendDeviceEventWithName:@"settingsUpdated"
|
||||
body:RCTJSONClean([_defaults dictionaryRepresentation])];
|
||||
}
|
||||
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
return @{
|
||||
@"settings": [_defaults dictionaryRepresentation]
|
||||
@"settings": RCTJSONClean([_defaults dictionaryRepresentation])
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* 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 TransformPropTypes
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactPropTypes = require('ReactPropTypes');
|
||||
|
||||
var TransformPropTypes = {
|
||||
transform: ReactPropTypes.arrayOf(ReactPropTypes.object),
|
||||
transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number),
|
||||
|
||||
// DEPRECATED
|
||||
rotation: ReactPropTypes.number,
|
||||
scaleX: ReactPropTypes.number,
|
||||
scaleY: ReactPropTypes.number,
|
||||
translateX: ReactPropTypes.number,
|
||||
translateY: ReactPropTypes.number,
|
||||
};
|
||||
|
||||
module.exports = TransformPropTypes;
|
|
@ -39,6 +39,7 @@
|
|||
+ (NSString *)NSString:(id)json;
|
||||
+ (NSNumber *)NSNumber:(id)json;
|
||||
+ (NSData *)NSData:(id)json;
|
||||
+ (NSIndexSet *)NSIndexSet:(id)json;
|
||||
|
||||
+ (NSURL *)NSURL:(id)json;
|
||||
+ (NSURLRequest *)NSURLRequest:(id)json;
|
||||
|
@ -76,6 +77,7 @@
|
|||
+ (UIImage *)UIImage:(id)json;
|
||||
+ (CGImageRef)CGImage:(id)json;
|
||||
|
||||
+ (UIFont *)UIFont:(id)json;
|
||||
+ (UIFont *)UIFont:(UIFont *)font withSize:(id)json;
|
||||
+ (UIFont *)UIFont:(UIFont *)font withWeight:(id)json;
|
||||
+ (UIFont *)UIFont:(UIFont *)font withStyle:(id)json;
|
||||
|
|
|
@ -64,6 +64,20 @@ RCT_CONVERTER(NSString *, NSString, description)
|
|||
return [[self NSString:json] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
+ (NSIndexSet *)NSIndexSet:(id)json
|
||||
{
|
||||
json = [self NSNumberArray:json];
|
||||
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
|
||||
for (NSNumber *number in json) {
|
||||
NSInteger index = number.integerValue;
|
||||
if (RCT_DEBUG && index < 0) {
|
||||
RCTLogError(@"Invalid index value %zd. Indices must be positive.", index);
|
||||
}
|
||||
[indexSet addIndex:index];
|
||||
}
|
||||
return indexSet;
|
||||
}
|
||||
|
||||
+ (NSURL *)NSURL:(id)json
|
||||
{
|
||||
NSString *path = [self NSString:json];
|
||||
|
@ -679,6 +693,16 @@ static BOOL RCTFontIsCondensed(UIFont *font)
|
|||
return (symbolicTraits & UIFontDescriptorTraitCondensed) != 0;
|
||||
}
|
||||
|
||||
+ (UIFont *)UIFont:(id)json
|
||||
{
|
||||
json = [self NSDictionary:json];
|
||||
return [self UIFont:nil
|
||||
withFamily:json[@"fontFamily"]
|
||||
size:json[@"fontSize"]
|
||||
weight:json[@"fontWeight"]
|
||||
style:json[@"fontStyle"]];
|
||||
}
|
||||
|
||||
+ (UIFont *)UIFont:(UIFont *)font withSize:(id)json
|
||||
{
|
||||
return [self UIFont:font withFamily:nil size:json weight:nil style:nil];
|
||||
|
@ -728,11 +752,6 @@ static BOOL RCTFontIsCondensed(UIFont *font)
|
|||
// Get font family
|
||||
familyName = [self NSString:family] ?: familyName;
|
||||
|
||||
// Get font style
|
||||
if (style) {
|
||||
isItalic = [self RCTFontStyle:style];
|
||||
}
|
||||
|
||||
// Gracefully handle being given a font name rather than font family, for
|
||||
// example: "Helvetica Light Oblique" rather than just "Helvetica".
|
||||
if ([UIFont fontNamesForFamilyName:familyName].count == 0) {
|
||||
|
@ -751,6 +770,11 @@ static BOOL RCTFontIsCondensed(UIFont *font)
|
|||
}
|
||||
}
|
||||
|
||||
// Get font style
|
||||
if (style) {
|
||||
isItalic = [self RCTFontStyle:style];
|
||||
}
|
||||
|
||||
// Get font weight
|
||||
if (weight) {
|
||||
fontWeight = [self RCTFontWeight:weight];
|
||||
|
@ -758,13 +782,7 @@ static BOOL RCTFontIsCondensed(UIFont *font)
|
|||
|
||||
// Get the closest font that matches the given weight for the fontFamily
|
||||
UIFont *bestMatch = [UIFont fontWithName:font.fontName size: fontSize];
|
||||
CGFloat closestWeight;
|
||||
|
||||
if (font && [font.familyName isEqualToString: familyName]) {
|
||||
closestWeight = RCTWeightOfFont(font);
|
||||
} else {
|
||||
closestWeight = INFINITY;
|
||||
}
|
||||
CGFloat closestWeight = INFINITY;
|
||||
|
||||
for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) {
|
||||
UIFont *match = [UIFont fontWithName:name size:fontSize];
|
||||
|
@ -834,7 +852,9 @@ static id RCTConvertPropertyListValue(id json)
|
|||
{
|
||||
if (!json || json == (id)kCFNull) {
|
||||
return nil;
|
||||
} else if ([json isKindOfClass:[NSDictionary class]]) {
|
||||
}
|
||||
|
||||
if ([json isKindOfClass:[NSDictionary class]]) {
|
||||
__block BOOL copy = NO;
|
||||
NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[json count]];
|
||||
[json enumerateKeysAndObjectsUsingBlock:^(NSString *key, id jsonValue, BOOL *stop) {
|
||||
|
@ -845,7 +865,9 @@ static id RCTConvertPropertyListValue(id json)
|
|||
copy |= value != jsonValue;
|
||||
}];
|
||||
return copy ? values : json;
|
||||
} else if ([json isKindOfClass:[NSArray class]]) {
|
||||
}
|
||||
|
||||
if ([json isKindOfClass:[NSArray class]]) {
|
||||
__block BOOL copy = NO;
|
||||
__block NSArray *values = json;
|
||||
[json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, BOOL *stop) {
|
||||
|
@ -860,15 +882,17 @@ static id RCTConvertPropertyListValue(id json)
|
|||
for (NSInteger i = 0; i < idx; i++) {
|
||||
[(NSMutableArray *)values addObject:json[i]];
|
||||
}
|
||||
[(NSMutableArray *)values addObject:value];
|
||||
if (value) {
|
||||
[(NSMutableArray *)values addObject:value];
|
||||
}
|
||||
copy = YES;
|
||||
}
|
||||
}];
|
||||
return values;
|
||||
} else {
|
||||
// All other JSON types are supported by property lists
|
||||
return json;
|
||||
}
|
||||
|
||||
// All other JSON types are supported by property lists
|
||||
return json;
|
||||
}
|
||||
|
||||
+ (NSPropertyList)NSPropertyList:(id)json
|
||||
|
|
|
@ -66,9 +66,7 @@ RCT_EXPORT_MODULE()
|
|||
// We're swizzling here because it's poor form to override methods in a category,
|
||||
// however UIWindow doesn't actually implement motionEnded:withEvent:, so there's
|
||||
// no need to call the original implementation.
|
||||
#if RCT_DEV
|
||||
RCTSwapInstanceMethods([UIWindow class], @selector(motionEnded:withEvent:), @selector(RCT_motionEnded:withEvent:));
|
||||
#endif
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
|
@ -121,8 +119,6 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
- (void)updateSettings
|
||||
{
|
||||
_settings = [NSMutableDictionary dictionaryWithDictionary:[_defaults objectForKey:RCTDevMenuSettingsKey]];
|
||||
|
||||
__weak RCTDevMenu *weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
RCTDevMenu *strongSelf = weakSelf;
|
||||
|
@ -130,6 +126,8 @@ RCT_EXPORT_MODULE()
|
|||
return;
|
||||
}
|
||||
|
||||
strongSelf->_settings = [NSMutableDictionary dictionaryWithDictionary:[strongSelf->_defaults objectForKey:RCTDevMenuSettingsKey]];
|
||||
|
||||
strongSelf.shakeToShow = [strongSelf->_settings[@"shakeToShow"] ?: @YES boolValue];
|
||||
strongSelf.profilingEnabled = [strongSelf->_settings[@"profilingEnabled"] ?: @NO boolValue];
|
||||
strongSelf.liveReloadEnabled = [strongSelf->_settings[@"liveReloadEnabled"] ?: @NO boolValue];
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
RCT_EXTERN NSString *RCTJSONStringify(id jsonObject, NSError **error);
|
||||
RCT_EXTERN id RCTJSONParse(NSString *jsonString, NSError **error);
|
||||
|
||||
// Strip non JSON-safe values from an object graph
|
||||
RCT_EXTERN id RCTJSONClean(id object);
|
||||
|
||||
// Get MD5 hash of a string (TODO: currently unused. Remove?)
|
||||
RCT_EXTERN NSString *RCTMD5Hash(NSString *string);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
NSString *RCTJSONStringify(id jsonObject, NSError **error)
|
||||
{
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:error];
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:(NSJSONWritingOptions)NSJSONReadingAllowFragments error:error];
|
||||
return jsonData ? [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] : nil;
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,57 @@ id RCTJSONParse(NSString *jsonString, NSError **error)
|
|||
return [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:error];
|
||||
}
|
||||
|
||||
id RCTJSONClean(id object)
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
static NSSet *validLeafTypes;
|
||||
dispatch_once(&onceToken, ^{
|
||||
validLeafTypes = [[NSSet alloc] initWithArray:@[
|
||||
[NSString class],
|
||||
[NSMutableString class],
|
||||
[NSNumber class],
|
||||
[NSNull class],
|
||||
]];
|
||||
});
|
||||
|
||||
if ([validLeafTypes containsObject:[object classForCoder]]) {
|
||||
return object;
|
||||
}
|
||||
|
||||
if ([object isKindOfClass:[NSDictionary class]]) {
|
||||
__block BOOL copy = NO;
|
||||
NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[object count]];
|
||||
[object enumerateKeysAndObjectsUsingBlock:^(NSString *key, id item, BOOL *stop) {
|
||||
id value = RCTJSONClean(item);
|
||||
values[key] = value;
|
||||
copy |= value != item;
|
||||
}];
|
||||
return copy ? values : object;
|
||||
}
|
||||
|
||||
if ([object isKindOfClass:[NSArray class]]) {
|
||||
__block BOOL copy = NO;
|
||||
__block NSArray *values = object;
|
||||
[object enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
|
||||
id value = RCTJSONClean(item);
|
||||
if (copy) {
|
||||
[(NSMutableArray *)values addObject:value];
|
||||
} else if (value != item) {
|
||||
// Converted value is different, so we'll need to copy the array
|
||||
values = [[NSMutableArray alloc] initWithCapacity:values.count];
|
||||
for (NSInteger i = 0; i < idx; i++) {
|
||||
[(NSMutableArray *)values addObject:object[i]];
|
||||
}
|
||||
[(NSMutableArray *)values addObject:value];
|
||||
copy = YES;
|
||||
}
|
||||
}];
|
||||
return values;
|
||||
}
|
||||
|
||||
return (id)kCFNull;
|
||||
}
|
||||
|
||||
NSString *RCTMD5Hash(NSString *string)
|
||||
{
|
||||
const char *str = [string UTF8String];
|
||||
|
|
|
@ -45,6 +45,6 @@
|
|||
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
|
||||
@property (nonatomic, assign) NSTimeInterval scrollEventThrottle;
|
||||
@property (nonatomic, assign) BOOL centerContent;
|
||||
@property (nonatomic, copy) NSArray *stickyHeaderIndices;
|
||||
@property (nonatomic, copy) NSIndexSet *stickyHeaderIndices;
|
||||
|
||||
@end
|
||||
|
|
|
@ -28,8 +28,8 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
|
|||
*/
|
||||
@interface RCTCustomScrollView : UIScrollView<UIGestureRecognizerDelegate>
|
||||
|
||||
@property (nonatomic, copy, readwrite) NSArray *stickyHeaderIndices;
|
||||
@property (nonatomic, readwrite, assign) BOOL centerContent;
|
||||
@property (nonatomic, copy) NSIndexSet *stickyHeaderIndices;
|
||||
@property (nonatomic, assign) BOOL centerContent;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -155,97 +155,72 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
|
|||
[super setContentOffset:contentOffset];
|
||||
}
|
||||
|
||||
- (void)setBounds:(CGRect)bounds
|
||||
{
|
||||
[super setBounds:bounds];
|
||||
[self dockClosestSectionHeader];
|
||||
}
|
||||
|
||||
- (void)dockClosestSectionHeader
|
||||
{
|
||||
UIView *contentView = [self contentView];
|
||||
if (_stickyHeaderIndices.count == 0 || !contentView) {
|
||||
CGFloat scrollTop = self.bounds.origin.y + self.contentInset.top;
|
||||
|
||||
// Find the section headers that need to be docked
|
||||
__block UIView *previousHeader = nil;
|
||||
__block UIView *currentHeader = nil;
|
||||
__block UIView *nextHeader = nil;
|
||||
NSInteger subviewCount = contentView.reactSubviews.count;
|
||||
[_stickyHeaderIndices enumerateIndexesWithOptions:0 usingBlock:^(NSUInteger idx, BOOL *stop) {
|
||||
|
||||
if (idx >= subviewCount) {
|
||||
RCTLogError(@"Sticky header index %zd was outside the range {0, %zd}", idx, subviewCount);
|
||||
return;
|
||||
}
|
||||
|
||||
UIView *header = contentView.reactSubviews[idx];
|
||||
|
||||
// If nextHeader not yet found, search for docked headers
|
||||
if (!nextHeader) {
|
||||
CGFloat height = header.bounds.size.height;
|
||||
CGFloat top = header.center.y - height * header.layer.anchorPoint.y;
|
||||
if (top > scrollTop) {
|
||||
nextHeader = header;
|
||||
} else {
|
||||
previousHeader = currentHeader;
|
||||
currentHeader = header;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset transforms for header views
|
||||
header.transform = CGAffineTransformIdentity;
|
||||
header.layer.zPosition = ZINDEX_DEFAULT;
|
||||
|
||||
}];
|
||||
|
||||
// If no docked header, bail out
|
||||
if (!currentHeader) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find the section header that needs to be docked
|
||||
NSInteger firstIndexInView = [[_stickyHeaderIndices firstObject] integerValue] + 1;
|
||||
CGRect scrollBounds = self.bounds;
|
||||
scrollBounds.origin.x += self.contentInset.left;
|
||||
scrollBounds.origin.y += self.contentInset.top;
|
||||
|
||||
NSInteger i = 0;
|
||||
for (UIView *subview in contentView.reactSubviews) {
|
||||
CGRect rowFrame = [RCTCustomScrollView _calculateUntransformedFrame:subview];
|
||||
if (CGRectIntersectsRect(scrollBounds, rowFrame)) {
|
||||
firstIndexInView = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
// Adjust current header to hug the top of the screen
|
||||
CGFloat currentFrameHeight = currentHeader.bounds.size.height;
|
||||
CGFloat currentFrameTop = currentHeader.center.y - currentFrameHeight * currentHeader.layer.anchorPoint.y;
|
||||
CGFloat yOffset = scrollTop - currentFrameTop;
|
||||
if (nextHeader) {
|
||||
// The next header nudges the current header out of the way when it reaches
|
||||
// the top of the screen
|
||||
CGFloat nextFrameHeight = nextHeader.bounds.size.height;
|
||||
CGFloat nextFrameTop = nextHeader.center.y - nextFrameHeight * nextHeader.layer.anchorPoint.y;
|
||||
CGFloat overlap = currentFrameHeight - (nextFrameTop - scrollTop);
|
||||
yOffset -= MAX(0, overlap);
|
||||
}
|
||||
NSInteger stickyHeaderii = 0;
|
||||
for (NSNumber *stickyHeaderI in _stickyHeaderIndices) {
|
||||
if ([stickyHeaderI integerValue] > firstIndexInView) {
|
||||
break;
|
||||
}
|
||||
stickyHeaderii++;
|
||||
}
|
||||
stickyHeaderii = MAX(0, stickyHeaderii - 1);
|
||||
|
||||
// Set up transforms for the various section headers
|
||||
NSInteger currentlyDockedIndex = [_stickyHeaderIndices[stickyHeaderii] integerValue];
|
||||
NSInteger previouslyDockedIndex = stickyHeaderii > 0 ? [_stickyHeaderIndices[stickyHeaderii-1] integerValue] : -1;
|
||||
NSInteger nextDockedIndex = (stickyHeaderii < _stickyHeaderIndices.count - 1) ?
|
||||
[_stickyHeaderIndices[stickyHeaderii + 1] integerValue] : -1;
|
||||
|
||||
UIView *currentHeader = contentView.reactSubviews[currentlyDockedIndex];
|
||||
UIView *previousHeader = previouslyDockedIndex >= 0 ? contentView.reactSubviews[previouslyDockedIndex] : nil;
|
||||
CGRect curFrame = [RCTCustomScrollView _calculateUntransformedFrame:currentHeader];
|
||||
currentHeader.transform = CGAffineTransformMakeTranslation(0, yOffset);
|
||||
currentHeader.layer.zPosition = ZINDEX_STICKY_HEADER;
|
||||
|
||||
if (previousHeader) {
|
||||
// the previous header is offset to sit right above the currentlyDockedHeader's initial position
|
||||
// (so it scrolls away nicely once the currentHeader locks into position)
|
||||
CGRect previousFrame = [RCTCustomScrollView _calculateUntransformedFrame:previousHeader];
|
||||
CGFloat yOffset = curFrame.origin.y - previousFrame.origin.y - previousFrame.size.height;
|
||||
// The previous header sits right above the currentHeader's initial position
|
||||
// so it scrolls away nicely once the currentHeader has locked into place
|
||||
CGFloat previousFrameHeight = previousHeader.bounds.size.height;
|
||||
CGFloat targetCenter = currentFrameTop - previousFrameHeight * (1.0 - previousHeader.layer.anchorPoint.y);
|
||||
yOffset = targetCenter - previousHeader.center.y;
|
||||
previousHeader.transform = CGAffineTransformMakeTranslation(0, yOffset);
|
||||
previousHeader.layer.zPosition = ZINDEX_STICKY_HEADER;
|
||||
}
|
||||
|
||||
UIView *nextHeader = nextDockedIndex >= 0 ? contentView.reactSubviews[nextDockedIndex] : nil;
|
||||
CGRect nextFrame = [RCTCustomScrollView _calculateUntransformedFrame:nextHeader];
|
||||
|
||||
if (curFrame.origin.y < scrollBounds.origin.y) {
|
||||
// scrolled off (or being scrolled off) the top of the screen
|
||||
CGFloat yOffset = 0;
|
||||
if (nextHeader && nextFrame.origin.y < scrollBounds.origin.y + curFrame.size.height) {
|
||||
// next frame is bumping me off if scrolling down (or i'm bumping the next one off if scrolling up)
|
||||
yOffset = nextFrame.origin.y - curFrame.origin.y - curFrame.size.height;
|
||||
} else {
|
||||
// standard sticky header position
|
||||
yOffset = scrollBounds.origin.y - curFrame.origin.y;
|
||||
}
|
||||
currentHeader.transform = CGAffineTransformMakeTranslation(0, yOffset);
|
||||
currentHeader.layer.zPosition = ZINDEX_STICKY_HEADER;
|
||||
} else {
|
||||
// i'm the current header but in the viewport, so just scroll in normal position
|
||||
currentHeader.transform = CGAffineTransformIdentity;
|
||||
currentHeader.layer.zPosition = ZINDEX_DEFAULT;
|
||||
}
|
||||
|
||||
// in our setup, 'next header' will always just scroll with the page
|
||||
if (nextHeader) {
|
||||
nextHeader.transform = CGAffineTransformIdentity;
|
||||
nextHeader.layer.zPosition = ZINDEX_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
+ (CGRect)_calculateUntransformedFrame:(UIView *)view
|
||||
{
|
||||
CGRect frame = CGRectNull;
|
||||
if (view) {
|
||||
frame.size = view.bounds.size;
|
||||
frame.origin = CGPointMake(view.layer.position.x - view.bounds.size.width * view.layer.anchorPoint.x, view.layer.position.y - view.bounds.size.height * view.layer.anchorPoint.y);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -312,7 +287,7 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
|
|||
_scrollView.centerContent = centerContent;
|
||||
}
|
||||
|
||||
- (void)setStickyHeaderIndices:(NSArray *)headerIndices
|
||||
- (void)setStickyHeaderIndices:(NSIndexSet *)headerIndices
|
||||
{
|
||||
RCTAssert(_scrollView.contentSize.width <= self.frame.size.width,
|
||||
@"sticky headers are not supported with horizontal scrolled views");
|
||||
|
@ -340,14 +315,8 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
|
|||
|
||||
- (void)setContentInset:(UIEdgeInsets)contentInset
|
||||
{
|
||||
CGPoint contentOffset = _scrollView.contentOffset;
|
||||
|
||||
_contentInset = contentInset;
|
||||
[RCTView autoAdjustInsetsForView:self
|
||||
withScrollView:_scrollView
|
||||
updateOffset:NO];
|
||||
|
||||
_scrollView.contentOffset = contentOffset;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)scrollToOffset:(CGPoint)offset
|
||||
|
@ -390,6 +359,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, RCTScrollEventTypeMove)
|
|||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||||
{
|
||||
[_scrollView dockClosestSectionHeader];
|
||||
[self updateClippedSubviews];
|
||||
|
||||
NSTimeInterval now = CACurrentMediaTime();
|
||||
|
|
|
@ -41,7 +41,7 @@ RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL)
|
|||
RCT_EXPORT_VIEW_PROPERTY(scrollsToTop, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(stickyHeaderIndices, NSNumberArray)
|
||||
RCT_EXPORT_VIEW_PROPERTY(stickyHeaderIndices, NSIndexSet)
|
||||
RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval)
|
||||
RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"chalk": "^1.0.0",
|
||||
"connect": "2.8.3",
|
||||
"debug": "~2.1.0",
|
||||
"graceful-fs": "^3.0.6",
|
||||
"image-size": "0.3.5",
|
||||
"joi": "~5.1.0",
|
||||
"jstransform": "10.1.0",
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
on run argv
|
||||
set theURL to item 1 of argv
|
||||
|
||||
tell application "Google Chrome"
|
||||
tell application "Chrome"
|
||||
activate
|
||||
|
||||
if (count every window) = 0 then
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
useGracefulFs();
|
||||
|
||||
var Activity = require('./src/Activity');
|
||||
var Server = require('./src/Server');
|
||||
|
||||
|
@ -45,3 +47,16 @@ exports.getDependencies = function(options, main) {
|
|||
return r.dependencies;
|
||||
});
|
||||
};
|
||||
|
||||
function useGracefulFs() {
|
||||
var fs = require('fs');
|
||||
var gracefulFs = require('graceful-fs');
|
||||
|
||||
// A bit sneaky but it's not straightforward to update all the
|
||||
// modules we depend on.
|
||||
Object.keys(fs).forEach(function(method) {
|
||||
if (typeof fs[method] === 'function' && gracefulFs[method]) {
|
||||
fs[method] = gracefulFs[method];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue