diff --git a/IntegrationTests/AsyncStorageTest.js b/IntegrationTests/AsyncStorageTest.js index ef7df1f1f365e5..ff3e26abca8c62 100644 --- a/IntegrationTests/AsyncStorageTest.js +++ b/IntegrationTests/AsyncStorageTest.js @@ -136,18 +136,23 @@ function testRemoveItem() { } function testMerge() { - AsyncStorage.setItem(KEY_MERGE, JSON.stringify(VAL_MERGE_1), (err1) => { - expectAsyncNoError('testMerge/setItem', err1); - AsyncStorage.mergeItem(KEY_MERGE, JSON.stringify(VAL_MERGE_2), (err2) => { - expectAsyncNoError('testMerge/mergeItem', err2); - AsyncStorage.getItem(KEY_MERGE, (err3, result) => { - expectAsyncNoError('testMerge/setItem', err3); - expectEqual(JSON.parse(result), VAL_MERGE_EXPECT, 'testMerge'); - updateMessage('objects deeply merged\nDone!'); - runTestCase('multi set and get', testOptimizedMultiGet); + if (AsyncStorage.mergeItem) { + AsyncStorage.setItem(KEY_MERGE, JSON.stringify(VAL_MERGE_1), (err1) => { + expectAsyncNoError('testMerge/setItem', err1); + AsyncStorage.mergeItem(KEY_MERGE, JSON.stringify(VAL_MERGE_2), (err2) => { + expectAsyncNoError('testMerge/mergeItem', err2); + AsyncStorage.getItem(KEY_MERGE, (err3, result) => { + expectAsyncNoError('testMerge/setItem', err3); + expectEqual(JSON.parse(result), VAL_MERGE_EXPECT, 'testMerge'); + updateMessage('objects deeply merged\nDone!'); + runTestCase('multi set and get', testOptimizedMultiGet); + }); }); }); - }); + } else { + updateMessage('AsyncStorage does not support the mergeItem method'); + runTestCase('multi set and get', testOptimizedMultiGet); + } } function testOptimizedMultiGet() { diff --git a/IntegrationTests/ImageCachePolicyTest.js b/IntegrationTests/ImageCachePolicyTest.js index 9732600712a88b..29b5e68d1d2117 100644 --- a/IntegrationTests/ImageCachePolicyTest.js +++ b/IntegrationTests/ImageCachePolicyTest.js @@ -65,7 +65,7 @@ class ImageCachePolicyTest extends React.Component { Hello this.testComplete('only-if-cached', false)} @@ -74,7 +74,7 @@ class ImageCachePolicyTest extends React.Component { /> this.testComplete('default', true)} @@ -83,7 +83,7 @@ class ImageCachePolicyTest extends React.Component { /> this.testComplete('reload', true)} @@ -92,7 +92,7 @@ class ImageCachePolicyTest extends React.Component { /> this.testComplete('force-cache', true)} diff --git a/IntegrationTests/LayoutEventsTest.js b/IntegrationTests/LayoutEventsTest.js index 220701c4e8687b..e011055c05bbfa 100644 --- a/IntegrationTests/LayoutEventsTest.js +++ b/IntegrationTests/LayoutEventsTest.js @@ -14,6 +14,7 @@ var React = require('react'); var createReactClass = require('create-react-class'); var ReactNative = require('react-native'); +var Platform = require('Platform'); var { Image, LayoutAnimation, @@ -68,13 +69,24 @@ var LayoutEventsTest = createReactClass({ }, animateViewLayout: function() { debug('animateViewLayout invoked'); - LayoutAnimation.configureNext( + + if (Platform.OS === 'macos') { + LayoutAnimation.configureNext( + LayoutAnimation.Presets.easeInEaseOut, + () => { + debug('animateViewLayout done'); + this.checkLayout(this.addWrapText); + } + ); + } else { + LayoutAnimation.configureNext( LayoutAnimation.Presets.spring, () => { debug('animateViewLayout done'); this.checkLayout(this.addWrapText); - } - ); + } + ); + } this.setState({viewStyle: {margin: 60}}); }, addWrapText: function() { diff --git a/IntegrationTests/websocket_integration_test_server.js b/IntegrationTests/websocket_integration_test_server.js index b7887f1c5e3753..c40af058afe36e 100755 --- a/IntegrationTests/websocket_integration_test_server.js +++ b/IntegrationTests/websocket_integration_test_server.js @@ -37,7 +37,12 @@ server.on('connection', (ws) => { process.exit(0); } console.log('Cookie:', ws.upgradeReq.headers.cookie); - ws.send(message + '_response'); + + if (message instanceof Buffer) { + ws.send(message); + } else { + ws.send(message + '_response'); + } }); ws.send('hello'); diff --git a/Libraries/ART/ART.xcodeproj/project.pbxproj b/Libraries/ART/ART.xcodeproj/project.pbxproj index 75eec5411710d8..40476c7f0febbe 100644 --- a/Libraries/ART/ART.xcodeproj/project.pbxproj +++ b/Libraries/ART/ART.xcodeproj/project.pbxproj @@ -43,25 +43,91 @@ 325CF7BC1E5F2ABA00AC9606 /* ARTSurfaceView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */; }; 325CF7BD1E5F2ABA00AC9606 /* ARTText.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE81AF0549300FF9E5C /* ARTText.m */; }; 325CF7BE1E5F2ABA00AC9606 /* RCTConvert+ART.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */; }; + 6431E8A01F9A30AD00BA05C9 /* ARTTextFrame.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AE91AF0549300FF9E5C /* ARTTextFrame.h */; }; + 6431E8A11F9A30AD00BA05C9 /* RCTConvert+ART.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AF61AF0549300FF9E5C /* RCTConvert+ART.h */; }; + 6431E8A21F9A30B300BA05C9 /* ARTSurfaceView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AE51AF0549300FF9E5C /* ARTSurfaceView.h */; }; + 6431E8A31F9A30B700BA05C9 /* ARTContainer.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68ADC1AF0549300FF9E5C /* ARTContainer.h */; }; + 6431E8A41F9A30BA00BA05C9 /* ARTCGFloatArray.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68ADB1AF0549300FF9E5C /* ARTCGFloatArray.h */; }; + 6431E8A51F9A30BD00BA05C9 /* ARTBrush.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AEB1AF0549300FF9E5C /* ARTBrush.h */; }; + 6431E8A61F9A312000BA05C9 /* ARTBrush.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AEB1AF0549300FF9E5C /* ARTBrush.h */; }; + 6431E8A71F9A312000BA05C9 /* ARTCGFloatArray.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68ADB1AF0549300FF9E5C /* ARTCGFloatArray.h */; }; + 6431E8A81F9A312000BA05C9 /* ARTContainer.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68ADC1AF0549300FF9E5C /* ARTContainer.h */; }; + 6431E8A91F9A312000BA05C9 /* ARTSurfaceView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AE51AF0549300FF9E5C /* ARTSurfaceView.h */; }; + 6431E8AA1F9A312000BA05C9 /* ARTTextFrame.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AE91AF0549300FF9E5C /* ARTTextFrame.h */; }; + 6431E8AB1F9A312000BA05C9 /* RCTConvert+ART.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AF61AF0549300FF9E5C /* RCTConvert+ART.h */; }; + 6431E8AC1F9A316D00BA05C9 /* ARTBrush.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AEB1AF0549300FF9E5C /* ARTBrush.h */; }; + 6431E8AD1F9A316D00BA05C9 /* ARTCGFloatArray.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68ADB1AF0549300FF9E5C /* ARTCGFloatArray.h */; }; + 6431E8AE1F9A316D00BA05C9 /* ARTContainer.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68ADC1AF0549300FF9E5C /* ARTContainer.h */; }; + 6431E8AF1F9A316D00BA05C9 /* ARTSurfaceView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AE51AF0549300FF9E5C /* ARTSurfaceView.h */; }; + 6431E8B01F9A316E00BA05C9 /* ARTTextFrame.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AE91AF0549300FF9E5C /* ARTTextFrame.h */; }; + 6431E8B11F9A316E00BA05C9 /* RCTConvert+ART.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 0CF68AF61AF0549300FF9E5C /* RCTConvert+ART.h */; }; + 647647631F0BC33500C2D89B /* ARTTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B041AF0549300FF9E5C /* ARTTextManager.m */; }; + 647647641F0BC33500C2D89B /* ARTGroupManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFA1AF0549300FF9E5C /* ARTGroupManager.m */; }; + 647647651F0BC33500C2D89B /* ARTPattern.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF01AF0549300FF9E5C /* ARTPattern.m */; }; + 647647661F0BC33500C2D89B /* ARTText.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE81AF0549300FF9E5C /* ARTText.m */; }; + 647647671F0BC33500C2D89B /* ARTNodeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFC1AF0549300FF9E5C /* ARTNodeManager.m */; }; + 647647681F0BC33500C2D89B /* ARTGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68ADE1AF0549300FF9E5C /* ARTGroup.m */; }; + 647647691F0BC33500C2D89B /* ARTRenderableManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFE1AF0549300FF9E5C /* ARTRenderableManager.m */; }; + 6476476A1F0BC33500C2D89B /* ARTSurfaceView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE61AF0549300FF9E5C /* ARTSurfaceView.m */; }; + 6476476B1F0BC33500C2D89B /* ARTRadialGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF21AF0549300FF9E5C /* ARTRadialGradient.m */; }; + 6476476C1F0BC33500C2D89B /* ARTSurfaceViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B021AF0549300FF9E5C /* ARTSurfaceViewManager.m */; }; + 6476476D1F0BC33500C2D89B /* ARTShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE41AF0549300FF9E5C /* ARTShape.m */; }; + 6476476E1F0BC33500C2D89B /* ARTRenderable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE21AF0549300FF9E5C /* ARTRenderable.m */; }; + 6476476F1F0BC33500C2D89B /* RCTConvert+ART.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF71AF0549300FF9E5C /* RCTConvert+ART.m */; }; + 647647701F0BC33500C2D89B /* ARTNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AE01AF0549300FF9E5C /* ARTNode.m */; }; + 647647711F0BC33500C2D89B /* ARTSolidColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF41AF0549300FF9E5C /* ARTSolidColor.m */; }; + 647647721F0BC33500C2D89B /* ARTLinearGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEE1AF0549300FF9E5C /* ARTLinearGradient.m */; }; + 647647731F0BC33500C2D89B /* ARTBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AEC1AF0549300FF9E5C /* ARTBrush.m */; }; + 647647741F0BC33500C2D89B /* ARTShapeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B001AF0549300FF9E5C /* ARTShapeManager.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ - 0CF68ABF1AF0540F00FF9E5C /* CopyFiles */ = { + 0CF68ABF1AF0540F00FF9E5C /* Copy Headers */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = "include/$(PRODUCT_NAME)"; dstSubfolderSpec = 16; files = ( + 6431E8AC1F9A316D00BA05C9 /* ARTBrush.h in Copy Headers */, + 6431E8AD1F9A316D00BA05C9 /* ARTCGFloatArray.h in Copy Headers */, + 6431E8AE1F9A316D00BA05C9 /* ARTContainer.h in Copy Headers */, + 6431E8AF1F9A316D00BA05C9 /* ARTSurfaceView.h in Copy Headers */, + 6431E8B01F9A316E00BA05C9 /* ARTTextFrame.h in Copy Headers */, + 6431E8B11F9A316E00BA05C9 /* RCTConvert+ART.h in Copy Headers */, ); + name = "Copy Headers"; runOnlyForDeploymentPostprocessing = 0; }; - 323A12851E5F266B004975B8 /* CopyFiles */ = { + 323A12851E5F266B004975B8 /* Copy Headers */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; - dstPath = "include/$(PRODUCT_NAME)"; + dstPath = include/ART; dstSubfolderSpec = 16; files = ( + 6431E8A61F9A312000BA05C9 /* ARTBrush.h in Copy Headers */, + 6431E8A71F9A312000BA05C9 /* ARTCGFloatArray.h in Copy Headers */, + 6431E8A81F9A312000BA05C9 /* ARTContainer.h in Copy Headers */, + 6431E8A91F9A312000BA05C9 /* ARTSurfaceView.h in Copy Headers */, + 6431E8AA1F9A312000BA05C9 /* ARTTextFrame.h in Copy Headers */, + 6431E8AB1F9A312000BA05C9 /* RCTConvert+ART.h in Copy Headers */, ); + name = "Copy Headers"; + runOnlyForDeploymentPostprocessing = 0; + }; + 647647761F0BC33500C2D89B /* Copy Headers */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/ART; + dstSubfolderSpec = 16; + files = ( + 6431E8A01F9A30AD00BA05C9 /* ARTTextFrame.h in Copy Headers */, + 6431E8A11F9A30AD00BA05C9 /* RCTConvert+ART.h in Copy Headers */, + 6431E8A21F9A30B300BA05C9 /* ARTSurfaceView.h in Copy Headers */, + 6431E8A51F9A30BD00BA05C9 /* ARTBrush.h in Copy Headers */, + 6431E8A41F9A30BA00BA05C9 /* ARTCGFloatArray.h in Copy Headers */, + 6431E8A31F9A30B700BA05C9 /* ARTContainer.h in Copy Headers */, + ); + name = "Copy Headers"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ @@ -108,6 +174,7 @@ 0CF68B031AF0549300FF9E5C /* ARTTextManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTTextManager.h; sourceTree = ""; }; 0CF68B041AF0549300FF9E5C /* ARTTextManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTTextManager.m; sourceTree = ""; }; 323A12871E5F266B004975B8 /* libART-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libART-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6476477A1F0BC33500C2D89B /* libART-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libART-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -125,6 +192,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 647647751F0BC33500C2D89B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -162,6 +236,7 @@ children = ( 0CF68AC11AF0540F00FF9E5C /* libART.a */, 323A12871E5F266B004975B8 /* libART-tvOS.a */, + 6476477A1F0BC33500C2D89B /* libART-macOS.a */, ); name = Products; sourceTree = ""; @@ -211,7 +286,7 @@ buildPhases = ( 0CF68ABD1AF0540F00FF9E5C /* Sources */, 0CF68ABE1AF0540F00FF9E5C /* Frameworks */, - 0CF68ABF1AF0540F00FF9E5C /* CopyFiles */, + 0CF68ABF1AF0540F00FF9E5C /* Copy Headers */, ); buildRules = ( ); @@ -228,7 +303,7 @@ buildPhases = ( 323A12831E5F266B004975B8 /* Sources */, 323A12841E5F266B004975B8 /* Frameworks */, - 323A12851E5F266B004975B8 /* CopyFiles */, + 323A12851E5F266B004975B8 /* Copy Headers */, ); buildRules = ( ); @@ -239,6 +314,23 @@ productReference = 323A12871E5F266B004975B8 /* libART-tvOS.a */; productType = "com.apple.product-type.library.static"; }; + 647647611F0BC33500C2D89B /* ART-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 647647771F0BC33500C2D89B /* Build configuration list for PBXNativeTarget "ART-macOS" */; + buildPhases = ( + 647647621F0BC33500C2D89B /* Sources */, + 647647751F0BC33500C2D89B /* Frameworks */, + 647647761F0BC33500C2D89B /* Copy Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ART-macOS"; + productName = ART; + productReference = 6476477A1F0BC33500C2D89B /* libART-macOS.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -270,6 +362,7 @@ targets = ( 0CF68AC01AF0540F00FF9E5C /* ART */, 323A12861E5F266B004975B8 /* ART-tvOS */, + 647647611F0BC33500C2D89B /* ART-macOS */, ); }; /* End PBXProject section */ @@ -325,6 +418,31 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 647647621F0BC33500C2D89B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 647647631F0BC33500C2D89B /* ARTTextManager.m in Sources */, + 647647641F0BC33500C2D89B /* ARTGroupManager.m in Sources */, + 647647651F0BC33500C2D89B /* ARTPattern.m in Sources */, + 647647661F0BC33500C2D89B /* ARTText.m in Sources */, + 647647671F0BC33500C2D89B /* ARTNodeManager.m in Sources */, + 647647681F0BC33500C2D89B /* ARTGroup.m in Sources */, + 647647691F0BC33500C2D89B /* ARTRenderableManager.m in Sources */, + 6476476A1F0BC33500C2D89B /* ARTSurfaceView.m in Sources */, + 6476476B1F0BC33500C2D89B /* ARTRadialGradient.m in Sources */, + 6476476C1F0BC33500C2D89B /* ARTSurfaceViewManager.m in Sources */, + 6476476D1F0BC33500C2D89B /* ARTShape.m in Sources */, + 6476476E1F0BC33500C2D89B /* ARTRenderable.m in Sources */, + 6476476F1F0BC33500C2D89B /* RCTConvert+ART.m in Sources */, + 647647701F0BC33500C2D89B /* ARTNode.m in Sources */, + 647647711F0BC33500C2D89B /* ARTSolidColor.m in Sources */, + 647647721F0BC33500C2D89B /* ARTLinearGradient.m in Sources */, + 647647731F0BC33500C2D89B /* ARTBrush.m in Sources */, + 647647741F0BC33500C2D89B /* ARTShapeManager.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -362,6 +480,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -397,6 +516,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -455,6 +575,24 @@ }; name = Release; }; + 647647781F0BC33500C2D89B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + 647647791F0BC33500C2D89B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -485,6 +623,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 647647771F0BC33500C2D89B /* Build configuration list for PBXNativeTarget "ART-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 647647781F0BC33500C2D89B /* Debug */, + 647647791F0BC33500C2D89B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 0CF68AB91AF0540F00FF9E5C /* Project object */; diff --git a/Libraries/ART/ARTSurfaceView.h b/Libraries/ART/ARTSurfaceView.h index 8be8d95040c146..6af59c26ed40c2 100644 --- a/Libraries/ART/ARTSurfaceView.h +++ b/Libraries/ART/ARTSurfaceView.h @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import #import "ARTContainer.h" diff --git a/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj b/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj index 40d08248bc3c8d..c8f0855093cc9d 100644 --- a/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj +++ b/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj/project.pbxproj @@ -8,12 +8,14 @@ /* Begin PBXBuildFile section */ 14C644C41AB0DFC900DE3C65 /* RCTActionSheetManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C644C21AB0DFC900DE3C65 /* RCTActionSheetManager.m */; }; + 649D87CE1F69D9BC0005AF18 /* RCTActionSheetManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14C644C21AB0DFC900DE3C65 /* RCTActionSheetManager.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 134814201AA4EA6300B7C361 /* libRCTActionSheet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTActionSheet.a; sourceTree = BUILT_PRODUCTS_DIR; }; 14C644C11AB0DFC900DE3C65 /* RCTActionSheetManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTActionSheetManager.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 14C644C21AB0DFC900DE3C65 /* RCTActionSheetManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTActionSheetManager.m; sourceTree = ""; }; + 649D87D21F69D9BC0005AF18 /* libRCTActionSheet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTActionSheet.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ @@ -31,6 +33,7 @@ 14C644C11AB0DFC900DE3C65 /* RCTActionSheetManager.h */, 14C644C21AB0DFC900DE3C65 /* RCTActionSheetManager.m */, 134814211AA4EA7D00B7C361 /* Products */, + 649D87D21F69D9BC0005AF18 /* libRCTActionSheet.a */, ); indentWidth = 2; sourceTree = ""; @@ -55,6 +58,21 @@ productReference = 134814201AA4EA6300B7C361 /* libRCTActionSheet.a */; productType = "com.apple.product-type.library.static"; }; + 649D87CC1F69D9BC0005AF18 /* RCTActionSheet-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 649D87CF1F69D9BC0005AF18 /* Build configuration list for PBXNativeTarget "RCTActionSheet-macOS" */; + buildPhases = ( + 649D87CD1F69D9BC0005AF18 /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RCTActionSheet-macOS"; + productName = RCTDataManager; + productReference = 649D87D21F69D9BC0005AF18 /* libRCTActionSheet.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -82,6 +100,7 @@ projectRoot = ""; targets = ( 58B511DA1A9E6C8500147676 /* RCTActionSheet */, + 649D87CC1F69D9BC0005AF18 /* RCTActionSheet-macOS */, ); }; /* End PBXProject section */ @@ -95,6 +114,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 649D87CD1F69D9BC0005AF18 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 649D87CE1F69D9BC0005AF18 /* RCTActionSheetManager.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -134,6 +161,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -175,6 +203,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -208,6 +237,30 @@ }; name = Release; }; + 649D87D01F69D9BC0005AF18 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTActionSheet; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 649D87D11F69D9BC0005AF18 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTActionSheet; + RUN_CLANG_STATIC_ANALYZER = NO; + SDKROOT = macosx; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -229,6 +282,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 649D87CF1F69D9BC0005AF18 /* Build configuration list for PBXNativeTarget "RCTActionSheet-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 649D87D01F69D9BC0005AF18 /* Debug */, + 649D87D11F69D9BC0005AF18 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 58B511D31A9E6C8500147676 /* Project object */; diff --git a/Libraries/ActionSheetIOS/RCTActionSheetManager.h b/Libraries/ActionSheetIOS/RCTActionSheetManager.h index 313939149fd1a6..577216f785630c 100644 --- a/Libraries/ActionSheetIOS/RCTActionSheetManager.h +++ b/Libraries/ActionSheetIOS/RCTActionSheetManager.h @@ -7,9 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import - #import +#import @interface RCTActionSheetManager : NSObject diff --git a/Libraries/ActionSheetIOS/RCTActionSheetManager.m b/Libraries/ActionSheetIOS/RCTActionSheetManager.m index 9f550f77af32a4..d292b198e4934c 100644 --- a/Libraries/ActionSheetIOS/RCTActionSheetManager.m +++ b/Libraries/ActionSheetIOS/RCTActionSheetManager.m @@ -15,7 +15,12 @@ #import #import -@interface RCTActionSheetManager () +@interface RCTActionSheetManager () +#if !TARGET_OS_OSX + +#else + +#endif @end @implementation RCTActionSheetManager @@ -23,6 +28,12 @@ @implementation RCTActionSheetManager // Use NSMapTable, as UIAlertViews do not implement // which is required for NSDictionary keys NSMapTable *_callbacks; +#if TARGET_OS_OSX + NSArray *_excludedActivities; + NSString *_sharingSubject; + RCTResponseErrorBlock _failureCallback; + RCTResponseSenderBlock _successCallback; +#endif } RCT_EXPORT_MODULE() @@ -34,6 +45,7 @@ - (dispatch_queue_t)methodQueue return dispatch_get_main_queue(); } +#if !TARGET_OS_OSX /* * The `anchor` option takes a view to set as the anchor for the share * popup to point to, on iPads running iOS 8. If it is not passed, it @@ -49,25 +61,37 @@ - (CGRect)sourceRectInView:(UIView *)sourceView return (CGRect){sourceView.center, {1, 1}}; } } +#endif RCT_EXPORT_METHOD(showActionSheetWithOptions:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback) { +#if !TARGET_OS_OSX if (RCTRunningInAppExtension()) { RCTLogError(@"Unable to show action sheet from app extension"); return; } +#endif if (!_callbacks) { _callbacks = [NSMapTable strongToStrongObjectsMapTable]; } NSString *title = [RCTConvert NSString:options[@"title"]]; - NSString *message = [RCTConvert NSString:options[@"message"]]; NSArray *buttons = [RCTConvert NSStringArray:options[@"options"]]; - NSInteger destructiveButtonIndex = options[@"destructiveButtonIndex"] ? [RCTConvert NSInteger:options[@"destructiveButtonIndex"]] : -1; NSInteger cancelButtonIndex = options[@"cancelButtonIndex"] ? [RCTConvert NSInteger:options[@"cancelButtonIndex"]] : -1; - + + /* + * The `anchor` option takes a view to set as the anchor for the share + * popup to point to, on iPads running iOS 8. If it is not passed, it + * defaults to centering the share popup on screen without any arrows. + */ + NSNumber *anchorViewTag = [RCTConvert NSNumber:options[@"anchor"]]; + +#if !TARGET_OS_OSX + NSInteger destructiveButtonIndex = options[@"destructiveButtonIndex"] ? [RCTConvert NSInteger:options[@"destructiveButtonIndex"]] : -1; + NSString *message = [RCTConvert NSString:options[@"message"]]; + UIViewController *controller = RCTPresentedViewController(); if (controller == nil) { @@ -75,12 +99,6 @@ - (CGRect)sourceRectInView:(UIView *)sourceView return; } - /* - * The `anchor` option takes a view to set as the anchor for the share - * popup to point to, on iPads running iOS 8. If it is not passed, it - * defaults to centering the share popup on screen without any arrows. - */ - NSNumber *anchorViewTag = [RCTConvert NSNumber:options[@"anchor"]]; UIView *sourceView = controller.view; CGRect sourceRect = [self sourceRectInView:sourceView anchorViewTag:anchorViewTag]; @@ -117,16 +135,51 @@ - (CGRect)sourceRectInView:(UIView *)sourceView [controller presentViewController:alertController animated:YES completion:nil]; alertController.view.tintColor = [RCTConvert UIColor:options[@"tintColor"]]; +#else + NSMenu *menu = [[NSMenu alloc] initWithTitle:title ?: @""]; + [_callbacks setObject:callback forKey:menu]; + for (NSInteger index = 0; index < buttons.count; index++) { + if (index == cancelButtonIndex) { + //NSMenu doesn't require a cancel button + continue; + } + + NSString *option = buttons[index]; + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:option action:@selector(menuItemDidTap:) keyEquivalent:@""]; + item.tag = index; + item.target = self; + [menu addItem:item]; + } + + NSPoint origin = NSZeroPoint; + NSEvent *event = nil; + RCTPlatformView *view = nil; + if (anchorViewTag) { + view = [self.bridge.uiManager viewForReactTag:anchorViewTag]; + event = [view.window currentEvent]; + } + if (event && view) { + origin = [view convertPoint:[event locationInWindow] fromView:nil]; + } else if (view) { + origin = NSMakePoint(NSMidX(view.superview.frame), NSMidY(view.superview.frame)); + } else { + origin = [NSEvent mouseLocation]; + } + + [menu popUpMenuPositioningItem:menu.itemArray.firstObject atLocation:origin inView:view.superview]; +#endif } RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options failureCallback:(RCTResponseErrorBlock)failureCallback successCallback:(RCTResponseSenderBlock)successCallback) { +#if !TARGET_OS_OSX if (RCTRunningInAppExtension()) { RCTLogError(@"Unable to show action sheet from app extension"); return; } +#endif NSMutableArray *items = [NSMutableArray array]; NSString *message = [RCTConvert NSString:options[@"message"]]; @@ -154,6 +207,7 @@ - (CGRect)sourceRectInView:(UIView *)sourceView return; } +#if !TARGET_OS_OSX UIActivityViewController *shareController = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil]; NSString *subject = [RCTConvert NSString:options[@"subject"]]; @@ -186,12 +240,41 @@ - (CGRect)sourceRectInView:(UIView *)sourceView [controller presentViewController:shareController animated:YES completion:nil]; shareController.view.tintColor = [RCTConvert UIColor:options[@"tintColor"]]; +#else + NSMutableArray *excludedTypes = [NSMutableArray array]; + for (NSString *excludeActivityType in [RCTConvert NSStringArray:options[@"excludedActivityTypes"]]) { + NSSharingService *sharingService = [NSSharingService sharingServiceNamed:excludeActivityType]; + if (sharingService) { + [excludedTypes addObject:sharingService]; + } + } + _excludedActivities = excludedTypes.copy; + _sharingSubject = [RCTConvert NSString:options[@"subject"]]; + _failureCallback = failureCallback; + _successCallback = successCallback; + RCTPlatformView *view = nil; + NSNumber *anchorViewTag = [RCTConvert NSNumber:options[@"anchor"]]; + if (anchorViewTag) { + view = [self.bridge.uiManager viewForReactTag:anchorViewTag]; + } + NSView *contentView = view ?: NSApp.keyWindow.contentView; + NSSharingServicePicker *picker = [[NSSharingServicePicker alloc] initWithItems:items]; + picker.delegate = self; + [picker showRelativeToRect:contentView.bounds ofView:contentView preferredEdge:0]; +#endif } +#if !TARGET_OS_OSX #pragma mark UIActionSheetDelegate Methods - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { +#else +- (void)menuItemDidTap:(NSMenuItem*)menuItem + { + NSMenu *actionSheet = menuItem.menu; + NSInteger buttonIndex = menuItem.tag; +#endif RCTResponseSenderBlock callback = [_callbacks objectForKey:actionSheet]; if (callback) { callback(@[@(buttonIndex)]); @@ -200,5 +283,43 @@ - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger RCTLogWarn(@"No callback registered for action sheet: %@", actionSheet.title); } } + +#if TARGET_OS_OSX + +#pragma mark - NSSharingServicePickerDelegate methods + +- (void)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker didChooseSharingService:(NSSharingService *)service +{ + if (service){ + service.subject = _sharingSubject; + } +} + +- (void)sharingService:(NSSharingService *)sharingService didFailToShareItems:(NSArray *)items error:(NSError *)error +{ + _failureCallback(error); +} +- (void)sharingService:(NSSharingService *)sharingService didShareItems:(NSArray *)items +{ + NSRange range = [sharingService.description rangeOfString:@"\\[com.apple.share.*\\]" options:NSRegularExpressionSearch]; + if (range.location == NSNotFound) { + _successCallback(@[@NO, (id)kCFNull]); + return; + } + range.location++; // Start after [ + range.length -= 2; // Remove both [ and ] + NSString *activityType = [sharingService.description substringWithRange:range]; + _successCallback(@[@YES, RCTNullIfNil(activityType)]); +} + +- (NSArray *)sharingServicePicker:(__unused NSSharingServicePicker *)sharingServicePicker sharingServicesForItems:(__unused NSArray *)items proposedSharingServices:(NSArray *)proposedServices +{ + return [proposedServices filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSSharingService *service, __unused NSDictionary * _Nullable bindings) { + return ![_excludedActivities containsObject:service]; + }]]; +} + +#endif + @end diff --git a/Libraries/Alert/Alert.js b/Libraries/Alert/Alert.js index bd7d46f51bea0c..4d7e8db9f8a97f 100644 --- a/Libraries/Alert/Alert.js +++ b/Libraries/Alert/Alert.js @@ -12,6 +12,7 @@ 'use strict'; const AlertIOS = require('AlertIOS'); +const AlertMacOS = require('AlertMacOS'); const NativeModules = require('NativeModules'); const Platform = require('Platform'); @@ -44,6 +45,11 @@ type Options = { * On iOS you can specify any number of buttons. Each button can optionally * specify a style, which is one of 'default', 'cancel' or 'destructive'. * + * ## macOS + * + * On macOS you can specify any number of buttons. The alerts have a default + * warning style and run modally as a sheet. + * * ## Android * * On Android at most three buttons can be specified. Android has a concept @@ -63,7 +69,7 @@ type Options = { * * Example usage: * ``` - * // Works on both iOS and Android + * // Works on iOS, macOS and Android * Alert.alert( * 'Alert Title', * 'My Alert Msg', @@ -87,11 +93,13 @@ class Alert { ): void { if (Platform.OS === 'ios') { if (typeof type !== 'undefined') { - console.warn('Alert.alert() with a 5th "type" parameter is deprecated and will be removed. Use AlertIOS.prompt() instead.'); + console.warn('Alert.alert() with a 4th "type" parameter is deprecated and will be removed. Use AlertIOS.prompt() instead.'); AlertIOS.alert(title, message, buttons, type); return; } AlertIOS.alert(title, message, buttons); + } else if (Platform.OS === 'macos') { + AlertMacOS.alert(title, message, buttons); } else if (Platform.OS === 'android') { AlertAndroid.alert(title, message, buttons, options); } diff --git a/Libraries/Alert/AlertMacOS.js b/Libraries/Alert/AlertMacOS.js new file mode 100644 index 00000000000000..ec6f4973500943 --- /dev/null +++ b/Libraries/Alert/AlertMacOS.js @@ -0,0 +1,204 @@ +/** + * 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 AlertMacOS + * @flow + * @jsdoc + */ +'use strict'; + +import type { AlertType } from 'AlertIOS'; + +var RCTAlertManager = require('NativeModules').AlertManager; + +/** + * Array or buttons + * @typedef {Array} ButtonsArray + * @property {string=} text Button label + * @property {Function=} onPress Callback function when button pressed + */ +export type ButtonsArray = Array<{ + /** + * Button label + */ + text?: string, + /** + * Callback function when button pressed + */ + onPress?: ?Function, +}>; + +/** + * Array of defaults input values + * @typedef {Array} DefaultsInputArray + * @property {string=} default input + * @property {string=} placeholder input + */ +export type DefaultInputsArray = Array<{ + /** + * Default input + */ + default?: string, + /** + * Placeholder input + */ + placeholder?: string, +}>; + +/** + * @description + * `AlertMacOS` provides functionality to create a macOS alert dialog with a + * message or create a prompt for user input. + * + * Creating an macOS alert: + * + * ``` + * AlertMacOS.alert( + * 'Sync Complete', + * 'All your data are belong to us.' + * ); + * ``` + * + * Creating an macOS prompt: + * + * ``` + * AlertMacOS.prompt( + * 'Enter a value', + * null, + * text => console.log("You entered "+text) + * ); + * ``` + * + * We recommend using the [`Alert.alert`](docs/alert.html) method for + * cross-platform support if you don't need to create macOS-only prompts. + * + */ +class AlertMacOS { + /** + * Create and display a popup alert. + * @static + * @method alert + * @param title The dialog's title. + * @param message An optional message that appears below + * the dialog's title. + * @param callbackOrButtons This optional argument should + * be either a single-argument function or an array of buttons. If passed + * a function, it will be called when the user taps 'OK'. + * + * If passed an array of button configurations, each button should include + * a `text` key, as well as optional `onPress` key. + * + * @example Example with custom buttons + * + * AlertMacOS.alert( + * 'Update available', + * 'Keep your app up to date to enjoy the latest features', + * [ + * {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'}, + * {text: 'Install', onPress: () => console.log('Install Pressed')}, + * ], + * ); + */ + static alert( + title: ?string, + message?: ?string, + callbackOrButtons?: ?(() => void) | ButtonsArray, + options?: Options, + ): void { + this.prompt(title, message, callbackOrButtons, 'default'); + } + + /** + * Create and display a prompt to enter some text. + * @static + * @method prompt + * @param title The dialog's title. + * @param message An optional message that appears above the text + * input. + * @param callbackOrButtons This optional argument should + * be either a single-argument function or an array of buttons. If passed + * a function, it will be called with the prompt's value when the user + * taps 'OK'. + * + * If passed an array of button configurations, each button should include + * a `text` key, as well as optional `onPress` key (see + * example). + * @param type This configures the text input. One of 'plain-text', + * 'secure-text' or 'login-password'. + * @param defaultInputs This optional argument should be an array of couple + * default value - placeholder for the input fields. + * @param modal The alert can be optionally run as an app-modal dialog, instead + * of the default presentation as a sheet. + * @param critical This optional argument should be used when it's needed to + * warn the user about severe consequences of an impending event + * (such as deleting a file). + * + * @example Example with custom buttons + * + * AlertMacOS.prompt( + * 'Enter password', + * 'Enter your password to claim your $1.5B in lottery winnings', + * [ + * {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'}, + * {text: 'OK', onPress: password => console.log('OK Pressed, password: ' + password)}, + * ], + * 'secure-text' + * ); + * + * @example Example with the default button and a custom callback + * + * AlertMacOS.prompt( + * 'Update username', + * null, + * text => console.log("Your username is "+text), + * null, + * 'default' + * ); + */ + static prompt( + title: ?string, + message?: ?string, + callbackOrButtons?: ?((text: string) => void) | ButtonsArray, + type?: ?AlertType = 'plain-text', + defaultInputs?: DefaultInputsArray, + modal?: ?boolean, + critical?: ?boolean, + ): void { + + var callbacks = []; + var buttons = []; + if (typeof callbackOrButtons === 'function') { + callbacks = [callbackOrButtons]; + } + else if (callbackOrButtons instanceof Array) { + callbackOrButtons.forEach((btn, index) => { + callbacks[index] = btn.onPress; + if (btn.text || index < (callbackOrButtons || []).length - 1) { + var btnDef = {}; + btnDef[index] = btn.text || ''; + buttons.push(btnDef); + } + }); + } + + RCTAlertManager.alertWithArgs({ + title: title || undefined, + message: message || undefined, + buttons, + type: type || undefined, + defaultInputs, + modal: modal || undefined, + critical: critical || undefined, + }, (id, value) => { + var cb = callbacks[id]; + cb && cb(value); + }); + } +} + +module.exports = AlertMacOS; diff --git a/Libraries/Alert/RCTAlertManager.macos.js b/Libraries/Alert/RCTAlertManager.macos.js new file mode 100644 index 00000000000000..409ecf5caf0430 --- /dev/null +++ b/Libraries/Alert/RCTAlertManager.macos.js @@ -0,0 +1,16 @@ +/** + * 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 RCTAlertManager + * @flow + */ +'use strict'; + +var RCTAlertManager = require('NativeModules').AlertManager; + +module.exports = RCTAlertManager; diff --git a/Libraries/Blob/RCTBlob.xcodeproj/project.pbxproj b/Libraries/Blob/RCTBlob.xcodeproj/project.pbxproj index bb5944caae6ad0..fb64b94d6baa31 100755 --- a/Libraries/Blob/RCTBlob.xcodeproj/project.pbxproj +++ b/Libraries/Blob/RCTBlob.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 6484CE4F201A74FA004275A4 /* RCTBlobManager.h in Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; }; + 6484CE51201A74FA004275A4 /* RCTBlobManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; }; + 6484CE53201A74FA004275A4 /* RCTBlobManager.m in Sources */ = {isa = PBXBuildFile; fileRef = AD9A43C21DFC7126008DC588 /* RCTBlobManager.m */; }; AD0871131E215B28007D136D /* RCTBlobManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; }; AD0871161E215EC9007D136D /* RCTBlobManager.h in Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; }; AD0871181E215ED1007D136D /* RCTBlobManager.h in Headers */ = {isa = PBXBuildFile; fileRef = AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */; }; @@ -27,6 +30,17 @@ name = "Copy Headers"; runOnlyForDeploymentPostprocessing = 0; }; + 6484CE50201A74FA004275A4 /* Copy Headers */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/RCTBlob; + dstSubfolderSpec = 16; + files = ( + 6484CE51201A74FA004275A4 /* RCTBlobManager.h in Copy Headers */, + ); + name = "Copy Headers"; + runOnlyForDeploymentPostprocessing = 0; + }; AD0871121E215B16007D136D /* Copy Headers */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -42,6 +56,7 @@ /* Begin PBXFileReference section */ 358F4ED71D1E81A9004DF814 /* libRCTBlob.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTBlob.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 6484CE57201A74FA004275A4 /* libRCTBlob-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTBlob-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; AD9A43C11DFC7126008DC588 /* RCTBlobManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBlobManager.h; sourceTree = ""; }; AD9A43C21DFC7126008DC588 /* RCTBlobManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBlobManager.m; sourceTree = ""; }; ADD01A681E09402E00F6D226 /* libRCTBlob-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTBlob-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -65,6 +80,7 @@ children = ( 358F4ED71D1E81A9004DF814 /* libRCTBlob.a */, ADD01A681E09402E00F6D226 /* libRCTBlob-tvOS.a */, + 6484CE57201A74FA004275A4 /* libRCTBlob-macOS.a */, ); name = Products; sourceTree = ""; @@ -72,6 +88,14 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 6484CE4E201A74FA004275A4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 6484CE4F201A74FA004275A4 /* RCTBlobManager.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; AD0871151E215EB7007D136D /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -108,6 +132,23 @@ productReference = 358F4ED71D1E81A9004DF814 /* libRCTBlob.a */; productType = "com.apple.product-type.library.static"; }; + 6484CE4D201A74FA004275A4 /* RCTBlob-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6484CE54201A74FA004275A4 /* Build configuration list for PBXNativeTarget "RCTBlob-macOS" */; + buildPhases = ( + 6484CE4E201A74FA004275A4 /* Headers */, + 6484CE50201A74FA004275A4 /* Copy Headers */, + 6484CE52201A74FA004275A4 /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RCTBlob-macOS"; + productName = SLKBlobs; + productReference = 6484CE57201A74FA004275A4 /* libRCTBlob-macOS.a */; + productType = "com.apple.product-type.library.static"; + }; ADD01A671E09402E00F6D226 /* RCTBlob-tvOS */ = { isa = PBXNativeTarget; buildConfigurationList = ADD01A6E1E09402E00F6D226 /* Build configuration list for PBXNativeTarget "RCTBlob-tvOS" */; @@ -157,6 +198,7 @@ targets = ( 358F4ED61D1E81A9004DF814 /* RCTBlob */, ADD01A671E09402E00F6D226 /* RCTBlob-tvOS */, + 6484CE4D201A74FA004275A4 /* RCTBlob-macOS */, ); }; /* End PBXProject section */ @@ -170,6 +212,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6484CE52201A74FA004275A4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6484CE53201A74FA004275A4 /* RCTBlobManager.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; ADD01A641E09402E00F6D226 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -219,6 +269,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -258,6 +309,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -283,6 +335,26 @@ }; name = Release; }; + 6484CE55201A74FA004275A4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 6484CE56201A74FA004275A4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Release; + }; ADD01A6F1E09402E00F6D226 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -332,6 +404,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 6484CE54201A74FA004275A4 /* Build configuration list for PBXNativeTarget "RCTBlob-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6484CE55201A74FA004275A4 /* Debug */, + 6484CE56201A74FA004275A4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; ADD01A6E1E09402E00F6D226 /* Build configuration list for PBXNativeTarget "RCTBlob-tvOS" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Libraries/Components/ActivityIndicator/ActivityIndicator.js b/Libraries/Components/ActivityIndicator/ActivityIndicator.js index c16a9f61e91908..6b50b98421418b 100644 --- a/Libraries/Components/ActivityIndicator/ActivityIndicator.js +++ b/Libraries/Components/ActivityIndicator/ActivityIndicator.js @@ -103,7 +103,7 @@ const ActivityIndicator = createReactClass({ /** * Whether the indicator should hide when not animating (true by default). * - * @platform ios + * @platform ios, macos */ hidesWhenStopped: PropTypes.bool, }, @@ -111,7 +111,7 @@ const ActivityIndicator = createReactClass({ getDefaultProps(): DefaultProps { return { animating: true, - color: Platform.OS === 'ios' ? GRAY : undefined, + color: Platform.OS === 'ios' || Platform.OS === 'macos' ? GRAY : undefined, hidesWhenStopped: true, size: 'small', }; @@ -142,7 +142,7 @@ const ActivityIndicator = createReactClass({ return ( - {Platform.OS === 'ios' ? ( + {Platform.OS === 'ios' || Platform.OS === 'macos' ? ( ) : ( @@ -167,7 +167,7 @@ const styles = StyleSheet.create({ }, }); -if (Platform.OS === 'ios') { +if (Platform.OS === 'ios' || Platform.OS === 'macos') { var RCTActivityIndicator = requireNativeComponent( 'RCTActivityIndicatorView', ActivityIndicator, diff --git a/Libraries/Components/AppleTV/TVEventHandler.macos.js b/Libraries/Components/AppleTV/TVEventHandler.macos.js new file mode 100644 index 00000000000000..dc5a1d6b94e931 --- /dev/null +++ b/Libraries/Components/AppleTV/TVEventHandler.macos.js @@ -0,0 +1,20 @@ +/** + * 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 TVEventHandler + * @flow + */ +'use strict'; + +function TVEventHandler() {} + +TVEventHandler.prototype.enable = function(component: ?any, callback: Function) {}; + +TVEventHandler.prototype.disable = function() {}; + +module.exports = TVEventHandler; diff --git a/Libraries/Components/Button.js b/Libraries/Components/Button.js index fc3f2ef5aab3ad..527f9b841d1046 100644 --- a/Libraries/Components/Button.js +++ b/Libraries/Components/Button.js @@ -17,6 +17,7 @@ const React = require('React'); const PropTypes = require('prop-types'); const StyleSheet = require('StyleSheet'); const Text = require('Text'); +const TouchableHighlight = require('TouchableHighlight'); const TouchableNativeFeedback = require('TouchableNativeFeedback'); const TouchableOpacity = require('TouchableOpacity'); const View = require('View'); @@ -56,6 +57,7 @@ class Button extends React.Component<{ onPress: () => any, color?: ?string, accessibilityLabel?: ?string, + accessibilityHint?: ?string, disabled?: ?boolean, testID?: ?string, hasTVPreferredFocus?: ?boolean, @@ -70,7 +72,11 @@ class Button extends React.Component<{ */ accessibilityLabel: PropTypes.string, /** - * Color of the text (iOS), or background color of the button (Android) + * Hint text to display blindness accessibility features + */ + accessibilityHint: PropTypes.string, + /** + * Color of the text (iOS, macOS), or background color of the button (Android) */ color: ColorPropType, /** @@ -96,6 +102,7 @@ class Button extends React.Component<{ render() { const { accessibilityLabel, + accessibilityHint, color, onPress, title, @@ -106,7 +113,7 @@ class Button extends React.Component<{ const buttonStyles = [styles.button]; const textStyles = [styles.text]; if (color) { - if (Platform.OS === 'ios') { + if (Platform.OS === 'ios' || Platform.OS === 'macos') { textStyles.push({color: color}); } else { buttonStyles.push({backgroundColor: color}); @@ -123,11 +130,12 @@ class Button extends React.Component<{ 'The title prop of a Button must be a string', ); const formattedTitle = Platform.OS === 'android' ? title.toUpperCase() : title; - const Touchable = Platform.OS === 'android' ? TouchableNativeFeedback : TouchableOpacity; + const Touchable = (Platform.OS === 'android') ? TouchableNativeFeedback : TouchableOpacity; return ( + DatePickerIOS is not supported on this platform! + + ); + } +} + +var styles = StyleSheet.create({ + dummyDatePickerIOS: { + height: 100, + width: 300, + backgroundColor: '#ffbcbc', + borderWidth: 1, + borderColor: 'red', + alignItems: 'center', + justifyContent: 'center', + margin: 10, + }, + datePickerText: { + color: '#333333', + margin: 20, + } +}); + +module.exports = DummyDatePickerIOS; diff --git a/Libraries/Components/DatePickerAndroid/DatePickerAndroid.macos.js b/Libraries/Components/DatePickerAndroid/DatePickerAndroid.macos.js new file mode 100644 index 00000000000000..afc8a19c2e8d7d --- /dev/null +++ b/Libraries/Components/DatePickerAndroid/DatePickerAndroid.macos.js @@ -0,0 +1,22 @@ +/** + * 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 DatePickerAndroid + * @flow + */ +'use strict'; + +const DatePickerAndroid = { + async open(options: Object): Promise { + return Promise.reject({ + message: 'DatePickerAndroid is not supported on this platform.' + }); + }, +}; + +module.exports = DatePickerAndroid; diff --git a/Libraries/Components/DatePickerMacOS/DatePickerMacOS.android.js b/Libraries/Components/DatePickerMacOS/DatePickerMacOS.android.js new file mode 100644 index 00000000000000..42dc3374354f12 --- /dev/null +++ b/Libraries/Components/DatePickerMacOS/DatePickerMacOS.android.js @@ -0,0 +1,46 @@ +/** + * 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 DatePickerMacOS + */ + +'use strict'; + +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var Text = require('Text'); +var View = require('View'); + +class dummyDatePickerMacOS extends React.Component { + render() { + return ( + + DatePickerMacOS is not supported on this platform! + + ); + } +} + +var styles = StyleSheet.create({ + dummyDatePickerMacOS: { + height: 100, + width: 300, + backgroundColor: '#ffbcbc', + borderWidth: 1, + borderColor: 'red', + alignItems: 'center', + justifyContent: 'center', + margin: 10, + }, + datePickerText: { + color: '#333333', + margin: 20, + } +}); + +module.exports = DummyDatePickerMacOS; diff --git a/Libraries/Components/DatePickerMacOS/DatePickerMacOS.ios.js b/Libraries/Components/DatePickerMacOS/DatePickerMacOS.ios.js new file mode 100644 index 00000000000000..b26dc6f2b8a065 --- /dev/null +++ b/Libraries/Components/DatePickerMacOS/DatePickerMacOS.ios.js @@ -0,0 +1,45 @@ +/** + * 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 DatePickerMacOS + */ +'use strict'; + +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var Text = require('Text'); +var View = require('View'); + +class DummyDatePickerMacOS extends React.Component { + render() { + return ( + + DatePickerMacOS is not supported on this platform! + + ); + } +} + +var styles = StyleSheet.create({ + dummyDatePickerMacOS: { + height: 100, + width: 300, + backgroundColor: '#ffbcbc', + borderWidth: 1, + borderColor: 'red', + alignItems: 'center', + justifyContent: 'center', + margin: 10, + }, + datePickerText: { + color: '#333333', + margin: 20, + } +}); + +module.exports = DummyDatePickerMacOS; diff --git a/Libraries/Components/DatePickerMacOS/DatePickerMacOS.macos.js b/Libraries/Components/DatePickerMacOS/DatePickerMacOS.macos.js new file mode 100644 index 00000000000000..d6327e2ce91e9a --- /dev/null +++ b/Libraries/Components/DatePickerMacOS/DatePickerMacOS.macos.js @@ -0,0 +1,165 @@ +/** + * 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 DatePickerMacOS + * @flow + * + * This is a controlled component version of RCTDatePicker + */ +'use strict'; + +const NativeMethodsMixin = require('NativeMethodsMixin'); +const React = require('React'); +const PropTypes = require('prop-types'); +const StyleSheet = require('StyleSheet'); +const View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); + +const createReactClass = require('create-react-class'); +const requireNativeComponent = require('requireNativeComponent'); + +type DefaultProps = { + mode: 'single' | 'range', +}; + +type Event = Object; + +/** + * Use `DatePickerMacOS` to render a date/time picker (selector) on macOS. This is + * a controlled component, so you must hook in to the `onDateChange` callback + * and update the `date` prop in order for the component to update, otherwise + * the user's change will be reverted immediately to reflect `props.date` as the + * source of truth. + */ +// $FlowFixMe(>=0.41.0) +const DatePickerMacOS = createReactClass({ + // TOOD: Put a better type for _picker + _picker: (undefined: ?$FlowFixMe), + + mixins: [NativeMethodsMixin], + + propTypes: { + ...ViewPropTypes, + /** + * The currently selected date. + */ + date: PropTypes.instanceOf(Date).isRequired, + + /** + * Date change handler. + * + * This is called when the user changes the date or time in the UI. + * The first and only argument is a Date object representing the new + * date and time. + */ + onDateChange: PropTypes.func.isRequired, + + /** + * Maximum date. + * + * Restricts the range of possible date/time values. + */ + maximumDate: PropTypes.instanceOf(Date), + + /** + * Minimum date. + * + * Restricts the range of possible date/time values. + */ + minimumDate: PropTypes.instanceOf(Date), + + /** + * The date picker mode. + */ + mode: PropTypes.oneOf(['single', 'range']), + + /** + * The date picker style. + */ + pickerStyle: PropTypes.oneOf(['textfield-stepper', 'clock-calendar', 'textfield']), + + /** + * Timezone offset in minutes. + * + * By default, the date picker will use the device's timezone. With this + * parameter, it is possible to force a certain timezone offset. For + * instance, to show times in Pacific Standard Time, pass -7 * 60. + */ + timeZoneOffsetInMinutes: PropTypes.number, + + /** + * + * [Styles](docs/style.html) + */ + style: ViewPropTypes.style, + }, + + getDefaultProps: function(): DefaultProps { + return { + mode: 'range', + }; + }, + + _onChange: function(event: Event) { + const nativeTimeStamp = event.nativeEvent.timestamp; + this.props.onDateChange && this.props.onDateChange( + new Date(nativeTimeStamp) + ); + // $FlowFixMe(>=0.41.0) + this.props.onChange && this.props.onChange(event); + + // We expect the onChange* handlers to be in charge of updating our `date` + // prop. That way they can also disallow/undo/mutate the selection of + // certain values. In other words, the embedder of this component should + // be the source of truth, not the native component. + const propsTimeStamp = this.props.date.getTime(); + if (this._picker && nativeTimeStamp !== propsTimeStamp) { + this._picker.setNativeProps({ + date: propsTimeStamp, + }); + } + }, + + render: function() { + const props = this.props; + return ( + + { this._picker = picker; } } + style={props.style} + date={props.date.getTime()} + maximumDate={ + props.maximumDate ? props.maximumDate.getTime() : undefined + } + minimumDate={ + props.minimumDate ? props.minimumDate.getTime() : undefined + } + mode={props.mode} + pickerStyle={props.pickerStyle} + timeZoneOffsetInMinutes={props.timeZoneOffsetInMinutes} + onChange={this._onChange} + onStartShouldSetResponder={() => true} + onResponderTerminationRequest={() => false} + /> + + ); + } +}); + +const RCTDatePickerMacOS = requireNativeComponent('RCTDatePicker', { + propTypes: { + ...DatePickerMacOS.propTypes, + date: PropTypes.number, + minimumDate: PropTypes.number, + maximumDate: PropTypes.number, + onDateChange: () => null, + onChange: PropTypes.func, + } +}); + +module.exports = DatePickerMacOS; diff --git a/Libraries/Components/MaskedView/MaskedViewIOS.macos.js b/Libraries/Components/MaskedView/MaskedViewIOS.macos.js new file mode 100644 index 00000000000000..57b4be358ff63c --- /dev/null +++ b/Libraries/Components/MaskedView/MaskedViewIOS.macos.js @@ -0,0 +1,14 @@ +/** + * 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 MaskedViewIOS + * @flow + */ +'use strict'; + +module.exports = require('UnimplementedView'); diff --git a/Libraries/Components/Picker/Picker.js b/Libraries/Components/Picker/Picker.js index 443622221b624d..7192e86f5dc87a 100644 --- a/Libraries/Components/Picker/Picker.js +++ b/Libraries/Components/Picker/Picker.js @@ -137,7 +137,7 @@ class Picker extends React.Component<{ mode: PropTypes.oneOf(['dialog', 'dropdown']), /** * Style to apply to each of the item labels. - * @platform ios + * @platform ios, macos */ itemStyle: itemStylePropType, /** @@ -152,7 +152,7 @@ class Picker extends React.Component<{ }; render() { - if (Platform.OS === 'ios') { + if (Platform.OS === 'ios' || Platform.OS === 'macos') { // $FlowFixMe found when converting React.createClass to ES6 return {this.props.children}; } else if (Platform.OS === 'android') { diff --git a/Libraries/Components/Picker/PickerAndroid.macos.js b/Libraries/Components/Picker/PickerAndroid.macos.js new file mode 100644 index 00000000000000..fc589882864a71 --- /dev/null +++ b/Libraries/Components/Picker/PickerAndroid.macos.js @@ -0,0 +1,13 @@ +/** + * 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 PickerAndroid + */ +'use strict'; + +module.exports = require('UnimplementedView'); diff --git a/Libraries/Components/Picker/PickerIOS.macos.js b/Libraries/Components/Picker/PickerIOS.macos.js new file mode 100644 index 00000000000000..a2c64ce43973a1 --- /dev/null +++ b/Libraries/Components/Picker/PickerIOS.macos.js @@ -0,0 +1,137 @@ +/** + * 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 PickerIOS + * + * This is a controlled component version of RCTPickerIOS + */ +'use strict'; + +var NativeMethodsMixin = require('NativeMethodsMixin'); +var React = require('React'); +const PropTypes = require('prop-types'); +var StyleSheet = require('StyleSheet'); +var StyleSheetPropType = require('StyleSheetPropType'); +var TextStylePropTypes = require('TextStylePropTypes'); +var View = require('View'); +const ViewPropTypes = require('ViewPropTypes'); +var processColor = require('processColor'); + +var createReactClass = require('create-react-class'); +var itemStylePropType = StyleSheetPropType(TextStylePropTypes); +var requireNativeComponent = require('requireNativeComponent'); + +var PickerIOS = createReactClass({ + displayName: 'PickerIOS', + mixins: [NativeMethodsMixin], + + propTypes: { + ...ViewPropTypes, + itemStyle: itemStylePropType, + onValueChange: PropTypes.func, + selectedValue: PropTypes.any, // string or integer basically + }, + + getInitialState: function() { + return this._stateFromProps(this.props); + }, + + componentWillReceiveProps: function(nextProps) { + this.setState(this._stateFromProps(nextProps)); + }, + + // Translate PickerIOS prop and children into stuff that RCTPickerIOS understands. + _stateFromProps: function(props) { + var selectedIndex = 0; + var items = []; + React.Children.toArray(props.children).forEach(function (child, index) { + if (child.props.value === props.selectedValue) { + selectedIndex = index; + } + items.push({ + value: child.props.value, + label: child.props.label, + textColor: processColor(child.props.color), + }); + }); + return {selectedIndex, items}; + }, + + render: function() { + return ( + + this._picker = picker} + style={[styles.pickerIOS, this.props.itemStyle]} + items={this.state.items} + selectedIndex={this.state.selectedIndex} + onChange={this._onChange} + onStartShouldSetResponder={() => true} + onResponderTerminationRequest={() => false} + /> + + ); + }, + + _onChange: function(event) { + if (this.props.onChange) { + this.props.onChange(event); + } + if (this.props.onValueChange) { + this.props.onValueChange(event.nativeEvent.newValue, event.nativeEvent.newIndex); + } + + // The picker is a controlled component. This means we expect the + // on*Change handlers to be in charge of updating our + // `selectedValue` prop. That way they can also + // disallow/undo/mutate the selection of certain values. In other + // words, the embedder of this component should be the source of + // truth, not the native component. + if (this._picker && this.state.selectedIndex !== event.nativeEvent.newIndex) { + this._picker.setNativeProps({ + selectedIndex: this.state.selectedIndex + }); + } + }, +}); + +PickerIOS.Item = class extends React.Component { + static propTypes = { + value: PropTypes.any, // string or integer basically + label: PropTypes.string, + color: PropTypes.string, + }; + + render() { + // These items don't get rendered directly. + return null; + } +}; + +var styles = StyleSheet.create({ + pickerIOS: { + // The picker will conform to whatever width is given, but we do + // have to set the component's height explicitly on the + // surrounding view to ensure it gets rendered. + height: 26, + }, +}); + +var RCTPickerIOS = requireNativeComponent('RCTPicker', { + propTypes: { + style: itemStylePropType, + }, +}, { + nativeOnly: { + items: true, + onChange: true, + selectedIndex: true, + }, +}); + +module.exports = PickerIOS; diff --git a/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.macos.js b/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.macos.js new file mode 100644 index 00000000000000..99f01c81f36c20 --- /dev/null +++ b/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.macos.js @@ -0,0 +1,13 @@ +/** + * 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 ProgressBarAndroid + */ +'use strict'; + +module.exports = require('UnimplementedView'); diff --git a/Libraries/Components/ProgressViewIOS/ProgressViewIOS.macos.js b/Libraries/Components/ProgressViewIOS/ProgressViewIOS.macos.js new file mode 100644 index 00000000000000..e6a5485f85b30c --- /dev/null +++ b/Libraries/Components/ProgressViewIOS/ProgressViewIOS.macos.js @@ -0,0 +1,85 @@ +/** + * 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 ProgressViewIOS + * @flow + */ +'use strict'; + +var Image = require('Image'); +var NativeMethodsMixin = require('NativeMethodsMixin'); +var React = require('React'); +var PropTypes = require('prop-types'); +var StyleSheet = require('StyleSheet'); +var ViewPropTypes = require('ViewPropTypes'); + +var createReactClass = require('create-react-class'); +var requireNativeComponent = require('requireNativeComponent'); + +/** + * Use `ProgressViewIOS` to render a UIProgressView on iOS. + */ +var ProgressViewIOS = createReactClass({ + displayName: 'ProgressViewIOS', + mixins: [NativeMethodsMixin], + + propTypes: { + ...ViewPropTypes, + /** + * The progress bar style. + */ + progressViewStyle: PropTypes.oneOf(['default', 'bar']), + + /** + * The progress value (between 0 and 1). + */ + progress: PropTypes.number, + + /** + * The tint color of the progress bar itself. + */ + progressTintColor: PropTypes.string, + + /** + * The tint color of the progress bar track. + */ + trackTintColor: PropTypes.string, + + /** + * A stretchable image to display as the progress bar. + */ + progressImage: Image.propTypes.source, + + /** + * A stretchable image to display behind the progress bar. + */ + trackImage: Image.propTypes.source, + }, + + render: function() { + return ( + + ); + } +}); + +var styles = StyleSheet.create({ + progressView: { + height: 2, + }, +}); + +var RCTProgressView = requireNativeComponent( + 'RCTProgressView', + ProgressViewIOS +); + +module.exports = ProgressViewIOS; diff --git a/Libraries/Components/SafeAreaView/SafeAreaView.macos.js b/Libraries/Components/SafeAreaView/SafeAreaView.macos.js new file mode 100644 index 00000000000000..46265995522ded --- /dev/null +++ b/Libraries/Components/SafeAreaView/SafeAreaView.macos.js @@ -0,0 +1,14 @@ +/** + * 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 SafeAreaView + * @flow + */ +'use strict'; + +module.exports = require('View'); diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 37f522af640714..962f12b1adbd71 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -363,6 +363,18 @@ const ScrollView = createReactClass({ * true. */ removeClippedSubviews: PropTypes.bool, + /** + * Experimental: specifies how much to adjust the content view by when using + * the keyboard to scroll. This value adjusts the content's horizontal offset. + * + * @platform macos + */ + horizontalLineScroll: PropTypes.number, + /** + * Experimental: specifies how much to adjust the content view by when using + * the keyboard to scroll. This value adjusts the content's vertical offset. + */ + verticalLineScroll: PropTypes.number, /** * The current scale of the scroll view content. The default value is 1.0. * @platform ios @@ -611,9 +623,47 @@ const ScrollView = createReactClass({ } }, + _handleKeyDown: function(e: Object) { + if (this.props.onKeyDown) { + this.props.onKeyDown(e); + } + else { + const event = e['nativeEvent']; + const key = event['key']; + const kMinScrollOffset = 10; + + if (Platform.OS === 'macos') { + if (key === 'PAGE_UP') { + this._handleScrollByKeyDown(event, {x: event.contentOffset.x, y: event.contentOffset.y + -event.layoutMeasurement.height}) + } + else if (key === 'PAGE_DOWN') { + this._handleScrollByKeyDown(event, {x: event.contentOffset.x, y: event.contentOffset.y + event.layoutMeasurement.height}) + } + else if (key === 'LEFT_ARROW') { + this._handleScrollByKeyDown(event, {x: event.contentOffset.x + -(this.props.horizontalLineScroll || kMinScrollOffset), y: event.contentOffset.y}); + } + else if (key === 'RIGHT_ARROW') { + this._handleScrollByKeyDown(event, {x: event.contentOffset.x + (this.props.horizontalLineScroll || kMinScrollOffset), y: event.contentOffset.y}); + } + else if (key === 'DOWN_ARROW') { + this._handleScrollByKeyDown(event, {x: event.contentOffset.x, y: event.contentOffset.y + (this.props.verticalLineScroll || kMinScrollOffset)}); + } + else if (key === 'UP_ARROW') { + this._handleScrollByKeyDown(event, {x: event.contentOffset.x, y: event.contentOffset.y + -(this.props.verticalLineScroll || kMinScrollOffset)}); + } + } + } + }, + + _handleScrollByKeyDown: function(e: Object, newOffset) { + const maxX = e.contentSize.width - e.layoutMeasurement.width; + const maxY = e.contentSize.height - e.layoutMeasurement.height; + this.scrollTo({x: Math.max(0, Math.min(maxX, newOffset.x)), y: Math.max(0, Math.min(maxY, newOffset.y))}); + }, + _handleScroll: function(e: Object) { if (__DEV__) { - if (this.props.onScroll && this.props.scrollEventThrottle == null && Platform.OS === 'ios') { + if (this.props.onScroll && this.props.scrollEventThrottle == null && (Platform.OS === 'ios' || Platform.OS === 'macos')) { console.log( // eslint-disable-line no-console 'You specified `onScroll` on a but not ' + '`scrollEventThrottle`. You will only receive one event. ' + @@ -649,7 +699,7 @@ const ScrollView = createReactClass({ render: function() { let ScrollViewClass; let ScrollContentContainerViewClass; - if (Platform.OS === 'ios') { + if (Platform.OS === 'ios' || Platform.OS === 'macos') { ScrollViewClass = RCTScrollView; ScrollContentContainerViewClass = RCTScrollContentView; warning( @@ -805,6 +855,7 @@ const ScrollView = createReactClass({ onTouchMove: this.scrollResponderHandleTouchMove, onTouchStart: this.scrollResponderHandleTouchStart, onTouchCancel: this.scrollResponderHandleTouchCancel, + onKeyDown: this._handleKeyDown, scrollEventThrottle: hasStickyHeaders ? 1 : this.props.scrollEventThrottle, sendMomentumEvents: (this.props.onMomentumScrollBegin || this.props.onMomentumScrollEnd) ? true : false, @@ -904,13 +955,14 @@ if (Platform.OS === 'android') { AndroidHorizontalScrollContentView = requireNativeComponent( 'AndroidHorizontalScrollContentView' ); -} else if (Platform.OS === 'ios') { +} else if (Platform.OS === 'ios' || Platform.OS === 'macos') { nativeOnlyProps = { nativeOnly: { onMomentumScrollBegin: true, onMomentumScrollEnd : true, onScrollBeginDrag: true, onScrollEndDrag: true, + onKeyDown: true, } }; RCTScrollView = requireNativeComponent( diff --git a/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.macos.js b/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.macos.js new file mode 100644 index 00000000000000..e5297d8f28b845 --- /dev/null +++ b/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.macos.js @@ -0,0 +1,133 @@ +/** + * 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 SegmentedControlIOS + * @flow + */ +'use strict'; + +var NativeMethodsMixin = require('NativeMethodsMixin'); +var React = require('React'); +var PropTypes = require('prop-types'); +var StyleSheet = require('StyleSheet'); +var ViewPropTypes = require('ViewPropTypes'); + +var createReactClass = require('create-react-class'); +var requireNativeComponent = require('requireNativeComponent'); + +type DefaultProps = { + values: Array, + enabled: boolean, +}; + +var SEGMENTED_CONTROL_REFERENCE = 'segmentedcontrol'; + +type Event = Object; + +/** + * Use `SegmentedControlIOS` to render a UISegmentedControl iOS. + * + * #### Programmatically changing selected index + * + * The selected index can be changed on the fly by assigning the + * selectedIndex prop to a state variable, then changing that variable. + * Note that the state variable would need to be updated as the user + * selects a value and changes the index, as shown in the example below. + * + * ```` + * { + * this.setState({selectedIndex: event.nativeEvent.selectedSegmentIndex}); + * }} + * /> + * ```` + */ +var SegmentedControlIOS = createReactClass({ + displayName: 'SegmentedControlIOS', + mixins: [NativeMethodsMixin], + + propTypes: { + ...ViewPropTypes, + /** + * The labels for the control's segment buttons, in order. + */ + values: PropTypes.arrayOf(PropTypes.string), + + /** + * The index in `props.values` of the segment to be (pre)selected. + */ + selectedIndex: PropTypes.number, + + /** + * Callback that is called when the user taps a segment; + * passes the segment's value as an argument + */ + onValueChange: PropTypes.func, + + /** + * Callback that is called when the user taps a segment; + * passes the event as an argument + */ + onChange: PropTypes.func, + + /** + * If false the user won't be able to interact with the control. + * Default value is true. + */ + enabled: PropTypes.bool, + + /** + * Accent color of the control. + */ + tintColor: PropTypes.string, + + /** + * If true, then selecting a segment won't persist visually. + * The `onValueChange` callback will still work as expected. + */ + momentary: PropTypes.bool + }, + + getDefaultProps: function(): DefaultProps { + return { + values: [], + enabled: true + }; + }, + + _onChange: function(event: Event) { + this.props.onChange && this.props.onChange(event); + this.props.onValueChange && this.props.onValueChange(event.nativeEvent.value); + }, + + render: function() { + return ( + + ); + } +}); + +var styles = StyleSheet.create({ + segmentedControl: { + height: 28, + }, +}); + +var RCTSegmentedControl = requireNativeComponent( + 'RCTSegmentedControl', + SegmentedControlIOS +); + +module.exports = SegmentedControlIOS; diff --git a/Libraries/Components/Slider/Slider.js b/Libraries/Components/Slider/Slider.js index 61e76f6bd13efa..129733a98f6536 100644 --- a/Libraries/Components/Slider/Slider.js +++ b/Libraries/Components/Slider/Slider.js @@ -198,7 +198,7 @@ var Slider = createReactClass({ }); let styles; -if (Platform.OS === 'ios') { +if (Platform.OS === 'ios' || Platform.OS === 'macos') { styles = StyleSheet.create({ slider: { height: 40, diff --git a/Libraries/Components/Switch/Switch.js b/Libraries/Components/Switch/Switch.js index 8ee3225f3a0ee7..9d39c7ee4a6b4f 100644 --- a/Libraries/Components/Switch/Switch.js +++ b/Libraries/Components/Switch/Switch.js @@ -63,14 +63,17 @@ var Switch = createReactClass({ /** * Border color on iOS and background color on Android when the switch is turned off. + * @platform ios, android */ tintColor: ColorPropType, /** * Background color when the switch is turned on. + * @platform ios, android */ onTintColor: ColorPropType, /** * Color of the foreground switch grip. + * @platform ios, android */ thumbTintColor: ColorPropType, }, @@ -108,7 +111,7 @@ var Switch = createReactClass({ props.on = this.props.value; props.style = this.props.style; props.trackTintColor = this.props.value ? this.props.onTintColor : this.props.tintColor; - } else if (Platform.OS === 'ios') { + } else if (Platform.OS === 'ios' || Platform.OS === 'macos') { props.style = [styles.rctSwitchIOS, this.props.style]; } return ( diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index 1684095582a5e4..93eac9b53ca2a3 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -49,7 +49,7 @@ const onlyMultiline = { if (Platform.OS === 'android') { var AndroidTextInput = requireNativeComponent('AndroidTextInput', null); -} else if (Platform.OS === 'ios') { +} else if (Platform.OS === 'ios' || Platform.OS === 'macos') { var RCTTextView = requireNativeComponent('RCTTextView', null); var RCTTextField = requireNativeComponent('RCTTextField', null); } @@ -61,12 +61,24 @@ type Selection = { }; const DataDetectorTypes = [ + // iOS+macOS 'phoneNumber', 'link', 'address', 'calendarEvent', + // iOS-only 'none', 'all', + // macOS-only + 'ortography', + 'spelling', + 'grammar', + 'quote', + 'dash', + 'replacement', + 'correction', + 'regularExpression', + 'transitInformation', ]; /** @@ -197,6 +209,7 @@ const TextInput = createReactClass({ * - `words`: first letter of each word. * - `sentences`: first letter of each sentence (*default*). * - `none`: don't auto capitalize anything. + * @platform ios, android */ autoCapitalize: PropTypes.oneOf([ 'none', @@ -211,7 +224,7 @@ const TextInput = createReactClass({ /** * If `false`, disables spell-check style (i.e. red underlines). * The default value is inherited from `autoCorrect`. - * @platform ios + * @platform ios, macos */ spellCheck: PropTypes.bool, /** @@ -263,6 +276,7 @@ const TextInput = createReactClass({ * The following values work on Android only: * * - `visible-password` + * @platform ios, android */ keyboardType: PropTypes.oneOf([ // Cross-platform @@ -295,9 +309,9 @@ const TextInput = createReactClass({ * Determines how the return key should look. On Android you can also use * `returnKeyLabel`. * - * *Cross platform* + * *Mobile cross platform* * - * The following values work across platforms: + * The following values work across mobile platforms: * * - `done` * - `go` @@ -322,9 +336,10 @@ const TextInput = createReactClass({ * - `join` * - `route` * - `yahoo` + * @platform ios, android */ returnKeyType: PropTypes.oneOf([ - // Cross-platform + // Mobile cross platform 'done', 'go', 'next', @@ -425,7 +440,6 @@ const TextInput = createReactClass({ onSelectionChange: PropTypes.func, /** * Callback that is called when the text input's submit button is pressed. - * Invalid if `multiline={true}` is specified. */ onSubmitEditing: PropTypes.func, /** @@ -458,6 +472,7 @@ const TextInput = createReactClass({ /** * If `true`, the text input obscures the text entered so that sensitive text * like passwords stay secure. The default value is `false`. Does not work with 'multiline={true}'. + * @platform ios, android */ secureTextEntry: PropTypes.bool, /** @@ -521,6 +536,7 @@ const TextInput = createReactClass({ clearTextOnFocus: PropTypes.bool, /** * If `true`, all text will automatically be selected on focus. + * @platform ios, android */ selectTextOnFocus: PropTypes.bool, /** @@ -529,6 +545,7 @@ const TextInput = createReactClass({ * multiline fields. Note that for multiline fields, setting `blurOnSubmit` * to `true` means that pressing return will blur the field and trigger the * `onSubmitEditing` event instead of inserting a newline into the field. + * Ignored on Android, if Hardware Keyboard is connected. */ blurOnSubmit: PropTypes.bool, /** @@ -583,14 +600,35 @@ const TextInput = createReactClass({ * * Possible values for `dataDetectorTypes` are: * + * *iOS + macOS* + * * - `'phoneNumber'` * - `'link'` * - `'address'` * - `'calendarEvent'` + * + * *iOS Only* + * + * The following values work on iOS only: + * * - `'none'` * - `'all'` * - * @platform ios + * *macOS Only* + * + * The following values work on macOS only: + * + * - `'ortography'` + * - `'spelling'` + * - `'grammar'` + * - `'quote'` + * - `'dash'` + * - `'replacement'` + * - `'correction'` + * - `'regularExpression'` + * - `'transitInformation'` + * + * @platform ios, macOS */ dataDetectorTypes: PropTypes.oneOfType([ PropTypes.oneOf(DataDetectorTypes), @@ -598,6 +636,7 @@ const TextInput = createReactClass({ ]), /** * If `true`, caret is hidden. The default value is `false`. + * @platform ios, android */ caretHidden: PropTypes.bool, }, @@ -624,6 +663,13 @@ const TextInput = createReactClass({ ReactNative.findNodeHandle(this._inputRef); }, + /** + * Returns the native `TextView` node. + */ + getTextViewHandle: function(): any { + return ReactNative.findNodeHandle(this._inputRef); + }, + contextTypes: { onFocusRequested: PropTypes.func, focusEmitter: PropTypes.instanceOf(EventEmitter), @@ -681,7 +727,7 @@ const TextInput = createReactClass({ }, render: function() { - if (Platform.OS === 'ios') { + if (Platform.OS === 'ios' || Platform.OS === 'macos') { return this._renderIOS(); } else if (Platform.OS === 'android') { return this._renderAndroid(); @@ -773,6 +819,7 @@ const TextInput = createReactClass({ rejectResponderTermination={true} accessible={props.accessible} accessibilityLabel={props.accessibilityLabel} + accessibilityHint={props.accessibilityHint} accessibilityTraits={props.accessibilityTraits} nativeID={this.props.nativeID} testID={props.testID}> @@ -831,6 +878,7 @@ const TextInput = createReactClass({ onPress={this._onPress} accessible={this.props.accessible} accessibilityLabel={this.props.accessibilityLabel} + accessibilityHint={this.props.accessibilityHint} accessibilityComponentType={this.props.accessibilityComponentType} nativeID={this.props.nativeID} testID={this.props.testID}> @@ -839,7 +887,26 @@ const TextInput = createReactClass({ ); }, + _renderWindows: function() { + var props = Object.assign({}, this.props); + props.style = [styles.input, this.props.style]; + + return ( + + ); + }, + _onFocus: function(event: Event) { + // Set the focused TextInput field info in TextInputState. + // Delaying this to onFocus native event ensures that - + // 1. The state is updated only after the native code completes setting focus on the view + // 2. In case the focus is moving from one TextInput(A) to another TextInput(B), the state of + // A needs to be updated (blurred) before info about B is updated in TestInputState. + TextInputState.setFocusedTextInput(ReactNative.findNodeHandle(this._inputRef)); if (this.props.onFocus) { this.props.onFocus(event); } @@ -943,7 +1010,12 @@ const TextInput = createReactClass({ }, _onBlur: function(event: Event) { - this.blur(); + // Set the focused TextInput field info in TextInputState. + // Delaying this to onBlur native event ensures that - + // 1. The state is updated only after the native code completes clearing focus on the view + // 2. In case the focus is moving from one TextInput(A) to another TextInput(B), the state of + // A needs to be updated (blurred) before info about B is updated in TestInputState. + TextInputState.clearFocusedTextInput(ReactNative.findNodeHandle(this._inputRef)); if (this.props.onBlur) { this.props.onBlur(event); } @@ -974,4 +1046,4 @@ var styles = StyleSheet.create({ }, }); -module.exports = TextInput; +module.exports = TextInput; \ No newline at end of file diff --git a/Libraries/Components/TextInput/TextInputState.js b/Libraries/Components/TextInput/TextInputState.js index 7d17fe639352d6..92e5b47a7c429c 100644 --- a/Libraries/Components/TextInput/TextInputState.js +++ b/Libraries/Components/TextInput/TextInputState.js @@ -39,8 +39,7 @@ var TextInputState = { */ focusTextInput: function(textFieldID: ?number) { if (this._currentlyFocusedID !== textFieldID && textFieldID !== null) { - this._currentlyFocusedID = textFieldID; - if (Platform.OS === 'ios') { + if (Platform.OS === 'ios' || Platform.OS === 'macos') { UIManager.focus(textFieldID); } else if (Platform.OS === 'android') { UIManager.dispatchViewManagerCommand( @@ -52,6 +51,17 @@ var TextInputState = { } }, + /** + * @param {number} TextInputID id of the text field that has received focus + * Should be called after the view has received focus and fired the onFocus event + * noop if the focused text field is same + */ + setFocusedTextInput: function(textFieldID: ?number) { + if (this._currentlyFocusedID !== textFieldID && textFieldID !== null) { + this._currentlyFocusedID = textFieldID; + } + }, + /** * @param {number} textFieldID id of the text field to unfocus * Unfocuses the specified text field @@ -59,8 +69,7 @@ var TextInputState = { */ blurTextInput: function(textFieldID: ?number) { if (this._currentlyFocusedID === textFieldID && textFieldID !== null) { - this._currentlyFocusedID = null; - if (Platform.OS === 'ios') { + if (Platform.OS === 'ios' || Platform.OS === 'macos') { UIManager.blur(textFieldID); } else if (Platform.OS === 'android') { UIManager.dispatchViewManagerCommand( @@ -70,6 +79,17 @@ var TextInputState = { ); } } + }, + + /** + * @param {number} TextInputID id of the text field whose focus has to be cleared + * Should be called after the view has cleared focus and fired the onFocus event + * noop if the focused text field is not same + */ + clearFocusedTextInput: function(textFieldID: ?number) { + if (this._currentlyFocusedID === textFieldID && textFieldID !== null) { + this._currentlyFocusedID = null; + } } }; diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index f034e582f4941b..099ce94a44c032 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -164,6 +164,10 @@ var TouchableBounce = createReactClass({ * comment suppresses an error when upgrading Flow's support for React. * To see the error delete this comment and run Flow. */ accessibilityLabel={this.props.accessibilityLabel} + /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This + * comment suppresses an error when upgrading Flow's support for React. + * To see the error delete this comment and run Flow. */ + accessibilityHint={this.props.accessibilityHint} /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This * comment suppresses an error when upgrading Flow's support for React. * To see the error delete this comment and run Flow. */ @@ -172,6 +176,10 @@ var TouchableBounce = createReactClass({ * comment suppresses an error when upgrading Flow's support for React. * To see the error delete this comment and run Flow. */ accessibilityTraits={this.props.accessibilityTraits} + /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This + * comment suppresses an error when upgrading Flow's support for React. + * To see the error delete this comment and run Flow. */ + onAccessibilityTap={this.props.onAccessibilityTap} /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This * comment suppresses an error when upgrading Flow's support for React. * To see the error delete this comment and run Flow. */ @@ -188,7 +196,15 @@ var TouchableBounce = createReactClass({ onResponderGrant={this.touchableHandleResponderGrant} onResponderMove={this.touchableHandleResponderMove} onResponderRelease={this.touchableHandleResponderRelease} - onResponderTerminate={this.touchableHandleResponderTerminate}> + onResponderTerminate={this.touchableHandleResponderTerminate} + clickable={this.props.clickable !== false && this.props.onPress !== undefined && !this.props.disabled} + onClick={this.touchableHandlePress} + onMouseEnter={this.props.onMouseEnter} + onMouseLeave={this.props.onMouseLeave} + onDragEnter={this.props.onDragEnter} + onDragLeave={this.props.onDragLeave} + onDrop={this.props.onDrop} + draggedTypes={this.props.draggedTypes}> { // $FlowFixMe(>=0.41.0) this.props.children diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 1bcd162bb4fae9..9bc1432f540cd4 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -352,8 +352,12 @@ var TouchableHighlight = createReactClass({ * comment suppresses an error when upgrading Flow's support for React. * To see the error delete this comment and run Flow. */ accessibilityLabel={this.props.accessibilityLabel} + accessibilityHint={this.props.accessibilityHint} accessibilityComponentType={this.props.accessibilityComponentType} accessibilityTraits={this.props.accessibilityTraits} + onAccessibilityTap={this.props.onAccessibilityTap} + acceptsKeyboardFocus={(this.props.acceptsKeyboardFocus === undefined || this.props.acceptsKeyboardFocus) && !this.props.disabled} + enableFocusRing={(this.props.enableFocusRing === undefined || this.props.enableFocusRing) && !this.props.disabled} ref={UNDERLAY_REF} style={this.state.underlayStyle} onLayout={this.props.onLayout} @@ -367,6 +371,14 @@ var TouchableHighlight = createReactClass({ onResponderMove={this.touchableHandleResponderMove} onResponderRelease={this.touchableHandleResponderRelease} onResponderTerminate={this.touchableHandleResponderTerminate} + clickable={this.props.clickable !== false && this.props.onPress !== undefined} + onClick={this.touchableHandlePress} + onMouseEnter={this.props.onMouseEnter} + onMouseLeave={this.props.onMouseLeave} + onDragEnter={this.props.onDragEnter} + onDragLeave={this.props.onDragLeave} + onDrop={this.props.onDrop} + draggedTypes={this.props.draggedTypes} /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This * comment suppresses an error when upgrading Flow's support for React. * To see the error delete this comment and run Flow. */ diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js index 7436dd234278ee..f7d0eeed96210a 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js @@ -240,8 +240,10 @@ var TouchableNativeFeedback = createReactClass({ [drawableProp]: this.props.background, accessible: this.props.accessible !== false, accessibilityLabel: this.props.accessibilityLabel, + accessibilityHint: this.props.accessibilityHint, accessibilityComponentType: this.props.accessibilityComponentType, accessibilityTraits: this.props.accessibilityTraits, + onAccessibilityTap: this.props.onAccessibilityTap, children, testID: this.props.testID, onLayout: this.props.onLayout, @@ -252,6 +254,8 @@ var TouchableNativeFeedback = createReactClass({ onResponderMove: this._handleResponderMove, onResponderRelease: this.touchableHandleResponderRelease, onResponderTerminate: this.touchableHandleResponderTerminate, + clickable: this.props.clickable !== false && this.props.onPress !== undefined && !this.props.disabled, + onClick: this.touchableHandlePress, }; // We need to clone the actual element so that the ripple background drawable diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.js b/Libraries/Components/Touchable/TouchableNativeFeedback.js new file mode 100644 index 00000000000000..99abaa3a07055a --- /dev/null +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.js @@ -0,0 +1,46 @@ +/** + * 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 TouchableNativeFeedback + */ + +'use strict'; + +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var Text = require('Text'); +var View = require('View'); + +class DummyTouchableNativeFeedback extends React.Component { + render() { + return ( + + TouchableNativeFeedback is not supported on this platform! + + ); + } +} + +var styles = StyleSheet.create({ + container: { + height: 100, + width: 300, + backgroundColor: '#ffbcbc', + borderWidth: 1, + borderColor: 'red', + alignItems: 'center', + justifyContent: 'center', + margin: 10, + }, + info: { + color: '#333333', + margin: 20, + } +}); + +module.exports = DummyTouchableNativeFeedback; diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 1dced65d627528..e3c59bf98ad11a 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -245,8 +245,12 @@ var TouchableOpacity = createReactClass({ + onResponderTerminate={this.touchableHandleResponderTerminate} + clickable={this.props.clickable !== false && this.props.onPress !== undefined} + onClick={this.touchableHandlePress} + onMouseEnter={this.props.onMouseEnter} + onMouseLeave={this.props.onMouseLeave} + onDragEnter={this.props.onDragEnter} + onDragLeave={this.props.onDragLeave} + onDrop={this.props.onDrop} + draggedTypes={this.props.draggedTypes}> {this.props.children} {Touchable.renderDebugView({color: 'cyan', hitSlop: this.props.hitSlop})} diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index 401c28394474eb..86799020b84bd7 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -56,10 +56,20 @@ const TouchableWithoutFeedback = createReactClass({ PropTypes.oneOf(AccessibilityTraits), PropTypes.arrayOf(PropTypes.oneOf(AccessibilityTraits)), ]), + onAccessibilityTap: PropTypes.func, + /** * If true, disable all interactions for this component. */ disabled: PropTypes.bool, + /** + * Called when the mouse enters the touchable element + */ + onMouseEnter: PropTypes.func, + /** + * Called when the mouse exits the touchable element + */ + onMouseLeave: PropTypes.func, /** * Called when the touch is released, but not if cancelled (e.g. by a scroll * that steals the responder lock). @@ -188,8 +198,12 @@ const TouchableWithoutFeedback = createReactClass({ accessible: this.props.accessible !== false, // $FlowFixMe(>=0.41.0) accessibilityLabel: this.props.accessibilityLabel, + accessibilityHint: this.props.accessibilityHint, accessibilityComponentType: this.props.accessibilityComponentType, accessibilityTraits: this.props.accessibilityTraits, + onAccessibilityTap: this.props.onAccessibilityTap, + acceptsKeyboardFocus: (this.props.acceptsKeyboardFocus === undefined || this.props.acceptsKeyboardFocus) && !this.props.disabled, + enableFocusRing: (this.props.enableFocusRing === true && !this.props.disabled), // $FlowFixMe(>=0.41.0) nativeID: this.props.nativeID, // $FlowFixMe(>=0.41.0) @@ -202,8 +216,16 @@ const TouchableWithoutFeedback = createReactClass({ onResponderMove: this.touchableHandleResponderMove, onResponderRelease: this.touchableHandleResponderRelease, onResponderTerminate: this.touchableHandleResponderTerminate, + clickable: this.props.clickable !== false && this.props.onPress !== undefined, + onClick: this.touchableHandlePress, + onMouseEnter: this.props.onMouseEnter, + onMouseLeave: this.props.onMouseLeave, + onDragEnter: this.props.onDragEnter, + onDragLeave: this.props.onDragLeave, + onDrop: this.props.onDrop, + draggedTypes: this.props.draggedTypes, style, - children, + children, }); } }); diff --git a/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap b/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap index 2bd2f08e376c2c..5058748b635c85 100644 --- a/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap +++ b/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap @@ -2,15 +2,27 @@ exports[`TouchableHighlight renders correctly 1`] = ` , + accessibilityHint?: React$PropType$Primitive, accessibilityComponentType?: AccessibilityComponentType, accessibilityLiveRegion?: 'none' | 'polite' | 'assertive', importantForAccessibility?: 'auto'| 'yes'| 'no'| 'no-hide-descendants', accessibilityTraits?: AccessibilityTrait | Array, accessibilityViewIsModal?: bool, + accessibilityElementsHidden?: bool, onAccessibilityTap?: Function, onMagicTap?: Function, testID?: string, nativeID?: string, + onDoubleClick?: Function, + onKeyDown?: Function, onLayout?: (event: ViewLayoutEvent) => void, onResponderGrant?: Function, onResponderMove?: Function, @@ -77,6 +74,19 @@ export type ViewProps = { shouldRasterizeIOS?: bool, collapsable?: bool, needsOffscreenAlphaCompositing?: bool, + overrideDefaultFocusLoop?: bool, + clickable?: bool, + onClick?: Function, + onMouseEnter: Function, + onMouseLeave: Function, + onDragEnter: Function, + onDragLeave: Function, + onDrop: Function, + onFocusChange?: Function, + acceptsKeyboardFocus?: bool, + enableFocusRing?: bool, + draggedTypes: DraggedTypes | Array, + accessibilityNodeInfo?: AccessibilityNodeInfoProp, } & TVViewProps; module.exports = { @@ -95,6 +105,12 @@ module.exports = { */ accessibilityLabel: PropTypes.node, + /** + * Sets the hint text that's read by the screen reader when the user interacts + * with the element. + */ + accessibilityHint: PropTypes.node, + /** * Indicates to accessibility services to treat UI component like a * native one. Works for Android only. @@ -156,6 +172,20 @@ module.exports = { 'no-hide-descendants', ]), + /** + * Provides privilege to overrides accessibilityNodeInfo properties to be delivered to accessibility + * service. Works for Android only. + * + * Currently supported properties: + * - clickable : bool + * + * See the [Android `AccessibilityNodeInfo` docs](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.html) + * for reference. + * + * @platform android + */ + accessibilityNodeInfo: AccessibilityNodeInfoPropType, + /** * Provides additional traits to screen reader. By default no traits are * provided unless specified otherwise in element. @@ -182,10 +212,14 @@ module.exports = { * - `'allowsDirectInteraction'` - The element allows direct touch interaction for VoiceOver users. * - `'pageTurn'` - Informs VoiceOver that it should scroll to the next page when it finishes reading the contents of the element. * + * The following values are applicable to macos but are ignored on all other platforms: + * - `'group'` - The element is a group that contains other elements. + * - `'list'` - The element should be treated as a list. + * * See the [Accessibility guide](docs/accessibility.html#accessibilitytraits-ios) * for more information. * - * @platform ios + * @platform ios, macos */ accessibilityTraits: PropTypes.oneOfType([ PropTypes.oneOf(AccessibilityTraits), @@ -204,6 +238,18 @@ module.exports = { */ accessibilityViewIsModal: PropTypes.bool, + /** + * A value indicating whether the accessibility elements contained within + * this accessibility element are hidden. + * + * @platform ios + * + * See http://facebook.github.io/react-native/docs/view.html#accessibilityElementsHidden + */ + accessibilityElementsHidden: PropTypes.bool, + + onDoubleClick: PropTypes.func, + /** * When `accessible` is true, the system will try to invoke this function * when the user performs accessibility tap gesture. @@ -464,4 +510,97 @@ module.exports = { * @platform android */ needsOffscreenAlphaCompositing: PropTypes.bool, + + /** + * For platforms that support tab navigation between controls, this value + * specifies whether the default tab focus loop should be overridden. + * Currently this only applies to the `macos` platform. + * Default is `false`. + * + * @platform macos + */ + overrideDefaultFocusLoop: PropTypes.bool, + + /** + * When `true`, indicates that the view is clickable. By default, + * all the touchable elements are clickable. + * + * @platform android + */ + clickable: PropTypes.bool, + + /** + * When `clickable` is true, the system will try to invoke this function + * when the user performs a click. + * + * @platform android + */ + onClick: PropTypes.func, + + /** + * Fired when a pointing device is moved over the view + * + * @platform macos + */ + onMouseEnter: PropTypes.func, + + /** + * Fired when a pointing device is moved out the view + * + * @platform macos + */ + onMouseLeave: PropTypes.func, + + /** + * Fired when a dragged element enters a valid drop target + * + * @platform macos + */ + onDragEnter: PropTypes.func, + + /** + * Fired when a dragged element leaves a valid drop target + * + * @platform macos + */ + onDragLeave: PropTypes.func, + + /** + * Fired when an element is dropped on a valid drop target + * + * @platform macos + */ + onDrop: PropTypes.func, + + /** + * Specifies whether the view participates in the key view loop as user tabs + * through different controls. + */ + acceptsKeyboardFocus: PropTypes.bool, + + /** + * Specifies whether focus ring should be drawn when the view has the first responder status. + */ + enableFocusRing: PropTypes.bool, + + /** + * fired when the view focus changes (gain->lose or lose->gain) + * + * @platform android + */ + onFocusChange: PropTypes.func, + + /** + * Enables Dran'n'Drop Support for certain types of dragged types + * + * Possible values for `draggedTypes` are: + * + * - `'fileUrl'` + * + * @platform macos + */ + draggedTypes: PropTypes.oneOfType([ + PropTypes.oneOf(DraggedTypes), + PropTypes.arrayOf(PropTypes.oneOf(DraggedTypes)), + ]), }; diff --git a/Libraries/Components/WebView/WebView.macos.js b/Libraries/Components/WebView/WebView.macos.js new file mode 100644 index 00000000000000..f7c371d480fb6a --- /dev/null +++ b/Libraries/Components/WebView/WebView.macos.js @@ -0,0 +1,623 @@ +/** + * 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 WebView + * @noflow + */ +'use strict'; + +var ActivityIndicator = require('ActivityIndicator'); +var EdgeInsetsPropType = require('EdgeInsetsPropType'); +var React = require('React'); +var PropTypes = require('prop-types'); +var ReactNative = require('ReactNative'); +var StyleSheet = require('StyleSheet'); +var Text = require('Text'); +var UIManager = require('UIManager'); +var View = require('View'); +var ViewPropTypes = require('ViewPropTypes'); +var ScrollView = require('ScrollView'); + +var deprecatedPropType = require('deprecatedPropType'); +var invariant = require('fbjs/lib/invariant'); +var keyMirror = require('fbjs/lib/keyMirror'); +var requireNativeComponent = require('requireNativeComponent'); +var resolveAssetSource = require('resolveAssetSource'); + +var RCTWebViewManager = require('NativeModules').WebViewManager; + +var BGWASH = 'rgba(255,255,255,0.8)'; +var RCT_WEBVIEW_REF = 'webview'; + +var WebViewState = keyMirror({ + IDLE: null, + LOADING: null, + ERROR: null, +}); + +const NavigationType = keyMirror({ + click: true, + formsubmit: true, + backforward: true, + reload: true, + formresubmit: true, + other: true, +}); + +const JSNavigationScheme = 'react-js-navigation'; + +type ErrorEvent = { + domain: any, + code: any, + description: any, +} + +type Event = Object; + +const DataDetectorTypes = [ + 'phoneNumber', + 'link', + 'address', + 'calendarEvent', + 'none', + 'all', +]; + +var defaultRenderLoading = () => ( + + + +); +var defaultRenderError = (errorDomain, errorCode, errorDesc) => ( + + + Error loading page + + + {'Domain: ' + errorDomain} + + + {'Error Code: ' + errorCode} + + + {'Description: ' + errorDesc} + + +); + +/** + * `WebView` renders web content in a native view. + * + *``` + * import React, { Component } from 'react'; + * import { WebView } from 'react-native'; + * + * class MyWeb extends Component { + * render() { + * return ( + * + * ); + * } + * } + *``` + * + * You can use this component to navigate back and forth in the web view's + * history and configure various properties for the web content. + */ +class WebView extends React.Component { + static JSNavigationScheme = JSNavigationScheme; + static NavigationType = NavigationType; + + static propTypes = { + ...ViewPropTypes, + + html: deprecatedPropType( + PropTypes.string, + 'Use the `source` prop instead.' + ), + + url: deprecatedPropType( + PropTypes.string, + 'Use the `source` prop instead.' + ), + + /** + * Loads static html or a uri (with optional headers) in the WebView. + */ + source: PropTypes.oneOfType([ + PropTypes.shape({ + /* + * The URI to load in the `WebView`. Can be a local or remote file. + */ + uri: PropTypes.string, + /* + * The HTTP Method to use. Defaults to GET if not specified. + * NOTE: On Android, only GET and POST are supported. + */ + method: PropTypes.string, + /* + * Additional HTTP headers to send with the request. + * NOTE: On Android, this can only be used with GET requests. + */ + headers: PropTypes.object, + /* + * The HTTP body to send with the request. This must be a valid + * UTF-8 string, and will be sent exactly as specified, with no + * additional encoding (e.g. URL-escaping or base64) applied. + * NOTE: On Android, this can only be used with POST requests. + */ + body: PropTypes.string, + }), + PropTypes.shape({ + /* + * A static HTML page to display in the WebView. + */ + html: PropTypes.string, + /* + * The base URL to be used for any relative links in the HTML. + */ + baseUrl: PropTypes.string, + }), + /* + * Used internally by packager. + */ + PropTypes.number, + ]), + + /** + * Function that returns a view to show if there's an error. + */ + renderError: PropTypes.func, // view to show if there's an error + /** + * Function that returns a loading indicator. + */ + renderLoading: PropTypes.func, + /** + * Function that is invoked when the `WebView` has finished loading. + */ + onLoad: PropTypes.func, + /** + * Function that is invoked when the `WebView` load succeeds or fails. + */ + onLoadEnd: PropTypes.func, + /** + * Function that is invoked when the `WebView` starts loading. + */ + onLoadStart: PropTypes.func, + /** + * Function that is invoked when the `WebView` load fails. + */ + onError: PropTypes.func, + /** + * Boolean value that determines whether the web view bounces + * when it reaches the edge of the content. The default value is `true`. + * @platform ios + */ + bounces: PropTypes.bool, + /** + * A floating-point number that determines how quickly the scroll view + * decelerates after the user lifts their finger. You may also use the + * string shortcuts `"normal"` and `"fast"` which match the underlying iOS + * settings for `UIScrollViewDecelerationRateNormal` and + * `UIScrollViewDecelerationRateFast` respectively: + * + * - normal: 0.998 + * - fast: 0.99 (the default for iOS web view) + * @platform ios + */ + decelerationRate: ScrollView.propTypes.decelerationRate, + /** + * Boolean value that determines whether scrolling is enabled in the + * `WebView`. The default value is `true`. + * @platform ios + */ + scrollEnabled: PropTypes.bool, + /** + * Controls whether to adjust the content inset for web views that are + * placed behind a navigation bar, tab bar, or toolbar. The default value + * is `true`. + */ + automaticallyAdjustContentInsets: PropTypes.bool, + /** + * The amount by which the web view content is inset from the edges of + * the scroll view. Defaults to {top: 0, left: 0, bottom: 0, right: 0}. + */ + contentInset: EdgeInsetsPropType, + /** + * Function that is invoked when the `WebView` loading starts or ends. + */ + onNavigationStateChange: PropTypes.func, + /** + * A function that is invoked when the webview calls `window.postMessage`. + * Setting this property will inject a `postMessage` global into your + * webview, but will still call pre-existing values of `postMessage`. + * + * `window.postMessage` accepts one argument, `data`, which will be + * available on the event object, `event.nativeEvent.data`. `data` + * must be a string. + */ + onMessage: PropTypes.func, + /** + * Boolean value that forces the `WebView` to show the loading view + * on the first load. + */ + startInLoadingState: PropTypes.bool, + /** + * The style to apply to the `WebView`. + */ + style: ViewPropTypes.style, + + /** + * Determines the types of data converted to clickable URLs in the web view’s content. + * By default only phone numbers are detected. + * + * You can provide one type or an array of many types. + * + * Possible values for `dataDetectorTypes` are: + * + * - `'phoneNumber'` + * - `'link'` + * - `'address'` + * - `'calendarEvent'` + * - `'none'` + * - `'all'` + * + * @platform ios + */ + dataDetectorTypes: PropTypes.oneOfType([ + PropTypes.oneOf(DataDetectorTypes), + PropTypes.arrayOf(PropTypes.oneOf(DataDetectorTypes)), + ]), + + /** + * Boolean value to enable JavaScript in the `WebView`. Used on Android only + * as JavaScript is enabled by default on iOS. The default value is `true`. + * @platform android + */ + javaScriptEnabled: PropTypes.bool, + + /** + * Boolean value to control whether DOM Storage is enabled. Used only in + * Android. + * @platform android + */ + domStorageEnabled: PropTypes.bool, + + /** + * Set this to provide JavaScript that will be injected into the web page + * when the view loads. + */ + injectedJavaScript: PropTypes.string, + + /** + * Sets the user-agent for the `WebView`. + * @platform android + */ + userAgent: PropTypes.string, + + /** + * Boolean that controls whether the web content is scaled to fit + * the view and enables the user to change the scale. The default value + * is `true`. + */ + scalesPageToFit: PropTypes.bool, + + /** + * Function that allows custom handling of any web view requests. Return + * `true` from the function to continue loading the request and `false` + * to stop loading. + * @platform ios + */ + onShouldStartLoadWithRequest: PropTypes.func, + + /** + * Boolean that determines whether HTML5 videos play inline or use the + * native full-screen controller. The default value is `false`. + * + * **NOTE** : In order for video to play inline, not only does this + * property need to be set to `true`, but the video element in the HTML + * document must also include the `webkit-playsinline` attribute. + * @platform ios + */ + allowsInlineMediaPlayback: PropTypes.bool, + + /** + * Boolean that determines whether HTML5 audio and video requires the user + * to tap them before they start playing. The default value is `true`. + */ + mediaPlaybackRequiresUserAction: PropTypes.bool, + + /** + * Function that accepts a string that will be passed to the WebView and + * executed immediately as JavaScript. + */ + injectJavaScript: PropTypes.func, + + /** + * Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin. + * + * Possible values for `mixedContentMode` are: + * + * - `'never'` (default) - WebView will not allow a secure origin to load content from an insecure origin. + * - `'always'` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure. + * - `'compatibility'` - WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content. + * @platform android + */ + mixedContentMode: PropTypes.oneOf([ + 'never', + 'always', + 'compatibility' + ]), + }; + + state = { + viewState: WebViewState.IDLE, + lastErrorEvent: (null: ?ErrorEvent), + startInLoadingState: true, + }; + + componentWillMount() { + if (this.props.startInLoadingState) { + this.setState({viewState: WebViewState.LOADING}); + } + } + + render() { + var otherView = null; + + if (this.state.viewState === WebViewState.LOADING) { + otherView = (this.props.renderLoading || defaultRenderLoading)(); + } else if (this.state.viewState === WebViewState.ERROR) { + var errorEvent = this.state.lastErrorEvent; + invariant( + errorEvent != null, + 'lastErrorEvent expected to be non-null' + ); + otherView = (this.props.renderError || defaultRenderError)( + errorEvent.domain, + errorEvent.code, + errorEvent.description + ); + } else if (this.state.viewState !== WebViewState.IDLE) { + console.error( + 'RCTWebView invalid state encountered: ' + this.state.loading + ); + } + + var webViewStyles = [styles.container, styles.webView, this.props.style]; + if (this.state.viewState === WebViewState.LOADING || + this.state.viewState === WebViewState.ERROR) { + // if we're in either LOADING or ERROR states, don't show the webView + webViewStyles.push(styles.hidden); + } + + var onShouldStartLoadWithRequest = this.props.onShouldStartLoadWithRequest && ((event: Event) => { + var shouldStart = this.props.onShouldStartLoadWithRequest && + this.props.onShouldStartLoadWithRequest(event.nativeEvent); + RCTWebViewManager.startLoadWithResult(!!shouldStart, event.nativeEvent.lockIdentifier); + }); + + var source = this.props.source || {}; + if (this.props.html) { + source.html = this.props.html; + } else if (this.props.url) { + source.uri = this.props.url; + } + + const messagingEnabled = typeof this.props.onMessage === 'function'; + + var webView = + ; + + return ( + + {webView} + {otherView} + + ); + } + + /** + * Go forward one page in the web view's history. + */ + goForward = () => { + UIManager.dispatchViewManagerCommand( + this.getWebViewHandle(), + UIManager.RCTWebView.Commands.goForward, + null + ); + }; + + /** + * Go back one page in the web view's history. + */ + goBack = () => { + UIManager.dispatchViewManagerCommand( + this.getWebViewHandle(), + UIManager.RCTWebView.Commands.goBack, + null + ); + }; + + /** + * Reloads the current page. + */ + reload = () => { + this.setState({viewState: WebViewState.LOADING}); + UIManager.dispatchViewManagerCommand( + this.getWebViewHandle(), + UIManager.RCTWebView.Commands.reload, + null + ); + }; + + /** + * Stop loading the current page. + */ + stopLoading = () => { + UIManager.dispatchViewManagerCommand( + this.getWebViewHandle(), + UIManager.RCTWebView.Commands.stopLoading, + null + ); + }; + + /** + * Posts a message to the web view, which will emit a `message` event. + * Accepts one argument, `data`, which must be a string. + * + * In your webview, you'll need to something like the following. + * + * ```js + * document.addEventListener('message', e => { document.title = e.data; }); + * ``` + */ + postMessage = (data) => { + UIManager.dispatchViewManagerCommand( + this.getWebViewHandle(), + UIManager.RCTWebView.Commands.postMessage, + [String(data)] + ); + }; + + /** + * Injects a javascript string into the referenced WebView. Deliberately does not + * return a response because using eval() to return a response breaks this method + * on pages with a Content Security Policy that disallows eval(). If you need that + * functionality, look into postMessage/onMessage. + */ + injectJavaScript = (data) => { + UIManager.dispatchViewManagerCommand( + this.getWebViewHandle(), + UIManager.RCTWebView.Commands.injectJavaScript, + [data] + ); + }; + + /** + * We return an event with a bunch of fields including: + * url, title, loading, canGoBack, canGoForward + */ + _updateNavigationState = (event: Event) => { + if (this.props.onNavigationStateChange) { + this.props.onNavigationStateChange(event.nativeEvent); + } + }; + + /** + * Returns the native `WebView` node. + */ + getWebViewHandle = (): any => { + return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]); + }; + + _onLoadingStart = (event: Event) => { + var onLoadStart = this.props.onLoadStart; + onLoadStart && onLoadStart(event); + this._updateNavigationState(event); + }; + + _onLoadingError = (event: Event) => { + event.persist(); // persist this event because we need to store it + var {onError, onLoadEnd} = this.props; + onError && onError(event); + onLoadEnd && onLoadEnd(event); + console.warn('Encountered an error loading page', event.nativeEvent); + + this.setState({ + lastErrorEvent: event.nativeEvent, + viewState: WebViewState.ERROR + }); + }; + + _onLoadingFinish = (event: Event) => { + var {onLoad, onLoadEnd} = this.props; + onLoad && onLoad(event); + onLoadEnd && onLoadEnd(event); + this.setState({ + viewState: WebViewState.IDLE, + }); + this._updateNavigationState(event); + }; + + _onMessage = (event: Event) => { + var {onMessage} = this.props; + onMessage && onMessage(event); + } +} + +var RCTWebView = requireNativeComponent('RCTWebView', WebView, { + nativeOnly: { + onLoadingStart: true, + onLoadingError: true, + onLoadingFinish: true, + onMessage: true, + messagingEnabled: PropTypes.bool, + }, +}); + +var styles = StyleSheet.create({ + container: { + flex: 1, + }, + errorContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: BGWASH, + }, + errorText: { + fontSize: 14, + textAlign: 'center', + marginBottom: 2, + }, + errorTextTitle: { + fontSize: 15, + fontWeight: '500', + marginBottom: 10, + }, + hidden: { + height: 0, + flex: 0, // disable 'flex:1' when hiding a View + }, + loadingView: { + backgroundColor: BGWASH, + flex: 1, + justifyContent: 'center', + alignItems: 'center', + height: 100, + }, + webView: { + backgroundColor: '#ffffff', + } +}); + +module.exports = WebView; diff --git a/Libraries/EventEmitter/NativeEventEmitter.js b/Libraries/EventEmitter/NativeEventEmitter.js index 5c979600295b54..b9ab169e6b3af7 100644 --- a/Libraries/EventEmitter/NativeEventEmitter.js +++ b/Libraries/EventEmitter/NativeEventEmitter.js @@ -33,7 +33,7 @@ class NativeEventEmitter extends EventEmitter { constructor(nativeModule: ?NativeModule) { super(RCTDeviceEventEmitter.sharedSubscriber); - if (Platform.OS === 'ios') { + if (Platform.OS === 'ios' || Platform.OS === 'macos') { invariant(nativeModule, 'Native module cannot be null.'); this._nativeModule = nativeModule; } diff --git a/Libraries/EventEmitter/RCTDeviceEventEmitter.macos.js b/Libraries/EventEmitter/RCTDeviceEventEmitter.macos.js new file mode 100644 index 00000000000000..fe8efa1e977537 --- /dev/null +++ b/Libraries/EventEmitter/RCTDeviceEventEmitter.macos.js @@ -0,0 +1,66 @@ +/** + * 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 RCTDeviceEventEmitter + * @flow + */ +'use strict'; + +const EventEmitter = require('EventEmitter'); +const EventSubscriptionVendor = require('EventSubscriptionVendor'); +const BatchedBridge = require('BatchedBridge'); + +import type EmitterSubscription from 'EmitterSubscription'; + +/** + * Deprecated - subclass NativeEventEmitter to create granular event modules instead of + * adding all event listeners directly to RCTDeviceEventEmitter. + */ +class RCTDeviceEventEmitter extends EventEmitter { + + sharedSubscriber: EventSubscriptionVendor; + + constructor() { + const sharedSubscriber = new EventSubscriptionVendor(); + super(sharedSubscriber); + this.sharedSubscriber = sharedSubscriber; + } + + _nativeEventModule(eventType: ?string) { + return null; + } + + addListener(eventType: string, listener: Function, context: ?Object): EmitterSubscription { + const eventModule = this._nativeEventModule(eventType); + return eventModule ? eventModule.addListener(eventType, listener, context) + : super.addListener(eventType, listener, context); + } + + removeAllListeners(eventType: ?string) { + const eventModule = this._nativeEventModule(eventType); + (eventModule && eventType) ? eventModule.removeAllListeners(eventType) + : super.removeAllListeners(eventType); + } + + removeSubscription(subscription: EmitterSubscription) { + if (subscription.emitter !== this) { + subscription.emitter.removeSubscription(subscription); + } else { + super.removeSubscription(subscription); + } + } +} + +RCTDeviceEventEmitter = new RCTDeviceEventEmitter(); + +BatchedBridge.registerCallableModule( + 'RCTDeviceEventEmitter', + RCTDeviceEventEmitter +); + +module.exports = RCTDeviceEventEmitter; diff --git a/Libraries/Experimental/SwipeableRow/SwipeableQuickActionButton.js b/Libraries/Experimental/SwipeableRow/SwipeableQuickActionButton.js index 59cccbe3d85869..8de0f6f9cac47b 100644 --- a/Libraries/Experimental/SwipeableRow/SwipeableQuickActionButton.js +++ b/Libraries/Experimental/SwipeableRow/SwipeableQuickActionButton.js @@ -28,6 +28,7 @@ import type {ImageSource} from 'ImageSource'; */ class SwipeableQuickActionButton extends React.Component<{ accessibilityLabel?: string, + accessibilityHint?: string, imageSource: ImageSource | number, imageStyle?: ?ViewPropTypes.style, onPress?: Function, @@ -38,6 +39,7 @@ class SwipeableQuickActionButton extends React.Component<{ }> { static propTypes = { accessibilityLabel: PropTypes.string, + accessibilityHint: PropTypes.string, imageSource: Image.propTypes.source.isRequired, imageStyle: Image.propTypes.style, onPress: PropTypes.func, @@ -60,6 +62,7 @@ class SwipeableQuickActionButton extends React.Component<{ diff --git a/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj b/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj index e00e30ce46ec69..97de8134592e6d 100644 --- a/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj +++ b/Libraries/Geolocation/RCTGeolocation.xcodeproj/project.pbxproj @@ -8,12 +8,14 @@ /* Begin PBXBuildFile section */ 134814061AA4E45400B7C361 /* RCTLocationObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 134814051AA4E45400B7C361 /* RCTLocationObserver.m */; }; + 4633DB9A1F212DFE0080B326 /* RCTLocationObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 134814051AA4E45400B7C361 /* RCTLocationObserver.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 134814041AA4E45400B7C361 /* RCTLocationObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTLocationObserver.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 134814051AA4E45400B7C361 /* RCTLocationObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTLocationObserver.m; sourceTree = ""; }; 134814201AA4EA6300B7C361 /* libRCTGeolocation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTGeolocation.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4633DB9E1F212DFE0080B326 /* libRCTGeolocation-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTGeolocation-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ @@ -31,6 +33,7 @@ 134814041AA4E45400B7C361 /* RCTLocationObserver.h */, 134814051AA4E45400B7C361 /* RCTLocationObserver.m */, 134814211AA4EA7D00B7C361 /* Products */, + 4633DB9E1F212DFE0080B326 /* libRCTGeolocation-macOS.a */, ); indentWidth = 2; sourceTree = ""; @@ -40,6 +43,21 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 4633DB981F212DFE0080B326 /* RCTGeolocation-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4633DB9B1F212DFE0080B326 /* Build configuration list for PBXNativeTarget "RCTGeolocation-macOS" */; + buildPhases = ( + 4633DB991F212DFE0080B326 /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RCTGeolocation-macOS"; + productName = RCTDataManager; + productReference = 4633DB9E1F212DFE0080B326 /* libRCTGeolocation-macOS.a */; + productType = "com.apple.product-type.library.static"; + }; 58B511DA1A9E6C8500147676 /* RCTGeolocation */ = { isa = PBXNativeTarget; buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTGeolocation" */; @@ -82,11 +100,20 @@ projectRoot = ""; targets = ( 58B511DA1A9E6C8500147676 /* RCTGeolocation */, + 4633DB981F212DFE0080B326 /* RCTGeolocation-macOS */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ + 4633DB991F212DFE0080B326 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4633DB9A1F212DFE0080B326 /* RCTLocationObserver.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 58B511D71A9E6C8500147676 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -98,6 +125,29 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ + 4633DB9C1F212DFE0080B326 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 4633DB9D1F212DFE0080B326 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; 58B511ED1A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -135,6 +185,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -177,6 +228,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -212,6 +264,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 4633DB9B1F212DFE0080B326 /* Build configuration list for PBXNativeTarget "RCTGeolocation-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4633DB9C1F212DFE0080B326 /* Debug */, + 4633DB9D1F212DFE0080B326 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTGeolocation" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Libraries/Geolocation/RCTLocationObserver.m b/Libraries/Geolocation/RCTLocationObserver.m index 4ea8a57dc1ea56..17f25e2c7edc11 100644 --- a/Libraries/Geolocation/RCTLocationObserver.m +++ b/Libraries/Geolocation/RCTLocationObserver.m @@ -199,7 +199,8 @@ - (void)timeout:(NSTimer *)timer _locationManager = [CLLocationManager new]; _locationManager.delegate = self; } - + +#if !TARGET_OS_OSX // Request location access permission if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] && [_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) { @@ -216,6 +217,7 @@ - (void)timeout:(NSTimer *)timer [_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { [_locationManager requestWhenInUseAuthorization]; } + #endif } RCT_EXPORT_METHOD(startObserving:(RCTLocationOptions)options) diff --git a/Libraries/Image/Image.js b/Libraries/Image/Image.js new file mode 100644 index 00000000000000..bedfbe2a6f9df9 --- /dev/null +++ b/Libraries/Image/Image.js @@ -0,0 +1,406 @@ +/** + * 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 Image + * @flow + */ +'use strict'; + +const EdgeInsetsPropType = require('EdgeInsetsPropType'); +const ImageResizeMode = require('ImageResizeMode'); +const ImageSourcePropType = require('ImageSourcePropType'); +const ImageStylePropTypes = require('ImageStylePropTypes'); +const NativeMethodsMixin = require('NativeMethodsMixin'); +const NativeModules = require('NativeModules'); +const React = require('React'); +const PropTypes = require('prop-types'); +const ReactNativeViewAttributes = require('ReactNativeViewAttributes'); +const StyleSheet = require('StyleSheet'); +const StyleSheetPropType = require('StyleSheetPropType'); + +const createReactClass = require('create-react-class'); +const flattenStyle = require('flattenStyle'); +const requireNativeComponent = require('requireNativeComponent'); +const resolveAssetSource = require('resolveAssetSource'); + +const ImageViewManager = NativeModules.ImageViewManager; + +/** + * A React component for displaying different types of images, + * including network images, static resources, temporary local images, and + * images from local disk, such as the camera roll. + * + * This example shows fetching and displaying an image from local storage + * as well as one from network and even from data provided in the `'data:'` uri scheme. + * + * > Note that for network and data images, you will need to manually specify the dimensions of your image! + * + * ```ReactNativeWebPlayer + * import React, { Component } from 'react'; + * import { AppRegistry, View, Image } from 'react-native'; + * + * export default class DisplayAnImage extends Component { + * render() { + * return ( + * + * + * + * + * + * ); + * } + * } + * + * // skip this line if using Create React Native App + * AppRegistry.registerComponent('DisplayAnImage', () => DisplayAnImage); + * ``` + * + * You can also add `style` to an image: + * + * ```ReactNativeWebPlayer + * import React, { Component } from 'react'; + * import { AppRegistry, View, Image, StyleSheet } from 'react-native'; + * + * const styles = StyleSheet.create({ + * stretch: { + * width: 50, + * height: 200 + * } + * }); + * + * export default class DisplayAnImageWithStyle extends Component { + * render() { + * return ( + * + * + * + * ); + * } + * } + * + * // skip these lines if using Create React Native App + * AppRegistry.registerComponent( + * 'DisplayAnImageWithStyle', + * () => DisplayAnImageWithStyle + * ); + * ``` + * + * ### GIF and WebP support on Android + * + * When building your own native code, GIF and WebP are not supported by default on Android. + * + * You will need to add some optional modules in `android/app/build.gradle`, depending on the needs of your app. + * + * ``` + * dependencies { + * // If your app supports Android versions before Ice Cream Sandwich (API level 14) + * compile 'com.facebook.fresco:animated-base-support:1.3.0' + * + * // For animated GIF support + * compile 'com.facebook.fresco:animated-gif:1.3.0' + * + * // For WebP support, including animated WebP + * compile 'com.facebook.fresco:animated-webp:1.3.0' + * compile 'com.facebook.fresco:webpsupport:1.3.0' + * + * // For WebP support, without animations + * compile 'com.facebook.fresco:webpsupport:1.3.0' + * } + * ``` + * + * Also, if you use GIF with ProGuard, you will need to add this rule in `proguard-rules.pro` : + * ``` + * -keep class com.facebook.imagepipeline.animated.factory.AnimatedFactoryImpl { + * public AnimatedFactoryImpl(com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory, com.facebook.imagepipeline.core.ExecutorSupplier); + * } + * ``` + * + */ +const Image = createReactClass({ + displayName: 'Image', + propTypes: { + /** + * > `ImageResizeMode` is an `Enum` for different image resizing modes, set via the + * > `resizeMode` style property on `Image` components. The values are `contain`, `cover`, + * > `stretch`, `center`, `repeat`. + */ + style: StyleSheetPropType(ImageStylePropTypes), + /** + * The image source (either a remote URL or a local file resource). + * + * This prop can also contain several remote URLs, specified together with + * their width and height and potentially with scale/other URI arguments. + * The native side will then choose the best `uri` to display based on the + * measured size of the image container. A `cache` property can be added to + * control how networked request interacts with the local cache. + * + * The currently supported formats are `png`, `jpg`, `jpeg`, `bmp`, `gif`, + * `webp` (Android only), `psd` (iOS only). + */ + source: ImageSourcePropType, + /** + * A static image to display while loading the image source. + * + * - `uri` - a string representing the resource identifier for the image, which + * should be either a local file path or the name of a static image resource + * (which should be wrapped in the `require('./path/to/image.png')` function). + * - `width`, `height` - can be specified if known at build time, in which case + * these will be used to set the default `` component dimensions. + * - `scale` - used to indicate the scale factor of the image. Defaults to 1.0 if + * unspecified, meaning that one image pixel equates to one display point / DIP. + * - `number` - Opaque type returned by something like `require('./image.jpg')`. + * + * @platform ios + */ + defaultSource: PropTypes.oneOfType([ + // TODO: Tooling to support documenting these directly and having them display in the docs. + PropTypes.shape({ + uri: PropTypes.string, + width: PropTypes.number, + height: PropTypes.number, + scale: PropTypes.number, + }), + PropTypes.number, + ]), + /** + * When true, indicates the image is an accessibility element. + * @platform ios + */ + accessible: PropTypes.bool, + /** + * The text that's read by the screen reader when the user interacts with + * the image. + * @platform ios + */ + accessibilityLabel: PropTypes.node, + /** + * The hint text that's read by the screen reader when the user interacts with + * the image. + * @platform ios + */ + accessibilityHint: PropTypes.node, + /** + * blurRadius: the blur radius of the blur filter added to the image + */ + blurRadius: PropTypes.number, + /** + * When the image is resized, the corners of the size specified + * by `capInsets` will stay a fixed size, but the center content and borders + * of the image will be stretched. This is useful for creating resizable + * rounded buttons, shadows, and other resizable assets. More info in the + * [official Apple documentation](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImage_Class/index.html#//apple_ref/occ/instm/UIImage/resizableImageWithCapInsets). + * + * @platform ios + */ + capInsets: EdgeInsetsPropType, + /** + * The mechanism that should be used to resize the image when the image's dimensions + * differ from the image view's dimensions. Defaults to `auto`. + * + * - `auto`: Use heuristics to pick between `resize` and `scale`. + * + * - `resize`: A software operation which changes the encoded image in memory before it + * gets decoded. This should be used instead of `scale` when the image is much larger + * than the view. + * + * - `scale`: The image gets drawn downscaled or upscaled. Compared to `resize`, `scale` is + * faster (usually hardware accelerated) and produces higher quality images. This + * should be used if the image is smaller than the view. It should also be used if the + * image is slightly bigger than the view. + * + * More details about `resize` and `scale` can be found at http://frescolib.org/docs/resizing-rotating.html. + * + * @platform android + */ + resizeMethod: PropTypes.oneOf(['auto', 'resize', 'scale']), + /** + * Determines how to resize the image when the frame doesn't match the raw + * image dimensions. + * + * - `cover`: Scale the image uniformly (maintain the image's aspect ratio) + * so that both dimensions (width and height) of the image will be equal + * to or larger than the corresponding dimension of the view (minus padding). + * + * - `contain`: Scale the image uniformly (maintain the image's aspect ratio) + * so that both dimensions (width and height) of the image will be equal to + * or less than the corresponding dimension of the view (minus padding). + * + * - `stretch`: Scale width and height independently, This may change the + * aspect ratio of the src. + * + * - `repeat`: Repeat the image to cover the frame of the view. The + * image will keep it's size and aspect ratio. (iOS only) + */ + resizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center']), + /** + * A unique identifier for this element to be used in UI Automation + * testing scripts. + */ + testID: PropTypes.string, + /** + * Invoked on mount and layout changes with + * `{nativeEvent: {layout: {x, y, width, height}}}`. + */ + onLayout: PropTypes.func, + /** + * Invoked on load start. + * + * e.g., `onLoadStart={(e) => this.setState({loading: true})}` + */ + onLoadStart: PropTypes.func, + /** + * Invoked on download progress with `{nativeEvent: {loaded, total}}`. + * @platform ios + */ + onProgress: PropTypes.func, + /** + * Invoked on load error with `{nativeEvent: {error}}`. + */ + onError: PropTypes.func, + /** + * Invoked when a partial load of the image is complete. The definition of + * what constitutes a "partial load" is loader specific though this is meant + * for progressive JPEG loads. + * @platform ios + */ + onPartialLoad: PropTypes.func, + /** + * Invoked when load completes successfully. + */ + onLoad: PropTypes.func, + /** + * Invoked when load either succeeds or fails. + */ + onLoadEnd: PropTypes.func, + }, + + statics: { + resizeMode: ImageResizeMode, + /** + * Retrieve the width and height (in pixels) of an image prior to displaying it. + * This method can fail if the image cannot be found, or fails to download. + * + * In order to retrieve the image dimensions, the image may first need to be + * loaded or downloaded, after which it will be cached. This means that in + * principle you could use this method to preload images, however it is not + * optimized for that purpose, and may in future be implemented in a way that + * does not fully load/download the image data. A proper, supported way to + * preload images will be provided as a separate API. + * + * Does not work for static image resources. + * + * @param uri The location of the image. + * @param success The function that will be called if the image was successfully found and width + * and height retrieved. + * @param failure The function that will be called if there was an error, such as failing to + * to retrieve the image. + * + * @returns void + * + * @platform ios + */ + getSize: function( + uri: string, + success: (width: number, height: number) => void, + failure?: (error: any) => void, + ) { + ImageViewManager.getSize(uri, success, failure || function() { + console.warn('Failed to get size for image: ' + uri); + }); + }, + /** + * Prefetches a remote image for later use by downloading it to the disk + * cache + * + * @param url The remote location of the image. + * + * @return The prefetched image. + */ + prefetch(url: string) { + return ImageViewManager.prefetchImage(url); + }, + /** + * Resolves an asset reference into an object which has the properties `uri`, `width`, + * and `height`. The input may either be a number (opaque type returned by + * require('./foo.png')) or an `ImageSource` like { uri: '' } + */ + resolveAssetSource: resolveAssetSource, + }, + + mixins: [NativeMethodsMixin], + + /** + * `NativeMethodsMixin` will look for this when invoking `setNativeProps`. We + * make `this` look like an actual native component class. + */ + viewConfig: { + uiViewClassName: 'UIView', + validAttributes: ReactNativeViewAttributes.UIView + }, + + render: function() { + const source = resolveAssetSource(this.props.source) || { uri: undefined, width: undefined, height: undefined }; + + let sources; + let style; + if (Array.isArray(source)) { + style = flattenStyle([styles.base, this.props.style]) || {}; + sources = source; + } else { + const {width, height, uri} = source; + style = flattenStyle([{width, height}, styles.base, this.props.style]) || {}; + sources = [source]; + + if (uri === '') { + console.warn('source.uri should not be an empty string'); + } + } + + const resizeMode = this.props.resizeMode || (style || {}).resizeMode || 'cover'; // Workaround for flow bug t7737108 + const tintColor = (style || {}).tintColor; // Workaround for flow bug t7737108 + + if (this.props.src) { + console.warn('The component requires a `source` property rather than `src`.'); + } + + if (this.props.children) { + throw new Error('The component cannot contain children. If you want to render content on top of the image, consider using aboslute positioning.'); + } + + return ( + + ); + }, +}); + +const styles = StyleSheet.create({ + base: { + overflow: 'hidden', + }, +}); + +const RCTImageView = requireNativeComponent('RCTImageView', Image); + +module.exports = Image; diff --git a/Libraries/Image/RCTGIFImageDecoder.m b/Libraries/Image/RCTGIFImageDecoder.m index e9777bec16a5e6..69c65b560d7602 100644 --- a/Libraries/Image/RCTGIFImageDecoder.m +++ b/Libraries/Image/RCTGIFImageDecoder.m @@ -47,7 +47,11 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, NULL); if (!image) { +#if !TARGET_OS_OSX image = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp]; +#else + image = [[NSImage alloc] initWithCGImage:imageRef size:size]; +#endif } NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL); @@ -98,7 +102,11 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData // Don't bother creating an animation CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); if (imageRef) { +#if !TARGET_OS_OSX image = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp]; +#else + image = [[NSImage alloc] initWithCGImage:imageRef size:size]; +#endif CFRelease(imageRef); } CFRelease(imageSource); diff --git a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj index 0249e7b0c253cb..01c6e4ab9eed89 100644 --- a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj +++ b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj @@ -14,6 +14,31 @@ 139A38841C4D587C00862840 /* RCTResizeMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 139A38831C4D587C00862840 /* RCTResizeMode.m */; }; 13EF7F7F1BC825B1003F47DD /* RCTLocalAssetImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F7E1BC825B1003F47DD /* RCTLocalAssetImageLoader.m */; }; 143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; }; + 18B022D11EF83BA700FC19B0 /* RCTGIFImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */; }; + 18B022D21EF83BA700FC19B0 /* RCTImageBlurUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = EEF314701C9B0DD30049118E /* RCTImageBlurUtils.h */; }; + 18B022D31EF83BA700FC19B0 /* RCTImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = CCD34C251D4B8FE900268922 /* RCTImageCache.h */; }; + 18B022D41EF83BA700FC19B0 /* RCTImageEditingManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 354631661B69857700AA0B86 /* RCTImageEditingManager.h */; }; + 18B022D51EF83BA700FC19B0 /* RCTImageLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D5FA63C1DE4B44A0058FD77 /* RCTImageLoader.h */; }; + 18B022D61EF83BA700FC19B0 /* RCTImageShadowView.h in Headers */ = {isa = PBXBuildFile; fileRef = 59AB09281EDE5DD1009F97B5 /* RCTImageShadowView.h */; }; + 18B022D71EF83BA700FC19B0 /* RCTImageStoreManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D5FA63D1DE4B44A0058FD77 /* RCTImageStoreManager.h */; }; + 18B022D81EF83BA700FC19B0 /* RCTImageUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 134B00A01B54232B00EC8DFB /* RCTImageUtils.h */; }; + 18B022D91EF83BA700FC19B0 /* RCTImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = 1304D5A71AA8C4A30002E2BE /* RCTImageView.h */; }; + 18B022DA1EF83BA700FC19B0 /* RCTImageViewManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */; }; + 18B022DB1EF83BA700FC19B0 /* RCTLocalAssetImageLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 13EF7F7D1BC825B1003F47DD /* RCTLocalAssetImageLoader.h */; }; + 18B022DC1EF83BA700FC19B0 /* RCTResizeMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D5FA63E1DE4B44A0058FD77 /* RCTResizeMode.h */; }; + 18B022DD1EF83BB300FC19B0 /* RCTGIFImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */; }; + 18B022DE1EF83BB300FC19B0 /* RCTImageBlurUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF314711C9B0DD30049118E /* RCTImageBlurUtils.m */; }; + 18B022DF1EF83BB300FC19B0 /* RCTImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = CCD34C261D4B8FE900268922 /* RCTImageCache.m */; }; + 18B022E01EF83BB300FC19B0 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; }; + 18B022E11EF83BB300FC19B0 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; }; + 18B022E21EF83BB300FC19B0 /* RCTImageShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59AB09291EDE5DD1009F97B5 /* RCTImageShadowView.m */; }; + 18B022E31EF83BB300FC19B0 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; }; + 18B022E41EF83BB300FC19B0 /* RCTImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */; }; + 18B022E51EF83BB300FC19B0 /* RCTImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTImageView.m */; }; + 18B022E61EF83BB300FC19B0 /* RCTImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */; }; + 18B022E71EF83BB300FC19B0 /* RCTLocalAssetImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F7E1BC825B1003F47DD /* RCTLocalAssetImageLoader.m */; }; + 18B022E81EF83BB300FC19B0 /* RCTResizeMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 139A38831C4D587C00862840 /* RCTResizeMode.m */; }; + 18B022EA1EF83BCD00FC19B0 /* RCTImageUtils.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 134B00A01B54232B00EC8DFB /* RCTImageUtils.h */; }; 2D3B5F1A1D9B0D0400451313 /* RCTImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = CCD34C261D4B8FE900268922 /* RCTImageCache.m */; }; 2D3B5F1B1D9B0D0700451313 /* RCTImageBlurUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = EEF314711C9B0DD30049118E /* RCTImageBlurUtils.m */; }; 2D3B5F1C1D9B0D1300451313 /* RCTResizeMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 139A38831C4D587C00862840 /* RCTResizeMode.m */; }; @@ -60,6 +85,17 @@ /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ + 18B022E91EF83BBC00FC19B0 /* Copy Headers */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/RCTImage; + dstSubfolderSpec = 16; + files = ( + 18B022EA1EF83BCD00FC19B0 /* RCTImageUtils.h in Copy Headers */, + ); + name = "Copy Headers"; + runOnlyForDeploymentPostprocessing = 0; + }; 3D302E171DF8225500D6DDAE /* Copy Headers */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -108,6 +144,7 @@ 58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; }; 59AB09281EDE5DD1009F97B5 /* RCTImageShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageShadowView.h; sourceTree = ""; }; 59AB09291EDE5DD1009F97B5 /* RCTImageShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageShadowView.m; sourceTree = ""; }; + 6BDE7AEB1ECB9C4400CC951F /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; }; CCD34C251D4B8FE900268922 /* RCTImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTImageCache.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; CCD34C261D4B8FE900268922 /* RCTImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageCache.m; sourceTree = ""; }; EEF314701C9B0DD30049118E /* RCTImageBlurUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTImageBlurUtils.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; @@ -163,6 +200,7 @@ children = ( 58B5115D1A9E6B3D00147676 /* libRCTImage.a */, 2D2A283A1D9B042B00D4039D /* libRCTImage-tvOS.a */, + 6BDE7AEB1ECB9C4400CC951F /* libRCTImage.a */, ); name = Products; sourceTree = ""; @@ -208,6 +246,25 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6BDE7ACE1ECB9C4400CC951F /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 18B022D11EF83BA700FC19B0 /* RCTGIFImageDecoder.h in Headers */, + 18B022D21EF83BA700FC19B0 /* RCTImageBlurUtils.h in Headers */, + 18B022D31EF83BA700FC19B0 /* RCTImageCache.h in Headers */, + 18B022D41EF83BA700FC19B0 /* RCTImageEditingManager.h in Headers */, + 18B022D51EF83BA700FC19B0 /* RCTImageLoader.h in Headers */, + 18B022D61EF83BA700FC19B0 /* RCTImageShadowView.h in Headers */, + 18B022D71EF83BA700FC19B0 /* RCTImageStoreManager.h in Headers */, + 18B022D81EF83BA700FC19B0 /* RCTImageUtils.h in Headers */, + 18B022D91EF83BA700FC19B0 /* RCTImageView.h in Headers */, + 18B022DA1EF83BA700FC19B0 /* RCTImageViewManager.h in Headers */, + 18B022DB1EF83BA700FC19B0 /* RCTLocalAssetImageLoader.h in Headers */, + 18B022DC1EF83BA700FC19B0 /* RCTResizeMode.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -245,6 +302,23 @@ productReference = 58B5115D1A9E6B3D00147676 /* libRCTImage.a */; productType = "com.apple.product-type.library.static"; }; + 6BDE7ACD1ECB9C4400CC951F /* RCTImage-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6BDE7AE81ECB9C4400CC951F /* Build configuration list for PBXNativeTarget "RCTImage-macOS" */; + buildPhases = ( + 6BDE7ACE1ECB9C4400CC951F /* Headers */, + 18B022E91EF83BBC00FC19B0 /* Copy Headers */, + 6BDE7ADC1ECB9C4400CC951F /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RCTImage-macOS"; + productName = RCTNetworkImage; + productReference = 6BDE7AEB1ECB9C4400CC951F /* libRCTImage.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -276,6 +350,7 @@ projectRoot = ""; targets = ( 58B5115C1A9E6B3D00147676 /* RCTImage */, + 6BDE7ACD1ECB9C4400CC951F /* RCTImage-macOS */, 2D2A28391D9B042B00D4039D /* RCTImage-tvOS */, ); }; @@ -320,6 +395,25 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6BDE7ADC1ECB9C4400CC951F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 18B022DD1EF83BB300FC19B0 /* RCTGIFImageDecoder.m in Sources */, + 18B022DE1EF83BB300FC19B0 /* RCTImageBlurUtils.m in Sources */, + 18B022DF1EF83BB300FC19B0 /* RCTImageCache.m in Sources */, + 18B022E01EF83BB300FC19B0 /* RCTImageEditingManager.m in Sources */, + 18B022E11EF83BB300FC19B0 /* RCTImageLoader.m in Sources */, + 18B022E21EF83BB300FC19B0 /* RCTImageShadowView.m in Sources */, + 18B022E31EF83BB300FC19B0 /* RCTImageStoreManager.m in Sources */, + 18B022E41EF83BB300FC19B0 /* RCTImageUtils.m in Sources */, + 18B022E51EF83BB300FC19B0 /* RCTImageView.m in Sources */, + 18B022E61EF83BB300FC19B0 /* RCTImageViewManager.m in Sources */, + 18B022E71EF83BB300FC19B0 /* RCTLocalAssetImageLoader.m in Sources */, + 18B022E81EF83BB300FC19B0 /* RCTResizeMode.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -398,6 +492,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -443,6 +538,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -478,6 +574,32 @@ }; name = Release; }; + 6BDE7AE91ECB9C4400CC951F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTImage; + PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/RCTImage; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 6BDE7AEA1ECB9C4400CC951F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTImage; + PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/RCTImage; + RUN_CLANG_STATIC_ANALYZER = NO; + SDKROOT = macosx; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -508,6 +630,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 6BDE7AE81ECB9C4400CC951F /* Build configuration list for PBXNativeTarget "RCTImage-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6BDE7AE91ECB9C4400CC951F /* Debug */, + 6BDE7AEA1ECB9C4400CC951F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 58B511551A9E6B3D00147676 /* Project object */; diff --git a/Libraries/Image/RCTImageBlurUtils.h b/Libraries/Image/RCTImageBlurUtils.h index db8c90943789fa..b01e708aa4b3e2 100644 --- a/Libraries/Image/RCTImageBlurUtils.h +++ b/Libraries/Image/RCTImageBlurUtils.h @@ -9,7 +9,7 @@ */ #import -#import +#import #import diff --git a/Libraries/Image/RCTImageBlurUtils.m b/Libraries/Image/RCTImageBlurUtils.m index 193c042770a12e..26e906657a257e 100644 --- a/Libraries/Image/RCTImageBlurUtils.m +++ b/Libraries/Image/RCTImageBlurUtils.m @@ -9,11 +9,16 @@ #import "RCTImageBlurUtils.h" +#import +#import + UIImage *RCTBlurredImageWithRadius(UIImage *inputImage, CGFloat radius) { - CGImageRef imageRef = inputImage.CGImage; - CGFloat imageScale = inputImage.scale; + CGImageRef imageRef = UIImageGetCGImageRef(inputImage); + CGFloat imageScale = UIImageGetScale(inputImage); +#if !TARGET_OS_OSX UIImageOrientation imageOrientation = inputImage.imageOrientation; +#endif // Image must be nonzero size if (CGImageGetWidth(imageRef) * CGImageGetHeight(imageRef) == 0) { @@ -24,9 +29,14 @@ if (CGImageGetBitsPerPixel(imageRef) != 32 || CGImageGetBitsPerComponent(imageRef) != 8 || !((CGImageGetBitmapInfo(imageRef) & kCGBitmapAlphaInfoMask))) { - UIGraphicsBeginImageContextWithOptions(inputImage.size, NO, inputImage.scale); - [inputImage drawAtPoint:CGPointZero]; + UIGraphicsBeginImageContextWithOptions(inputImage.size, NO, imageScale); +#if !TARGET_OS_OSX + [inputImage drawAtPoint:CGPointZero]; imageRef = UIGraphicsGetImageFromCurrentImageContext().CGImage; +#else + [inputImage drawAtPoint:CGPointZero fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0]; + imageRef = (CGImageRef)CFAutorelease(CGBitmapContextCreateImage(UIGraphicsGetCurrentContext())); +#endif UIGraphicsEndImageContext(); } @@ -69,7 +79,11 @@ //create image from context imageRef = CGBitmapContextCreateImage(ctx); +#if !TARGET_OS_OSX UIImage *outputImage = [UIImage imageWithCGImage:imageRef scale:imageScale orientation:imageOrientation]; +#else + NSImage *outputImage = [[NSImage alloc] initWithCGImage:imageRef size:inputImage.size]; +#endif CGImageRelease(imageRef); CGContextRelease(ctx); free(buffer1.data); diff --git a/Libraries/Image/RCTImageCache.m b/Libraries/Image/RCTImageCache.m index ed825f00c13997..9ff66c9f158659 100644 --- a/Libraries/Image/RCTImageCache.m +++ b/Libraries/Image/RCTImageCache.m @@ -39,6 +39,7 @@ - (instancetype)init _decodedImageCache = [NSCache new]; _decodedImageCache.totalCostLimit = 5 * 1024 * 1024; // 5MB +#if !TARGET_OS_OSX [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(clearCache) name:UIApplicationDidReceiveMemoryWarningNotification @@ -47,6 +48,7 @@ - (instancetype)init selector:@selector(clearCache) name:UIApplicationWillResignActiveNotification object:nil]; +#endif return self; } @@ -56,10 +58,12 @@ - (void)dealloc [[NSNotificationCenter defaultCenter] removeObserver:self]; } +#if !TARGET_OS_OSX - (void)clearCache { [_decodedImageCache removeAllObjects]; } +#endif - (void)addImageToCache:(UIImage *)image forKey:(NSString *)cacheKey @@ -67,7 +71,8 @@ - (void)addImageToCache:(UIImage *)image if (!image) { return; } - CGFloat bytes = image.size.width * image.size.height * image.scale * image.scale * 4; + CGFloat imageScale = UIImageGetScale(image); + CGFloat bytes = image.size.width * image.size.height * imageScale * imageScale * 4; if (bytes <= RCTMaxCachableDecodedImageSizeInBytes) { [self->_decodedImageCache setObject:image forKey:cacheKey diff --git a/Libraries/Image/RCTImageEditingManager.m b/Libraries/Image/RCTImageEditingManager.m index 89341f652646c4..e69c114c2df054 100644 --- a/Libraries/Image/RCTImageEditingManager.m +++ b/Libraries/Image/RCTImageEditingManager.m @@ -9,7 +9,7 @@ #import "RCTImageEditingManager.h" -#import +#import #import #import @@ -55,7 +55,7 @@ @implementation RCTImageEditingManager CGSize targetSize = rect.size; CGRect targetRect = {{-rect.origin.x, -rect.origin.y}, image.size}; CGAffineTransform transform = RCTTransformFromTargetRect(image.size, targetRect); - UIImage *croppedImage = RCTTransformImage(image, targetSize, image.scale, transform); + UIImage *croppedImage = RCTTransformImage(image, targetSize, UIImageGetScale(image), transform); // Scale image if (cropData[@"displaySize"]) { @@ -63,7 +63,7 @@ @implementation RCTImageEditingManager RCTResizeMode resizeMode = [RCTConvert RCTResizeMode:cropData[@"resizeMode"] ?: @"contain"]; targetRect = RCTTargetRect(croppedImage.size, targetSize, 1, resizeMode); transform = RCTTransformFromTargetRect(croppedImage.size, targetRect); - croppedImage = RCTTransformImage(croppedImage, targetSize, image.scale, transform); + croppedImage = RCTTransformImage(croppedImage, targetSize, UIImageGetScale(image), transform); } // Store image diff --git a/Libraries/Image/RCTImageLoader.h b/Libraries/Image/RCTImageLoader.h index 3f7e7385203afd..2c78cdd8f0a6c7 100644 --- a/Libraries/Image/RCTImageLoader.h +++ b/Libraries/Image/RCTImageLoader.h @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import #import #import diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m index 7e57f481b2ecd9..c2d3263750b0a2 100644 --- a/Libraries/Image/RCTImageLoader.m +++ b/Libraries/Image/RCTImageLoader.m @@ -14,6 +14,7 @@ #import #import +#import #import #import #import @@ -652,14 +653,16 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data // Decompress the image data (this may be CPU and memory intensive) UIImage *image = RCTDecodeImageWithData(data, size, scale, resizeMode); -#if RCT_DEV - CGSize imagePixelSize = RCTSizeInPixels(image.size, image.scale); - CGSize screenPixelSize = RCTSizeInPixels(RCTScreenSize(), RCTScreenScale()); - if (imagePixelSize.width * imagePixelSize.height > - screenPixelSize.width * screenPixelSize.height) { - RCTLogInfo(@"[PERF ASSETS] Loading image at size %@, which is larger " - "than the screen size %@", NSStringFromCGSize(imagePixelSize), - NSStringFromCGSize(screenPixelSize)); +#if !TARGET_OS_OSX && RCT_DEV + if ([[self->_bridge devSettings] isDevModeEnabled]) { + CGSize imagePixelSize = RCTSizeInPixels(image.size, image.scale); + CGSize screenPixelSize = RCTSizeInPixels(RCTScreenSize(), RCTScreenScale()); + if (imagePixelSize.width * imagePixelSize.height > + screenPixelSize.width * screenPixelSize.height) { + RCTLogInfo(@"[PERF ASSETS] Loading image at size %@, which is larger " + "than the screen size %@", NSStringFromCGSize(imagePixelSize), + NSStringFromCGSize(screenPixelSize)); + } } #endif @@ -742,9 +745,10 @@ - (RCTImageLoaderCancellationBlock)getImageSizeForURLRequest:(NSURLRequest *)ima } } else { UIImage *image = imageOrData; + CGFloat imageScale = UIImageGetScale(image); size = (CGSize){ - image.size.width * image.scale, - image.size.height * image.scale, + image.size.width * imageScale, + image.size.height * imageScale, }; } callback(error, size); @@ -811,7 +815,7 @@ - (id)sendRequest:(NSURLRequest *)request withDelegate:(id +#import #import #import diff --git a/Libraries/Image/RCTImageStoreManager.m b/Libraries/Image/RCTImageStoreManager.m index 87fe71387eac14..00bd31cfffcff4 100644 --- a/Libraries/Image/RCTImageStoreManager.m +++ b/Libraries/Image/RCTImageStoreManager.m @@ -12,7 +12,9 @@ #import #import +#if !TARGET_OS_OSX #import +#endif #import #import @@ -215,7 +217,7 @@ - (UIImage *)imageForTag:(NSString *)imageTag dispatch_sync(_methodQueue, ^{ imageData = self->_store[imageTag]; }); - return [UIImage imageWithData:imageData]; + return UIImageWithData(imageData); } - (void)getImageForTag:(NSString *)imageTag withBlock:(void (^)(UIImage *image))block @@ -225,7 +227,7 @@ - (void)getImageForTag:(NSString *)imageTag withBlock:(void (^)(UIImage *image)) NSData *imageData = self->_store[imageTag]; dispatch_async(dispatch_get_main_queue(), ^{ // imageWithData: is not thread-safe, so we can't do this on methodQueue - block([UIImage imageWithData:imageData]); + block(UIImageWithData(imageData)); }); }); } diff --git a/Libraries/Image/RCTImageUtils.h b/Libraries/Image/RCTImageUtils.h index 371770cb1b92cd..ccb32e1f5dd076 100644 --- a/Libraries/Image/RCTImageUtils.h +++ b/Libraries/Image/RCTImageUtils.h @@ -8,7 +8,7 @@ * */ -#import +#import #import #import @@ -93,4 +93,9 @@ RCT_EXTERN UIImage *__nullable RCTTransformImage(UIImage *image, */ RCT_EXTERN BOOL RCTImageHasAlpha(CGImageRef image); +/* + * Return YES if image has an alpha component + */ +RCT_EXTERN BOOL RCTUIImageHasAlpha(UIImage *image); + NS_ASSUME_NONNULL_END diff --git a/Libraries/Image/RCTImageUtils.m b/Libraries/Image/RCTImageUtils.m index f64f1f7b686887..b27f9f4f892a35 100644 --- a/Libraries/Image/RCTImageUtils.m +++ b/Libraries/Image/RCTImageUtils.m @@ -12,7 +12,9 @@ #import #import +#if !TARGET_OS_OSX #import +#endif #import #import @@ -35,6 +37,7 @@ static CGSize RCTCeilSize(CGSize size, CGFloat scale) }; } +#if !TARGET_OS_OSX static CGImagePropertyOrientation CGImagePropertyOrientationFromUIImageOrientation(UIImageOrientation imageOrientation) { // see https://stackoverflow.com/a/6699649/496389 @@ -50,6 +53,7 @@ static CGImagePropertyOrientation CGImagePropertyOrientationFromUIImageOrientati default: return kCGImagePropertyOrientationUp; } } +#endif CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize, CGFloat destScale, RCTResizeMode resizeMode) @@ -282,7 +286,11 @@ BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale, destScale = 1; } } else if (!destScale) { +#if !TARGET_OS_OSX destScale = RCTScreenScale(); +#else + destScale = 1.0; // It's not possible to derive the correct scale on macOS, but it's not necessary for NSImage anyway +#endif } if (resizeMode == UIViewContentModeScaleToFill) { @@ -312,9 +320,14 @@ BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale, } // Return image +#if !TARGET_OS_OSX UIImage *image = [UIImage imageWithCGImage:imageRef scale:destScale orientation:UIImageOrientationUp]; +#else + NSImage *image = [[NSImage alloc] initWithCGImage:imageRef + size:targetSize]; +#endif CGImageRelease(imageRef); return image; } @@ -333,11 +346,17 @@ BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale, NSData *__nullable RCTGetImageData(UIImage *image, float quality) { NSMutableDictionary *properties = [[NSMutableDictionary alloc] initWithDictionary:@{ +#if !TARGET_OS_OSX (id)kCGImagePropertyOrientation : @(CGImagePropertyOrientationFromUIImageOrientation(image.imageOrientation)) +#endif }]; CGImageDestinationRef destination; CFMutableDataRef imageData = CFDataCreateMutable(NULL, 0); +#if !TARGET_OS_OSX CGImageRef cgImage = image.CGImage; +#else + CGImageRef cgImage = [image CGImageForProposedRect:NULL context:NULL hints:NULL]; +#endif if (RCTImageHasAlpha(cgImage)) { // get png data destination = CGImageDestinationCreateWithData(imageData, kUTTypePNG, 1, NULL); @@ -365,11 +384,15 @@ BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale, return nil; } - BOOL opaque = !RCTImageHasAlpha(image.CGImage); + BOOL opaque = !RCTUIImageHasAlpha(image); UIGraphicsBeginImageContextWithOptions(destSize, opaque, destScale); CGContextRef currentContext = UIGraphicsGetCurrentContext(); CGContextConcatCTM(currentContext, transform); +#if !TARGET_OS_OSX [image drawAtPoint:CGPointZero]; +#else + [image drawAtPoint:CGPointZero fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0]; +#endif UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return result; @@ -386,3 +409,20 @@ BOOL RCTImageHasAlpha(CGImageRef image) return YES; } } + +#if !TARGET_OS_OSX +BOOL RCTUIImageHasAlpha(UIImage *image) +{ + return RCTImageHasAlpha(image.CGImage); +} +#else +BOOL RCTUIImageHasAlpha(UIImage *image) +{ + for (NSImageRep *imageRep in image.representations) { + if (imageRep.hasAlpha) { + return YES; + } + } + return NO; +} +#endif diff --git a/Libraries/Image/RCTImageView.h b/Libraries/Image/RCTImageView.h index 7c632b3bfa66a1..b67e6cd289c2a8 100644 --- a/Libraries/Image/RCTImageView.h +++ b/Libraries/Image/RCTImageView.h @@ -7,14 +7,25 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import #import @class RCTBridge; @class RCTImageSource; +#if TARGET_OS_OSX +typedef NS_ENUM(NSInteger, UIImageRenderingMode) { + UIImageRenderingModeAlwaysOriginal, + UIImageRenderingModeAlwaysTemplate, +}; +#endif + +#if !TARGET_OS_OSX @interface RCTImageView : UIImageView +#else +@interface RCTImageView : NSImageView +#endif - (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; diff --git a/Libraries/Image/RCTImageView.m b/Libraries/Image/RCTImageView.m index 144fff6cbff33c..f7b898d0caebc4 100644 --- a/Libraries/Image/RCTImageView.m +++ b/Libraries/Image/RCTImageView.m @@ -36,6 +36,54 @@ static BOOL RCTShouldReloadImageForSizeChange(CGSize currentSize, CGSize idealSi heightMultiplier > upscaleThreshold || heightMultiplier < downscaleThreshold; } +#if TARGET_OS_OSX +/** + * Implements macOS equivalent behavior of UIViewContentModeScaleAspectFill. + * Used for RCTResizeModeCover support. + */ +static NSImage *RCTFillImagePreservingAspectRatio(NSImage *originalImage, NSSize targetSize, CGFloat windowScale) +{ + RCTAssertParam(originalImage); + if (!originalImage) { + return nil; + } + + NSSize originalImageSize = originalImage.size; + if (NSEqualSizes(originalImageSize, NSZeroSize) || [[originalImage representations] count] == 0) { + return originalImage; + } + + CGFloat scaleX = targetSize.width / originalImageSize.width; + CGFloat scaleY = targetSize.height / originalImageSize.height; + CGFloat scale = 1.0; + + if (scaleX < scaleY) { + // clamped width + scale = scaleY; + } + else { + // clamped height + scale = scaleX; + } + + NSSize newSize = NSMakeSize(RCTRoundPixelValue(originalImageSize.width * scale, windowScale), + RCTRoundPixelValue(originalImageSize.height * scale, windowScale)); + NSImage *newImage = [[NSImage alloc] initWithSize:newSize]; + + for (NSImageRep *imageRep in [originalImage representations]) { + NSImageRep *newImageRep = [imageRep copy]; + NSSize newImageRepSize = NSMakeSize(RCTRoundPixelValue(imageRep.size.width * scale, windowScale), + RCTRoundPixelValue(imageRep.size.height * scale, windowScale)); + + newImageRep.size = newImageRepSize; + + [newImage addRepresentation:newImageRep]; + } + + return newImage; +} +#endif + /** * See RCTConvert (ImageSource). We want to send down the source as a similar * JSON parameter. @@ -80,13 +128,26 @@ @implementation RCTImageView // Whether the latest change of props requires the image to be reloaded BOOL _needsReload; + +#if TARGET_OS_OSX + // Whether observing changes to the window's backing scale + BOOL _subscribedToWindowBackingNotifications; +#endif } - (instancetype)initWithBridge:(RCTBridge *)bridge { +#if !TARGET_OS_OSX if ((self = [super init])) { +#else + if ((self = [super initWithFrame:NSZeroRect])) { +#endif _bridge = bridge; +#if TARGET_OS_OSX + self.wantsLayer = YES; +#endif +#if !TARGET_OS_OSX NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(clearImageIfDetached) @@ -96,6 +157,7 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge selector:@selector(clearImageIfDetached) name:UIApplicationDidEnterBackgroundNotification object:nil]; +#endif } return self; } @@ -107,6 +169,11 @@ - (void)dealloc RCT_NOT_IMPLEMENTED(- (instancetype)init) +#if TARGET_OS_OSX +RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(NSRect)frame) +RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)coder) +#endif + - (void)updateWithImage:(UIImage *)image { if (!image) { @@ -115,15 +182,31 @@ - (void)updateWithImage:(UIImage *)image } // Apply rendering mode +#if !TARGET_OS_OSX if (_renderingMode != image.renderingMode) { image = [image imageWithRenderingMode:_renderingMode]; } +#else + if ((_renderingMode == UIImageRenderingModeAlwaysTemplate) != image.template) { + image.template = (_renderingMode == UIImageRenderingModeAlwaysTemplate); + } +#endif if (_resizeMode == RCTResizeModeRepeat) { +#if !TARGET_OS_OSX image = [image resizableImageWithCapInsets:_capInsets resizingMode:UIImageResizingModeTile]; +#else + image.capInsets = _capInsets; + image.resizingMode = NSImageResizingModeTile; +#endif } else if (!UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, _capInsets)) { // Applying capInsets of 0 will switch the "resizingMode" of the image to "tile" which is undesired +#if !TARGET_OS_OSX image = [image resizableImageWithCapInsets:_capInsets resizingMode:UIImageResizingModeStretch]; +#else + image.capInsets = _capInsets; + image.resizingMode = NSImageResizingModeStretch; +#endif } // Apply trilinear filtering to smooth out mis-sized images @@ -137,6 +220,11 @@ - (void)setImage:(UIImage *)image { image = image ?: _defaultImage; if (image != self.image) { +#if TARGET_OS_OSX + if (image && _resizeMode == RCTResizeModeCover && !NSEqualSizes(self.bounds.size, NSZeroSize)) { + image = RCTFillImagePreservingAspectRatio(image, self.bounds.size, self.window.backingScaleFactor ?: 1.0); + } +#endif [self updateWithImage:image]; } } @@ -188,9 +276,21 @@ - (void)setResizeMode:(RCTResizeMode)resizeMode if (_resizeMode == RCTResizeModeRepeat) { // Repeat resize mode is handled by the UIImage. Use scale to fill // so the repeated image fills the UIImageView. +#if !TARGET_OS_OSX self.contentMode = UIViewContentModeScaleToFill; +#else + self.imageScaling = NSImageScaleAxesIndependently; +#endif } else { +#if !TARGET_OS_OSX self.contentMode = (UIViewContentMode)resizeMode; +#else + // This relies on having previously resampled the image to a size that exceeds the iamge view. + if (resizeMode == RCTResizeModeCover) { + resizeMode = RCTResizeModeCenter; + } + self.imageScaling = (NSImageScaling)resizeMode; +#endif } if ([self shouldReloadImageSourceAfterResize]) { @@ -218,12 +318,14 @@ - (void)clearImage _imageSource = nil; } +#if !TARGET_OS_OSX - (void)clearImageIfDetached { if (!self.window) { [self clearImage]; } } +#endif - (BOOL)hasMultipleSources { @@ -241,7 +343,11 @@ - (RCTImageSource *)imageSourceForSize:(CGSize)size return nil; } +#if !TARGET_OS_OSX const CGFloat scale = RCTScreenScale(); +#else + const CGFloat scale = self.window != nil ? self.window.backingScaleFactor : [NSScreen mainScreen].backingScaleFactor; +#endif const CGFloat targetImagePixels = size.width * size.height * scale * scale; RCTImageSource *bestSource = nil; @@ -304,7 +410,11 @@ - (void)reloadImage }; CGSize imageSize = self.bounds.size; +#if !TARGET_OS_OSX CGFloat imageScale = RCTScreenScale(); +#else + CGFloat imageScale = self.window != nil ? self.window.backingScaleFactor : [NSScreen mainScreen].backingScaleFactor; +#endif if (!UIEdgeInsetsEqualToEdgeInsets(_capInsets, UIEdgeInsetsZero)) { // Don't resize images that use capInsets imageSize = CGSizeZero; @@ -404,9 +514,21 @@ - (void)reactSetFrame:(CGRect)frame [self reloadImage]; } else if ([self shouldReloadImageSourceAfterResize]) { CGSize imageSize = self.image.size; - CGFloat imageScale = self.image.scale; - CGSize idealSize = RCTTargetSize(imageSize, imageScale, frame.size, RCTScreenScale(), - (RCTResizeMode)self.contentMode, YES); + CGFloat imageScale = UIImageGetScale(self.image); +#if !TARGET_OS_OSX + CGFloat windowScale = RCTScreenScale(); + RCTResizeMode resizeMode = (RCTResizeMode)self.contentMode; +#else + CGFloat windowScale = self.window != nil ? self.window.backingScaleFactor : [NSScreen mainScreen].backingScaleFactor; + RCTResizeMode resizeMode = self.resizeMode; + + // self.contentMode on iOS is translated to RCTResizeModeRepeat in -setResizeMode: + if (resizeMode == RCTResizeModeRepeat) { + resizeMode = RCTResizeModeStretch; + } +#endif + CGSize idealSize = RCTTargetSize(imageSize, imageScale, frame.size, windowScale, + resizeMode, YES); // Don't reload if the current image or target image size is close enough if (!RCTShouldReloadImageForSizeChange(imageSize, idealSize) || @@ -437,10 +559,35 @@ - (void)didSetProps:(NSArray *)changedProps } } +#if TARGET_OS_OSX +#define didMoveToWindow viewDidMoveToWindow +#endif +#if TARGET_OS_OSX +- (void)viewWillMoveToWindow:(NSWindow *)newWindow +{ + if (_subscribedToWindowBackingNotifications && + self.window != nil && + self.window != newWindow) { + [[NSNotificationCenter defaultCenter] removeObserver:self + name:NSWindowDidChangeBackingPropertiesNotification + object:self.window]; + _subscribedToWindowBackingNotifications = NO; + } +} +#endif - (void)didMoveToWindow { [super didMoveToWindow]; +#if TARGET_OS_OSX + if (!_subscribedToWindowBackingNotifications && self.window != nil) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowDidChangeBackingProperties:) + name:NSWindowDidChangeBackingPropertiesNotification + object:self.window]; + _subscribedToWindowBackingNotifications = YES; + } +#endif if (!self.window) { // Cancel loading the image if we've moved offscreen. In addition to helping // prioritise image requests that are actually on-screen, this removes @@ -452,4 +599,10 @@ - (void)didMoveToWindow } } +#if TARGET_OS_OSX +- (void)windowDidChangeBackingProperties:(NSNotification *)notification +{ + [self reloadImage]; +} +#endif @end diff --git a/Libraries/Image/RCTImageViewManager.m b/Libraries/Image/RCTImageViewManager.m index b313e016fd3094..916059d34f77be 100644 --- a/Libraries/Image/RCTImageViewManager.m +++ b/Libraries/Image/RCTImageViewManager.m @@ -9,7 +9,7 @@ #import "RCTImageViewManager.h" -#import +#import #import @@ -26,7 +26,7 @@ - (RCTShadowView *)shadowView return [RCTImageShadowView new]; } -- (UIView *)view +- (RCTPlatformView *)view { return [[RCTImageView alloc] initWithBridge:self.bridge]; } @@ -42,6 +42,7 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(onLoadEnd, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(resizeMode, RCTResizeMode) RCT_REMAP_VIEW_PROPERTY(source, imageSources, NSArray); +#if !TARGET_OS_OSX RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTImageView) { // Default tintColor isn't nil - it's inherited from the superView - but we @@ -50,6 +51,7 @@ - (UIView *)view view.tintColor = [RCTConvert UIColor:json] ?: defaultView.tintColor; view.renderingMode = json ? UIImageRenderingModeAlwaysTemplate : defaultView.renderingMode; } +#endif RCT_EXPORT_METHOD(getSize:(NSURLRequest *)request successBlock:(RCTResponseSenderBlock)successBlock diff --git a/Libraries/Image/RCTResizeMode.h b/Libraries/Image/RCTResizeMode.h index 52f888073894d6..08bc45eb4de65d 100644 --- a/Libraries/Image/RCTResizeMode.h +++ b/Libraries/Image/RCTResizeMode.h @@ -10,10 +10,17 @@ #import typedef NS_ENUM(NSInteger, RCTResizeMode) { +#if !TARGET_OS_OSX RCTResizeModeCover = UIViewContentModeScaleAspectFill, RCTResizeModeContain = UIViewContentModeScaleAspectFit, RCTResizeModeStretch = UIViewContentModeScaleToFill, RCTResizeModeCenter = UIViewContentModeCenter, +#else + RCTResizeModeCover = -2, // Not supported by NSImageView + RCTResizeModeContain = NSImageScaleProportionallyUpOrDown, + RCTResizeModeStretch = NSImageScaleAxesIndependently, + RCTResizeModeCenter = NSImageScaleNone, // assumes NSImageAlignmentCenter +#endif RCTResizeModeRepeat = -1, // Use negative values to avoid conflicts with iOS enum values. }; diff --git a/Libraries/LinkingIOS/RCTLinking.xcodeproj/project.pbxproj b/Libraries/LinkingIOS/RCTLinking.xcodeproj/project.pbxproj index b7ab8f88d800ee..91bcbc9c0ff0c9 100644 --- a/Libraries/LinkingIOS/RCTLinking.xcodeproj/project.pbxproj +++ b/Libraries/LinkingIOS/RCTLinking.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 148699CF1ABD045300480536 /* RCTLinkingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 148699CE1ABD045300480536 /* RCTLinkingManager.m */; }; 2D3B5F251D9B0DE600451313 /* RCTLinkingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 148699CE1ABD045300480536 /* RCTLinkingManager.m */; }; + D42902C41F1F85F100685AE7 /* RCTLinkingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D42902891F1CDFF300685AE7 /* RCTLinkingManager.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -16,6 +17,8 @@ 148699CD1ABD045300480536 /* RCTLinkingManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTLinkingManager.h; sourceTree = ""; }; 148699CE1ABD045300480536 /* RCTLinkingManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTLinkingManager.m; sourceTree = ""; }; 2D2A28471D9B043800D4039D /* libRCTLinking-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTLinking-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + D42902891F1CDFF300685AE7 /* RCTLinkingManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RCTLinkingManager.m; path = macOS/RCTLinkingManager.m; sourceTree = ""; }; + D4C812E71F1CC42300FFA059 /* libRCTLinking-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTLinking-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ @@ -30,9 +33,11 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( + D42902C31F1F853800685AE7 /* macos */, 148699CD1ABD045300480536 /* RCTLinkingManager.h */, 148699CE1ABD045300480536 /* RCTLinkingManager.m */, 134814211AA4EA7D00B7C361 /* Products */, + D4C812E71F1CC42300FFA059 /* libRCTLinking-macOS.a */, 2D2A28471D9B043800D4039D /* libRCTLinking-tvOS.a */, ); indentWidth = 2; @@ -40,6 +45,14 @@ tabWidth = 2; usesTabs = 0; }; + D42902C31F1F853800685AE7 /* macos */ = { + isa = PBXGroup; + children = ( + D42902891F1CDFF300685AE7 /* RCTLinkingManager.m */, + ); + name = macos; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -73,6 +86,21 @@ productReference = 134814201AA4EA6300B7C361 /* libRCTLinking.a */; productType = "com.apple.product-type.library.static"; }; + D4C812E11F1CC42300FFA059 /* RCTLinking-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D4C812E41F1CC42300FFA059 /* Build configuration list for PBXNativeTarget "RCTLinking-macOS" */; + buildPhases = ( + D4C812E21F1CC42300FFA059 /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RCTLinking-macOS"; + productName = RCTDataManager; + productReference = D4C812E71F1CC42300FFA059 /* libRCTLinking-macOS.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -105,6 +133,7 @@ targets = ( 58B511DA1A9E6C8500147676 /* RCTLinking */, 2D2A28461D9B043800D4039D /* RCTLinking-tvOS */, + D4C812E11F1CC42300FFA059 /* RCTLinking-macOS */, ); }; /* End PBXProject section */ @@ -126,6 +155,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D4C812E21F1CC42300FFA059 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D42902C41F1F85F100685AE7 /* RCTLinkingManager.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -199,6 +236,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -240,6 +278,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -269,6 +308,26 @@ }; name = Release; }; + D4C812E51F1CC42300FFA059 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + D4C812E61F1CC42300FFA059 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -299,6 +358,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + D4C812E41F1CC42300FFA059 /* Build configuration list for PBXNativeTarget "RCTLinking-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D4C812E51F1CC42300FFA059 /* Debug */, + D4C812E61F1CC42300FFA059 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 58B511D31A9E6C8500147676 /* Project object */; diff --git a/Libraries/LinkingIOS/RCTLinkingManager.h b/Libraries/LinkingIOS/RCTLinkingManager.h index 8f5f8b89aa7b27..8bada5f1c29ab8 100644 --- a/Libraries/LinkingIOS/RCTLinkingManager.h +++ b/Libraries/LinkingIOS/RCTLinkingManager.h @@ -7,12 +7,15 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import #import @interface RCTLinkingManager : RCTEventEmitter +#if TARGET_OS_OSX ++ (void)getUrlEventHandler:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent; +#else + (BOOL)application:(UIApplication *)app openURL:(NSURL *)URL options:(NSDictionary *)options; @@ -25,5 +28,5 @@ + (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler; - +#endif @end diff --git a/Libraries/LinkingIOS/macos/RCTLinkingManager.m b/Libraries/LinkingIOS/macos/RCTLinkingManager.m new file mode 100644 index 00000000000000..3b8fb0a57bc221 --- /dev/null +++ b/Libraries/LinkingIOS/macos/RCTLinkingManager.m @@ -0,0 +1,106 @@ + +#import "RCTLinkingManager.h" + +#import +#import + +NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification"; + +static NSString *initialURL = nil; +static BOOL moduleInitalized = NO; + +static void postNotificationWithURL(NSString *url, id sender) +{ + NSDictionary *payload = @{@"url": url}; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTOpenURLNotification + object:sender + userInfo:payload]; +} + +@implementation RCTLinkingManager + +RCT_EXPORT_MODULE() + +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + +- (void)startObserving +{ + moduleInitalized = YES; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleOpenURLNotification:) + name:RCTOpenURLNotification + object:nil]; +} + +- (void)stopObserving +{ + moduleInitalized = NO; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (NSDictionary *)constantsToExport +{ + NSString *argv = self.bridge.launchOptions[@"argv"]; + return @{@"argv": RCTNullIfNil(argv)}; +} + +- (NSArray *)supportedEvents +{ + return @[@"url"]; +} + + ++ (void)getUrlEventHandler:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent +{ + // extract url value from the event + NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; + + // If the application was launched via URL, this handler will be called before + // the module is initialized by the bridge. Store the initial URL, becase we are not listening to the notification yet. + if (!moduleInitalized && initialURL == nil) { + initialURL = url; + } + + postNotificationWithURL(url, self); +} + +- (void)handleOpenURLNotification:(NSNotification *)notification +{ + // foreground top level window, need to grab it like this, because [NSApp mainWindow] returns nil when the app is hidden + // and another app is maximized + [NSApp activateIgnoringOtherApps:YES]; + NSWindow *lastWindow = [[NSApp windows] lastObject]; + [lastWindow makeKeyAndOrderFront:nil]; + + [self sendEventWithName:@"url" body:notification.userInfo[@"url"]]; +} + +RCT_EXPORT_METHOD(openURL:(NSURL *)URL + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) +{ + BOOL result = [[NSWorkspace sharedWorkspace] openURL:URL]; + if (result) { + resolve(@YES); + } else { + reject(RCTErrorUnspecified, [NSString stringWithFormat:@"Unable to open URL: %@", URL], nil); + } +} + +RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL + resolve:(RCTPromiseResolveBlock)resolve + reject:(__unused RCTPromiseRejectBlock)reject) +{ + resolve(@YES); +} + +RCT_EXPORT_METHOD(getInitialURL:(RCTPromiseResolveBlock)resolve + reject:(__unused RCTPromiseRejectBlock)reject) +{ + resolve(RCTNullIfNil(initialURL)); +} +@end diff --git a/Libraries/Lists/FlatList.js b/Libraries/Lists/FlatList.js index 9b145cdbd4b5bc..b7b38e62a937bd 100644 --- a/Libraries/Lists/FlatList.js +++ b/Libraries/Lists/FlatList.js @@ -586,7 +586,7 @@ class FlatList extends React.PureComponent, void> { _renderItem = (info: Object) => { const {renderItem, numColumns, columnWrapperStyle} = this.props; if (numColumns > 1) { - const {item, index} = info; + const {item, index, isSelected} = info; invariant( Array.isArray(item), 'Expected array of items with numColumns > 1', @@ -597,6 +597,7 @@ class FlatList extends React.PureComponent, void> { const element = renderItem({ item: it, index: index * numColumns + kk, + isSelected: isSelected, separators: info.separators, }); return element && React.cloneElement(element, {key: kk}); diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index ba60b610432f3d..8870bcc0ce63ed 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -14,6 +14,7 @@ const Batchinator = require('Batchinator'); const FillRateHelper = require('FillRateHelper'); +const Platform = require('Platform'); const PropTypes = require('prop-types'); const React = require('React'); const ReactNative = require('ReactNative'); @@ -80,6 +81,12 @@ type OptionalProps = { * this for debugging purposes. */ disableVirtualization: boolean, + /** + * Handles key down events and updates selection based on the key event + * + * @platform macos + */ + enableSelectionOnKeyPress: boolean, /** * A marker property for telling the list to re-render (since it implements `PureComponent`). If * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the @@ -153,6 +160,19 @@ type OptionalProps = { highestMeasuredFrameIndex: number, averageItemLength: number, }) => void, + /** + * If provided, will be invoked whenever the selection on the list changes. Make sure to set + * the property enableSelectionOnKeyPress to true to change selection via keyboard (macOS). + * + * @platform macos + */ + onSelectionChanged: ?Function, + /** + * If provided, called when 'Enter' key is pressed on an item. + * + * @platform macos + */ + onSelectionEntered: ?Function, /** * Called when the viewability of rows changes, as defined by the * `viewabilityConfig` prop. @@ -354,6 +374,24 @@ class VirtualizedList extends React.PureComponent { ); } + ensureItemAtIndexIsVisible = (rowIndex) => { + const frame = this._getFrameMetricsApprox(rowIndex); + const visTop = this._scrollMetrics.offset; + const visLen = this._scrollMetrics.visibleLength; + const visEnd = visTop + visLen; + const contentLength = this._scrollMetrics.contentLength; + const frameEnd = frame.offset + frame.length; + + if (frameEnd > visEnd) { + const newOffset = Math.min(contentLength, visTop + (frameEnd - visEnd)); + this.scrollToOffset({offset : newOffset}); + } else if (frame.offset < visTop) { + const newOffset = Math.max(0, visTop - frame.length); + this.scrollToOffset({offset : newOffset }); + } + } + + recordInteraction() { this._viewabilityTuples.forEach(t => { t.viewabilityHelper.recordInteraction(); @@ -562,6 +600,7 @@ class VirtualizedList extends React.PureComponent { index={ii} inversionStyle={inversionStyle} item={item} + isSelected={ this.state.selectedRowIndex == ii ? true : false } key={key} prevCellKey={prevCellKey} onUpdateSeparators={this._onUpdateSeparators} @@ -765,7 +804,7 @@ class VirtualizedList extends React.PureComponent { , ); } - const scrollProps = { + var scrollProps = { ...this.props, onContentSizeChange: this._onContentSizeChange, onLayout: this._onLayout, @@ -843,6 +882,10 @@ class VirtualizedList extends React.PureComponent { } _defaultRenderScrollComponent = props => { + let keyEventHandler = this.props.onKeyDown; + if (!keyEventHandler) { + keyEventHandler = this.props.enableSelectionOnKeyPress ? this._handleKeyDown : null; + } if (this._isNestedWithSameOrientation()) { return ; } else if (props.onRefresh) { @@ -851,13 +894,14 @@ class VirtualizedList extends React.PureComponent { '`refreshing` prop must be set as a boolean in order to use `onRefresh`, but got `' + JSON.stringify(props.refreshing) + '`', - ); + ); return ( /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This * comment suppresses an error when upgrading Flow's support for React. * To see the error delete this comment and run Flow. */ =0.53.0 site=react_native_fb,react_native_oss) This * comment suppresses an error when upgrading Flow's support for @@ -874,7 +918,7 @@ class VirtualizedList extends React.PureComponent { /* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This * comment suppresses an error when upgrading Flow's support for React. * To see the error delete this comment and run Flow. */ - return ; + return ; } }; @@ -937,6 +981,66 @@ class VirtualizedList extends React.PureComponent { this._headerLength = this._selectLength(e.nativeEvent.layout); }; + _selectRowAboveIndex = (rowIndex) => { + const rowAbove = rowIndex > 0 ? rowIndex - 1 : rowIndex; + this.setState( state => { return {selectedRowIndex: rowAbove}; }); + return rowAbove; + } + + _selectRowBelowIndex = (rowIndex) => { + if (this.props.getItemCount) { + const {data} = this.props; + const itemCount = this.props.getItemCount(data); + const rowBelow = rowIndex < (itemCount - 1) ? rowIndex + 1 : rowIndex; + this.setState( state => { return {selectedRowIndex: rowBelow}; }); + return rowBelow; + } + } + + _handleKeyDown = (e) => { + if (this.props.onKeyDown) { + this.props.onKeyDown(e); + } + else { + if (Platform.OS === 'macos') { + const event = e['nativeEvent']; + const key = event['key']; + + let prevIndex = -1; + let newIndex = -1; + if ("selectedRowIndex" in this.state) { + prevIndex = this.state.selectedRowIndex; + } + + const {data, getItem} = this.props; + if (key === 'DOWN_ARROW') { + newIndex = this._selectRowBelowIndex(prevIndex); + this.ensureItemAtIndexIsVisible(newIndex); + + if (this.props.onSelectionChanged && prevIndex != newIndex) { + const item = getItem(data, newIndex); + this.props.onSelectionChanged( {previousSelection: prevIndex, newSelection: newIndex, item: item}); + } + } + else if (key === 'UP_ARROW') { + newIndex = this._selectRowAboveIndex(prevIndex); + this.ensureItemAtIndexIsVisible(newIndex); + + if (this.props.onSelectionChanged && prevIndex != newIndex) { + const item = getItem(data, newIndex); + this.props.onSelectionChanged( {previousSelection: prevIndex, newSelection: newIndex, item: item}); + } + } + else if (key === 'ENTER') { + if (this.props.onSelectionEntered) { + const item = getItem(data, prevIndex); + this.props.onSelectionEntered(item); + } + } + } + } + } + _renderDebugOverlay() { const normalize = this._scrollMetrics.visibleLength / this._scrollMetrics.contentLength; @@ -1279,6 +1383,7 @@ class CellRenderer extends React.Component< horizontal: ?boolean, index: number, inversionStyle: ?StyleObj, + isSelected: Boolean, item: Item, onLayout: (event: Object) => void, // This is extracted by ScrollViewStickyHeader onUnmount: (cellKey: string) => void, @@ -1341,6 +1446,7 @@ class CellRenderer extends React.Component< item, index, inversionStyle, + isSelected, parentProps, } = this.props; const {renderItem, getItemLayout} = parentProps; @@ -1348,6 +1454,7 @@ class CellRenderer extends React.Component< const element = renderItem({ item, index, + isSelected, separators: this._separators, }); const onLayout = diff --git a/Libraries/Lists/VirtualizedSectionList.js b/Libraries/Lists/VirtualizedSectionList.js index ca3a8708239a14..0a2b4d8ecbe2ba 100644 --- a/Libraries/Lists/VirtualizedSectionList.js +++ b/Libraries/Lists/VirtualizedSectionList.js @@ -12,6 +12,7 @@ */ 'use strict'; +const Platform = require('Platform'); const React = require('React'); const View = require('View'); const VirtualizedList = require('VirtualizedList'); @@ -54,6 +55,12 @@ type RequiredProps = { }; type OptionalProps = { + /** + * Handles key down events and updates selection based on the key event + * + * @platform macos + */ + enableSelectionOnKeyPress: boolean, /** * Rendered after the last item in the last section. */ @@ -105,6 +112,19 @@ type OptionalProps = { * sure to also set the `refreshing` prop correctly. */ onRefresh?: ?Function, + /** + * If provided, processes key press and mouse click events to update selection state + * and invokes the provided function to notify of selection state changes. + * + * @platform macos + */ + onSelectionChanged: ?Function, + /** + * If provided, called when 'Enter' key is pressed on an item. + * + * @platform macos + */ + onSelectionEntered: ?Function, /** * Called when the viewability of rows changes, as defined by the * `viewabilityConfig` prop. @@ -255,6 +275,23 @@ class VirtualizedSectionList extends React.PureComponent< } }; + _isItemSelected = (item: Item) : Boolean => { + let isSelected = false; + if (this.state.selectedRowIndexPath) + { + const selection = this.state.selectedRowIndexPath; + const sections = this.props.sections; + if (sections && selection.sectionIndex < sections.length) { + const section = sections[selection.sectionIndex]; + if (selection.rowIndex < section.data.length) { + const selectedItem = section.data[selection.rowIndex]; + isSelected = (item == selectedItem); + } + } + } + return isSelected; + } + _renderItem = ({item, index}: {item: Item, index: number}) => { const info = this._subExtractor(index); if (!info) { @@ -273,7 +310,7 @@ class VirtualizedSectionList extends React.PureComponent< } else { const renderItem = info.section.renderItem || this.props.renderItem; const SeparatorComponent = this._getSeparatorComponent(index, info); - invariant(renderItem, 'no renderItem!'); + invariant(renderItem, 'no renderItem!'); return ( extends React.PureComponent< } cellKey={info.key} index={infoIndex} + isSelected={this._isItemSelected(item)} item={item} leadingItem={info.leadingItem} leadingSection={info.leadingSection} @@ -333,7 +371,13 @@ class VirtualizedSectionList extends React.PureComponent< stickyHeaderIndices.push(v + offset); return v + section.data.length + 2; // Add two for the section header and footer. }, 0); - + + let selectionIndex = null; + if (this.state && this.state.selectedRowIndexPath) { + selectionIndex = this.state.selectedRowIndexPath; + } else { + selectionIndex = {sectionIndex: 0, rowIndex: -1}; + } return { childProps: { ...props, @@ -350,7 +394,8 @@ class VirtualizedSectionList extends React.PureComponent< ? stickyHeaderIndices : undefined, }, - }; + selectedRowIndexPath: {sectionIndex: 0, rowIndex: -1}, + }; } constructor(props: Props, context: Object) { @@ -361,10 +406,105 @@ class VirtualizedSectionList extends React.PureComponent< componentWillReceiveProps(nextProps: Props) { this.setState(this._computeState(nextProps)); } + + _selectRowAboveIndexPath = (rowIndexPath) => { + let sectionIndex = rowIndexPath.sectionIndex; + if (sectionIndex >= this.props.sections.length) { + return rowIndexPath; + } + + const count = this.props.sections[sectionIndex].data.length; + let row = rowIndexPath.rowIndex; + let rowAbove = row - 1; + + if (rowAbove < 0) { + if (sectionIndex > 0) { + sectionIndex = sectionIndex - 1; + rowAbove = Math.max(0, this.props.sections[sectionIndex].data.length - 1); + } else { + rowAbove = row; + } + } + const nextIndexPath = {sectionIndex: sectionIndex, rowIndex: rowAbove}; + this.setState( state => { return {selectedRowIndexPath: nextIndexPath}; }); + return nextIndexPath; + } + + _selectRowBelowIndexPath = (rowIndexPath) => { + let sectionIndex = rowIndexPath.sectionIndex; + if (sectionIndex >= this.props.sections.length) { + return rowIndexPath; + } + + const count = this.props.sections[sectionIndex].data.length; + let row = rowIndexPath.rowIndex; + let rowBelow = row + 1; + + if (rowBelow > count - 1) { + if (sectionIndex < this.props.sections.length - 1) { + sectionIndex = sectionIndex + 1; + rowBelow = 0; + } + else { + rowBelow = row; + } + } + const nextIndexPath = {sectionIndex: sectionIndex, rowIndex: rowBelow}; + this.setState( state => { return {selectedRowIndexPath: nextIndexPath}; }); + return nextIndexPath; + } + + _ensureItemAtIndexPathIsVisible = (rowIndexPath) => { + let index = rowIndexPath.rowIndex + 1; + for (let ii = 0; ii < rowIndexPath.sectionIndex; ii++) { + index += this.props.sections[ii].data.length + 2; + } + this._listRef.ensureItemAtIndexIsVisible(index); + } + + _handleKeyDown = (e) => { + if (Platform.OS === 'macos') { + const event = e['nativeEvent']; + const key = event['key']; + let prevIndexPath = this.state.selectedRowIndexPath; + let nextIndexPath = null; + const sectionIndex = this.state.selectedRowIndexPath.sectionIndex; + const rowIndex = this.state.selectedRowIndexPath.rowIndex; + + if (key === 'DOWN_ARROW') { + nextIndexPath = this._selectRowBelowIndexPath(prevIndexPath); + this._ensureItemAtIndexPathIsVisible(nextIndexPath); + + if (this.props.onSelectionChanged) { + const item = this.props.sections[sectionIndex].data[rowIndex]; + this.props.onSelectionChanged( {previousSelection: prevIndexPath, newSelection: nextIndexPath, item: item}); + } + } + else if (key === 'UP_ARROW') { + nextIndexPath = this._selectRowAboveIndexPath(prevIndexPath); + this._ensureItemAtIndexPathIsVisible(nextIndexPath); + + if (this.props.onSelectionChanged) { + const item = this.props.sections[sectionIndex].data[rowIndex]; + this.props.onSelectionChanged( {previousSelection: prevIndexPath, newSelection: nextIndexPath, item: item}); + } + } + else if (key === 'ENTER') { + if (this.props.onSelectionEntered) { + const item = this.props.sections[sectionIndex].data[rowIndex]; + this.props.onSelectionEntered(item); + } + } + } + } render() { - return ( - + let keyEventHandler = this.props.onKeyDown; + if (!keyEventHandler) { + keyEventHandler = this.props.enableSelectionOnKeyPress ? this._handleKeyDown : null; + } + return ( + ); } @@ -476,11 +616,13 @@ class ItemWithSeparator extends React.Component< SeparatorComponent, item, index, + isSelected, section, } = this.props; const element = this.props.renderItem({ item, index, + isSelected, section, separators: this._separators, }); diff --git a/Libraries/Lists/__tests__/__snapshots__/FlatList-test.js.snap b/Libraries/Lists/__tests__/__snapshots__/FlatList-test.js.snap index 7007b25c7e7551..67016fe8809d9c 100644 --- a/Libraries/Lists/__tests__/__snapshots__/FlatList-test.js.snap +++ b/Libraries/Lists/__tests__/__snapshots__/FlatList-test.js.snap @@ -36,6 +36,7 @@ exports[`FlatList renders all the bells and whistles 1`] = ` numColumns={2} onContentSizeChange={[Function]} onEndReachedThreshold={2} + onKeyDown={null} onLayout={[Function]} onMomentumScrollEnd={[Function]} onRefresh={[Function]} @@ -153,6 +154,7 @@ exports[`FlatList renders empty list 1`] = ` numColumns={1} onContentSizeChange={[Function]} onEndReachedThreshold={2} + onKeyDown={null} onLayout={[Function]} onMomentumScrollEnd={[Function]} onScroll={[Function]} @@ -182,6 +184,7 @@ exports[`FlatList renders null list 1`] = ` numColumns={1} onContentSizeChange={[Function]} onEndReachedThreshold={2} + onKeyDown={null} onLayout={[Function]} onMomentumScrollEnd={[Function]} onScroll={[Function]} @@ -223,6 +226,7 @@ exports[`FlatList renders simple list 1`] = ` numColumns={1} onContentSizeChange={[Function]} onEndReachedThreshold={2} + onKeyDown={null} onLayout={[Function]} onMomentumScrollEnd={[Function]} onScroll={[Function]} diff --git a/Libraries/Lists/__tests__/__snapshots__/SectionList-test.js.snap b/Libraries/Lists/__tests__/__snapshots__/SectionList-test.js.snap index cd0e25eeb4cba0..e295b32c097ab7 100644 --- a/Libraries/Lists/__tests__/__snapshots__/SectionList-test.js.snap +++ b/Libraries/Lists/__tests__/__snapshots__/SectionList-test.js.snap @@ -27,6 +27,7 @@ exports[`SectionList rendering empty section headers is fine 1`] = ` maxToRenderPerBatch={10} onContentSizeChange={[Function]} onEndReachedThreshold={2} + onKeyDown={null} onLayout={[Function]} onMomentumScrollEnd={[Function]} onScroll={[Function]} @@ -35,7 +36,9 @@ exports[`SectionList rendering empty section headers is fine 1`] = ` onViewableItemsChanged={undefined} renderItem={[Function]} renderSectionHeader={[Function]} + rowIndex={-1} scrollEventThrottle={50} + sectionIndex={0} sections={ Array [ Object { @@ -109,6 +112,7 @@ exports[`SectionList renders a footer when there is no data 1`] = ` maxToRenderPerBatch={10} onContentSizeChange={[Function]} onEndReachedThreshold={2} + onKeyDown={null} onLayout={[Function]} onMomentumScrollEnd={[Function]} onScroll={[Function]} @@ -118,7 +122,9 @@ exports[`SectionList renders a footer when there is no data 1`] = ` renderItem={[Function]} renderSectionFooter={[Function]} renderSectionHeader={[Function]} + rowIndex={-1} scrollEventThrottle={50} + sectionIndex={0} sections={ Array [ Object { @@ -177,6 +183,7 @@ exports[`SectionList renders a footer when there is no data and no header 1`] = maxToRenderPerBatch={10} onContentSizeChange={[Function]} onEndReachedThreshold={2} + onKeyDown={null} onLayout={[Function]} onMomentumScrollEnd={[Function]} onScroll={[Function]} @@ -185,7 +192,9 @@ exports[`SectionList renders a footer when there is no data and no header 1`] = onViewableItemsChanged={undefined} renderItem={[Function]} renderSectionFooter={[Function]} + rowIndex={-1} scrollEventThrottle={50} + sectionIndex={0} sections={ Array [ Object { @@ -276,6 +285,7 @@ exports[`SectionList renders all the bells and whistles 1`] = ` maxToRenderPerBatch={10} onContentSizeChange={[Function]} onEndReachedThreshold={2} + onKeyDown={null} onLayout={[Function]} onMomentumScrollEnd={[Function]} onRefresh={[Function]} @@ -294,7 +304,9 @@ exports[`SectionList renders all the bells and whistles 1`] = ` renderItem={[Function]} renderSectionFooter={[Function]} renderSectionHeader={[Function]} + rowIndex={-1} scrollEventThrottle={50} + sectionIndex={0} sections={ Array [ Object { @@ -373,7 +385,7 @@ exports[`SectionList renders all the bells and whistles 1`] = ` v="highlighted:false,leadingItem:undefined,leadingSection:undefined,section:s1,trailingItem:i1s1,trailingSection:s2" /> +#import #import #import "RCTValueAnimatedNode.h" diff --git a/Libraries/NativeAnimation/Drivers/RCTFrameAnimation.m b/Libraries/NativeAnimation/Drivers/RCTFrameAnimation.m index cd8b3a4817c3cd..bdd2144bd06c66 100644 --- a/Libraries/NativeAnimation/Drivers/RCTFrameAnimation.m +++ b/Libraries/NativeAnimation/Drivers/RCTFrameAnimation.m @@ -9,7 +9,7 @@ #import "RCTFrameAnimation.h" -#import +#import #import #import diff --git a/Libraries/NativeAnimation/Drivers/RCTSpringAnimation.m b/Libraries/NativeAnimation/Drivers/RCTSpringAnimation.m index e4811f601d2594..b776cced91938e 100644 --- a/Libraries/NativeAnimation/Drivers/RCTSpringAnimation.m +++ b/Libraries/NativeAnimation/Drivers/RCTSpringAnimation.m @@ -9,7 +9,7 @@ #import "RCTSpringAnimation.h" -#import +#import #import #import diff --git a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h index 5b66520b8210b5..b0d047daba28be 100644 --- a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h +++ b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import #import "RCTAnimatedNode.h" diff --git a/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj b/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj index cddec7f5e67eeb..6c520ee7984536 100644 --- a/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj +++ b/Libraries/NativeAnimation/RCTAnimation.xcodeproj/project.pbxproj @@ -112,6 +112,62 @@ 2D3B5EFF1D9B0B4800451313 /* RCTTransformAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E51D07A6C9005F35D8 /* RCTTransformAnimatedNode.m */; }; 2D3B5F001D9B0B4800451313 /* RCTValueAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E71D07A6C9005F35D8 /* RCTValueAnimatedNode.m */; }; 5C9894951D999639008027DB /* RCTDivisionAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9894941D999639008027DB /* RCTDivisionAnimatedNode.m */; }; + 647647881F0BC7F200C2D89B /* RCTFrameAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294D1D4069170025F25C /* RCTFrameAnimation.m */; }; + 647647891F0BC7F200C2D89B /* RCTSpringAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294F1D4069170025F25C /* RCTSpringAnimation.m */; }; + 6476478A1F0BC7F200C2D89B /* RCTNativeAnimatedNodesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 94DA09171DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.m */; }; + 6476478B1F0BC7F200C2D89B /* RCTValueAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E71D07A6C9005F35D8 /* RCTValueAnimatedNode.m */; }; + 6476478C1F0BC7F200C2D89B /* RCTModuloAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 94DAE3F81D7334A70059942F /* RCTModuloAnimatedNode.m */; }; + 6476478D1F0BC7F200C2D89B /* RCTDiffClampAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 193F64F31D776EC6004D1CAA /* RCTDiffClampAnimatedNode.m */; }; + 6476478E1F0BC7F200C2D89B /* RCTEventAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 19F00F211DC8847500113FEE /* RCTEventAnimation.m */; }; + 6476478F1F0BC7F200C2D89B /* RCTStyleAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E31D07A6C9005F35D8 /* RCTStyleAnimatedNode.m */; }; + 647647901F0BC7F200C2D89B /* RCTAnimationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501B81D07A644005F35D8 /* RCTAnimationUtils.m */; }; + 647647911F0BC7F200C2D89B /* RCTNativeAnimatedModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501BE1D07A644005F35D8 /* RCTNativeAnimatedModule.m */; }; + 647647921F0BC7F200C2D89B /* RCTMultiplicationAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501DF1D07A6C9005F35D8 /* RCTMultiplicationAnimatedNode.m */; }; + 647647931F0BC7F200C2D89B /* RCTPropsAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E11D07A6C9005F35D8 /* RCTPropsAnimatedNode.m */; }; + 647647941F0BC7F200C2D89B /* RCTAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501D91D07A6C9005F35D8 /* RCTAnimatedNode.m */; }; + 647647951F0BC7F200C2D89B /* RCTInterpolationAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501DD1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.m */; }; + 647647961F0BC7F200C2D89B /* RCTAdditionAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501D71D07A6C9005F35D8 /* RCTAdditionAnimatedNode.m */; }; + 647647971F0BC7F200C2D89B /* RCTDivisionAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9894941D999639008027DB /* RCTDivisionAnimatedNode.m */; }; + 647647981F0BC7F200C2D89B /* RCTTransformAnimatedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E501E51D07A6C9005F35D8 /* RCTTransformAnimatedNode.m */; }; + 647647991F0BC7F200C2D89B /* RCTDecayAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 194804EC1E975D8E00623005 /* RCTDecayAnimation.m */; }; + 6476479B1F0BC7F200C2D89B /* RCTAnimationUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E501B71D07A644005F35D8 /* RCTAnimationUtils.h */; }; + 6476479C1F0BC7F200C2D89B /* RCTNativeAnimatedModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E501BD1D07A644005F35D8 /* RCTNativeAnimatedModule.h */; }; + 6476479D1F0BC7F200C2D89B /* RCTNativeAnimatedNodesManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 94DA09161DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.h */; }; + 6476479E1F0BC7F200C2D89B /* RCTAnimationDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 94C1294A1D4069170025F25C /* RCTAnimationDriver.h */; }; + 6476479F1F0BC7F200C2D89B /* RCTEventAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 19F00F201DC8847500113FEE /* RCTEventAnimation.h */; }; + 647647A01F0BC7F200C2D89B /* RCTDecayAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 194804EB1E975D8E00623005 /* RCTDecayAnimation.h */; }; + 647647A11F0BC7F200C2D89B /* RCTFrameAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 94C1294C1D4069170025F25C /* RCTFrameAnimation.h */; }; + 647647A21F0BC7F200C2D89B /* RCTSpringAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 94C1294E1D4069170025F25C /* RCTSpringAnimation.h */; }; + 647647A31F0BC7F200C2D89B /* RCTDivisionAnimatedNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C9894931D999639008027DB /* RCTDivisionAnimatedNode.h */; }; + 647647A41F0BC7F200C2D89B /* RCTDiffClampAnimatedNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 193F64F21D776EC6004D1CAA /* RCTDiffClampAnimatedNode.h */; }; + 647647A51F0BC7F200C2D89B /* RCTAdditionAnimatedNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E501D61D07A6C9005F35D8 /* RCTAdditionAnimatedNode.h */; }; + 647647A61F0BC7F200C2D89B /* RCTAnimatedNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E501D81D07A6C9005F35D8 /* RCTAnimatedNode.h */; }; + 647647A71F0BC7F200C2D89B /* RCTInterpolationAnimatedNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E501DC1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.h */; }; + 647647A81F0BC7F200C2D89B /* RCTModuloAnimatedNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 94DAE3F71D7334A70059942F /* RCTModuloAnimatedNode.h */; }; + 647647A91F0BC7F200C2D89B /* RCTMultiplicationAnimatedNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E501DE1D07A6C9005F35D8 /* RCTMultiplicationAnimatedNode.h */; }; + 647647AA1F0BC7F200C2D89B /* RCTPropsAnimatedNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E501E01D07A6C9005F35D8 /* RCTPropsAnimatedNode.h */; }; + 647647AB1F0BC7F200C2D89B /* RCTStyleAnimatedNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E501E21D07A6C9005F35D8 /* RCTStyleAnimatedNode.h */; }; + 647647AC1F0BC7F200C2D89B /* RCTTransformAnimatedNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E501E41D07A6C9005F35D8 /* RCTTransformAnimatedNode.h */; }; + 647647AD1F0BC7F200C2D89B /* RCTValueAnimatedNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 13E501E61D07A6C9005F35D8 /* RCTValueAnimatedNode.h */; }; + 647647AF1F0BC7F200C2D89B /* RCTDecayAnimation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 194804EB1E975D8E00623005 /* RCTDecayAnimation.h */; }; + 647647B01F0BC7F200C2D89B /* RCTNativeAnimatedModule.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13E501BD1D07A644005F35D8 /* RCTNativeAnimatedModule.h */; }; + 647647B11F0BC7F200C2D89B /* RCTNativeAnimatedNodesManager.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 94DA09161DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.h */; }; + 647647B21F0BC7F200C2D89B /* RCTAnimationDriver.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 94C1294A1D4069170025F25C /* RCTAnimationDriver.h */; }; + 647647B31F0BC7F200C2D89B /* RCTEventAnimation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 19F00F201DC8847500113FEE /* RCTEventAnimation.h */; }; + 647647B41F0BC7F200C2D89B /* RCTFrameAnimation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 94C1294C1D4069170025F25C /* RCTFrameAnimation.h */; }; + 647647B51F0BC7F200C2D89B /* RCTSpringAnimation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 94C1294E1D4069170025F25C /* RCTSpringAnimation.h */; }; + 647647B61F0BC7F200C2D89B /* RCTDivisionAnimatedNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5C9894931D999639008027DB /* RCTDivisionAnimatedNode.h */; }; + 647647B71F0BC7F200C2D89B /* RCTDiffClampAnimatedNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 193F64F21D776EC6004D1CAA /* RCTDiffClampAnimatedNode.h */; }; + 647647B81F0BC7F200C2D89B /* RCTAdditionAnimatedNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13E501D61D07A6C9005F35D8 /* RCTAdditionAnimatedNode.h */; }; + 647647B91F0BC7F200C2D89B /* RCTAnimatedNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13E501D81D07A6C9005F35D8 /* RCTAnimatedNode.h */; }; + 647647BA1F0BC7F200C2D89B /* RCTInterpolationAnimatedNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13E501DC1D07A6C9005F35D8 /* RCTInterpolationAnimatedNode.h */; }; + 647647BB1F0BC7F200C2D89B /* RCTModuloAnimatedNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 94DAE3F71D7334A70059942F /* RCTModuloAnimatedNode.h */; }; + 647647BC1F0BC7F200C2D89B /* RCTMultiplicationAnimatedNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13E501DE1D07A6C9005F35D8 /* RCTMultiplicationAnimatedNode.h */; }; + 647647BD1F0BC7F200C2D89B /* RCTPropsAnimatedNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13E501E01D07A6C9005F35D8 /* RCTPropsAnimatedNode.h */; }; + 647647BE1F0BC7F200C2D89B /* RCTStyleAnimatedNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13E501E21D07A6C9005F35D8 /* RCTStyleAnimatedNode.h */; }; + 647647BF1F0BC7F200C2D89B /* RCTTransformAnimatedNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13E501E41D07A6C9005F35D8 /* RCTTransformAnimatedNode.h */; }; + 647647C01F0BC7F200C2D89B /* RCTValueAnimatedNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13E501E61D07A6C9005F35D8 /* RCTValueAnimatedNode.h */; }; + 647647C11F0BC7F200C2D89B /* RCTAnimationUtils.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13E501B71D07A644005F35D8 /* RCTAnimationUtils.h */; }; 944244D01DB962DA0032A02B /* RCTFrameAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294D1D4069170025F25C /* RCTFrameAnimation.m */; }; 944244D11DB962DC0032A02B /* RCTSpringAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 94C1294F1D4069170025F25C /* RCTSpringAnimation.m */; }; 9476E8EC1DC9232D005D5CD1 /* RCTNativeAnimatedNodesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 94DA09171DC7971C00AEA8C9 /* RCTNativeAnimatedNodesManager.m */; }; @@ -178,6 +234,34 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 647647AE1F0BC7F200C2D89B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/RCTAnimation; + dstSubfolderSpec = 16; + files = ( + 647647AF1F0BC7F200C2D89B /* RCTDecayAnimation.h in CopyFiles */, + 647647B01F0BC7F200C2D89B /* RCTNativeAnimatedModule.h in CopyFiles */, + 647647B11F0BC7F200C2D89B /* RCTNativeAnimatedNodesManager.h in CopyFiles */, + 647647B21F0BC7F200C2D89B /* RCTAnimationDriver.h in CopyFiles */, + 647647B31F0BC7F200C2D89B /* RCTEventAnimation.h in CopyFiles */, + 647647B41F0BC7F200C2D89B /* RCTFrameAnimation.h in CopyFiles */, + 647647B51F0BC7F200C2D89B /* RCTSpringAnimation.h in CopyFiles */, + 647647B61F0BC7F200C2D89B /* RCTDivisionAnimatedNode.h in CopyFiles */, + 647647B71F0BC7F200C2D89B /* RCTDiffClampAnimatedNode.h in CopyFiles */, + 647647B81F0BC7F200C2D89B /* RCTAdditionAnimatedNode.h in CopyFiles */, + 647647B91F0BC7F200C2D89B /* RCTAnimatedNode.h in CopyFiles */, + 647647BA1F0BC7F200C2D89B /* RCTInterpolationAnimatedNode.h in CopyFiles */, + 647647BB1F0BC7F200C2D89B /* RCTModuloAnimatedNode.h in CopyFiles */, + 647647BC1F0BC7F200C2D89B /* RCTMultiplicationAnimatedNode.h in CopyFiles */, + 647647BD1F0BC7F200C2D89B /* RCTPropsAnimatedNode.h in CopyFiles */, + 647647BE1F0BC7F200C2D89B /* RCTStyleAnimatedNode.h in CopyFiles */, + 647647BF1F0BC7F200C2D89B /* RCTTransformAnimatedNode.h in CopyFiles */, + 647647C01F0BC7F200C2D89B /* RCTValueAnimatedNode.h in CopyFiles */, + 647647C11F0BC7F200C2D89B /* RCTAnimationUtils.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -211,6 +295,7 @@ 2D2A28201D9B03D100D4039D /* libRCTAnimation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTAnimation.a; sourceTree = BUILT_PRODUCTS_DIR; }; 5C9894931D999639008027DB /* RCTDivisionAnimatedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTDivisionAnimatedNode.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 5C9894941D999639008027DB /* RCTDivisionAnimatedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDivisionAnimatedNode.m; sourceTree = ""; }; + 647647C51F0BC7F200C2D89B /* libRCTAnimation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTAnimation.a; sourceTree = BUILT_PRODUCTS_DIR; }; 94C1294A1D4069170025F25C /* RCTAnimationDriver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTAnimationDriver.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 94C1294C1D4069170025F25C /* RCTFrameAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTFrameAnimation.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 94C1294D1D4069170025F25C /* RCTFrameAnimation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTFrameAnimation.m; sourceTree = ""; }; @@ -273,6 +358,7 @@ 13E501D51D07A6C9005F35D8 /* Nodes */, 134814211AA4EA7D00B7C361 /* Products */, 2D2A28201D9B03D100D4039D /* libRCTAnimation.a */, + 647647C51F0BC7F200C2D89B /* libRCTAnimation.a */, ); indentWidth = 2; sourceTree = ""; @@ -350,6 +436,32 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6476479A1F0BC7F200C2D89B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 6476479B1F0BC7F200C2D89B /* RCTAnimationUtils.h in Headers */, + 6476479C1F0BC7F200C2D89B /* RCTNativeAnimatedModule.h in Headers */, + 6476479D1F0BC7F200C2D89B /* RCTNativeAnimatedNodesManager.h in Headers */, + 6476479E1F0BC7F200C2D89B /* RCTAnimationDriver.h in Headers */, + 6476479F1F0BC7F200C2D89B /* RCTEventAnimation.h in Headers */, + 647647A01F0BC7F200C2D89B /* RCTDecayAnimation.h in Headers */, + 647647A11F0BC7F200C2D89B /* RCTFrameAnimation.h in Headers */, + 647647A21F0BC7F200C2D89B /* RCTSpringAnimation.h in Headers */, + 647647A31F0BC7F200C2D89B /* RCTDivisionAnimatedNode.h in Headers */, + 647647A41F0BC7F200C2D89B /* RCTDiffClampAnimatedNode.h in Headers */, + 647647A51F0BC7F200C2D89B /* RCTAdditionAnimatedNode.h in Headers */, + 647647A61F0BC7F200C2D89B /* RCTAnimatedNode.h in Headers */, + 647647A71F0BC7F200C2D89B /* RCTInterpolationAnimatedNode.h in Headers */, + 647647A81F0BC7F200C2D89B /* RCTModuloAnimatedNode.h in Headers */, + 647647A91F0BC7F200C2D89B /* RCTMultiplicationAnimatedNode.h in Headers */, + 647647AA1F0BC7F200C2D89B /* RCTPropsAnimatedNode.h in Headers */, + 647647AB1F0BC7F200C2D89B /* RCTStyleAnimatedNode.h in Headers */, + 647647AC1F0BC7F200C2D89B /* RCTTransformAnimatedNode.h in Headers */, + 647647AD1F0BC7F200C2D89B /* RCTValueAnimatedNode.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -387,6 +499,23 @@ productReference = 134814201AA4EA6300B7C361 /* libRCTAnimation.a */; productType = "com.apple.product-type.library.static"; }; + 647647861F0BC7F200C2D89B /* RCTAnimation-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 647647C21F0BC7F200C2D89B /* Build configuration list for PBXNativeTarget "RCTAnimation-macOS" */; + buildPhases = ( + 647647871F0BC7F200C2D89B /* Sources */, + 6476479A1F0BC7F200C2D89B /* Headers */, + 647647AE1F0BC7F200C2D89B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RCTAnimation-macOS"; + productName = RCTDataManager; + productReference = 647647C51F0BC7F200C2D89B /* libRCTAnimation.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -419,6 +548,7 @@ targets = ( 58B511DA1A9E6C8500147676 /* RCTAnimation */, 2D2A281F1D9B03D100D4039D /* RCTAnimation-tvOS */, + 647647861F0BC7F200C2D89B /* RCTAnimation-macOS */, ); }; /* End PBXProject section */ @@ -474,6 +604,31 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 647647871F0BC7F200C2D89B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 647647881F0BC7F200C2D89B /* RCTFrameAnimation.m in Sources */, + 647647891F0BC7F200C2D89B /* RCTSpringAnimation.m in Sources */, + 6476478A1F0BC7F200C2D89B /* RCTNativeAnimatedNodesManager.m in Sources */, + 6476478B1F0BC7F200C2D89B /* RCTValueAnimatedNode.m in Sources */, + 6476478C1F0BC7F200C2D89B /* RCTModuloAnimatedNode.m in Sources */, + 6476478D1F0BC7F200C2D89B /* RCTDiffClampAnimatedNode.m in Sources */, + 6476478E1F0BC7F200C2D89B /* RCTEventAnimation.m in Sources */, + 6476478F1F0BC7F200C2D89B /* RCTStyleAnimatedNode.m in Sources */, + 647647901F0BC7F200C2D89B /* RCTAnimationUtils.m in Sources */, + 647647911F0BC7F200C2D89B /* RCTNativeAnimatedModule.m in Sources */, + 647647921F0BC7F200C2D89B /* RCTMultiplicationAnimatedNode.m in Sources */, + 647647931F0BC7F200C2D89B /* RCTPropsAnimatedNode.m in Sources */, + 647647941F0BC7F200C2D89B /* RCTAnimatedNode.m in Sources */, + 647647951F0BC7F200C2D89B /* RCTInterpolationAnimatedNode.m in Sources */, + 647647961F0BC7F200C2D89B /* RCTAdditionAnimatedNode.m in Sources */, + 647647971F0BC7F200C2D89B /* RCTDivisionAnimatedNode.m in Sources */, + 647647981F0BC7F200C2D89B /* RCTTransformAnimatedNode.m in Sources */, + 647647991F0BC7F200C2D89B /* RCTDecayAnimation.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -544,6 +699,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -587,6 +743,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -617,6 +774,26 @@ }; name = Release; }; + 647647C31F0BC7F200C2D89B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTAnimation; + SDKROOT = macosx; + }; + name = Debug; + }; + 647647C41F0BC7F200C2D89B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTAnimation; + SDKROOT = macosx; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -647,6 +824,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 647647C21F0BC7F200C2D89B /* Build configuration list for PBXNativeTarget "RCTAnimation-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 647647C31F0BC7F200C2D89B /* Debug */, + 647647C41F0BC7F200C2D89B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 58B511D31A9E6C8500147676 /* Project object */; diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h index 1a0b684c10a271..b97a8d899df940 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h @@ -12,6 +12,7 @@ #import #import #import +#import @interface RCTNativeAnimatedNodesManager : NSObject diff --git a/Libraries/Network/NetInfo.js b/Libraries/Network/NetInfo.js index ae598179feab32..44e6955ff43e13 100644 --- a/Libraries/Network/NetInfo.js +++ b/Libraries/Network/NetInfo.js @@ -60,7 +60,7 @@ type ConnectivityStateAndroid = $Enum<{ const _subscriptions = new Map(); let _isConnectedDeprecated; -if (Platform.OS === 'ios') { +if (Platform.OS === 'ios' || Platform.OS === 'macos') { _isConnectedDeprecated = function( reachability: ReachabilityStateIOS, ): bool { @@ -332,7 +332,7 @@ const NetInfo = { isConnectionExpensive(): Promise { return ( - Platform.OS === 'android' ? RCTNetInfo.isConnectionMetered() : Promise.reject(new Error('Currently not supported on iOS')) + Platform.OS === 'android' ? RCTNetInfo.isConnectionMetered() : Promise.reject(new Error('Currently not supported on iOS or MacOS')) ); }, }; diff --git a/Libraries/Network/RCTFileRequestHandler.m b/Libraries/Network/RCTFileRequestHandler.m index da0b97968b2cab..8af23ccbfb6e57 100644 --- a/Libraries/Network/RCTFileRequestHandler.m +++ b/Libraries/Network/RCTFileRequestHandler.m @@ -9,7 +9,11 @@ #import "RCTFileRequestHandler.h" +#if !TARGET_OS_OSX #import +#else +#import +#endif #import diff --git a/Libraries/Network/RCTNetInfo.m b/Libraries/Network/RCTNetInfo.m index be04f411881ecc..b15813ee3e135f 100644 --- a/Libraries/Network/RCTNetInfo.m +++ b/Libraries/Network/RCTNetInfo.m @@ -59,7 +59,7 @@ static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SC status = RCTReachabilityStateNone; } -#if !TARGET_OS_TV +#if !TARGET_OS_TV && !TARGET_OS_OSX else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { connectionType = RCTConnectionTypeCellular; diff --git a/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj b/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj index 2ca45336dd64d4..4ff1c346dbf997 100644 --- a/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj +++ b/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj @@ -19,8 +19,47 @@ 2D3B5F2B1D9B0EB400451313 /* RCTNetworking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.mm */; }; 352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.mm */; }; 58B512081A9E6CE300147676 /* RCTNetworking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.mm */; }; + 649BF6D820190E380068273E /* RCTHTTPRequestHandler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */; }; + 649BF6D920190E3C0068273E /* RCTFileRequestHandler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */; }; + 649BF6DA20190E3E0068273E /* RCTDataRequestHandler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 134E96981BCEB7F800AFFDA1 /* RCTDataRequestHandler.h */; }; + 649BF6DD20190E690068273E /* RCTHTTPRequestHandler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */; }; + 649BF6DE20190E6C0068273E /* RCTFileRequestHandler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */; }; + 649BF6DF20190E6E0068273E /* RCTDataRequestHandler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 134E96981BCEB7F800AFFDA1 /* RCTDataRequestHandler.h */; }; + 6BDE7A871ECB6E8400CC951F /* RCTNetworkTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */; }; + 6BDE7A881ECB6E8400CC951F /* RCTFileRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */; }; + 6BDE7A891ECB6E8400CC951F /* RCTDataRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */; }; + 6BDE7A8A1ECB6E8400CC951F /* RCTNetInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */; }; + 6BDE7A8B1ECB6E8400CC951F /* RCTNetworking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.mm */; }; + 6BDE7A8C1ECB6E8400CC951F /* RCTHTTPRequestHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.mm */; }; /* End PBXBuildFile section */ +/* Begin PBXCopyFilesBuildPhase section */ + 649BF6D720190E260068273E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/RCTNetwork; + dstSubfolderSpec = 16; + files = ( + 649BF6D820190E380068273E /* RCTHTTPRequestHandler.h in CopyFiles */, + 649BF6D920190E3C0068273E /* RCTFileRequestHandler.h in CopyFiles */, + 649BF6DA20190E3E0068273E /* RCTDataRequestHandler.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 649BF6DC20190E4C0068273E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/RCTNetwork; + dstSubfolderSpec = 16; + files = ( + 649BF6DD20190E690068273E /* RCTHTTPRequestHandler.h in CopyFiles */, + 649BF6DE20190E6C0068273E /* RCTFileRequestHandler.h in CopyFiles */, + 649BF6DF20190E6E0068273E /* RCTDataRequestHandler.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 134E96981BCEB7F800AFFDA1 /* RCTDataRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDataRequestHandler.h; sourceTree = ""; }; 134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDataRequestHandler.m; sourceTree = ""; }; @@ -36,6 +75,7 @@ 3D5FA63F1DE4B4790058FD77 /* RCTNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTNetworking.h; sourceTree = ""; }; 58B511DB1A9E6C8500147676 /* libRCTNetwork.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTNetwork.a; sourceTree = BUILT_PRODUCTS_DIR; }; 58B512071A9E6CE300147676 /* RCTNetworking.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNetworking.mm; sourceTree = ""; }; + 6BDE7A901ECB6E8400CC951F /* libRCTNetwork.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTNetwork.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ @@ -66,6 +106,7 @@ children = ( 58B511DB1A9E6C8500147676 /* libRCTNetwork.a */, 2D2A28541D9B044C00D4039D /* libRCTNetwork-tvOS.a */, + 6BDE7A901ECB6E8400CC951F /* libRCTNetwork.a */, ); name = Products; sourceTree = ""; @@ -93,6 +134,7 @@ buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTNetwork" */; buildPhases = ( 58B511D71A9E6C8500147676 /* Sources */, + 649BF6DC20190E4C0068273E /* CopyFiles */, ); buildRules = ( ); @@ -103,6 +145,22 @@ productReference = 58B511DB1A9E6C8500147676 /* libRCTNetwork.a */; productType = "com.apple.product-type.library.static"; }; + 6BDE7A851ECB6E8400CC951F /* RCTNetwork-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6BDE7A8D1ECB6E8400CC951F /* Build configuration list for PBXNativeTarget "RCTNetwork-macOS" */; + buildPhases = ( + 6BDE7A861ECB6E8400CC951F /* Sources */, + 649BF6D720190E260068273E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RCTNetwork-macOS"; + productName = RCTDataManager; + productReference = 6BDE7A901ECB6E8400CC951F /* libRCTNetwork.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -134,6 +192,7 @@ projectRoot = ""; targets = ( 58B511DA1A9E6C8500147676 /* RCTNetwork */, + 6BDE7A851ECB6E8400CC951F /* RCTNetwork-macOS */, 2D2A28531D9B044C00D4039D /* RCTNetwork-tvOS */, ); }; @@ -166,6 +225,19 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6BDE7A861ECB6E8400CC951F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6BDE7A871ECB6E8400CC951F /* RCTNetworkTask.m in Sources */, + 6BDE7A881ECB6E8400CC951F /* RCTFileRequestHandler.m in Sources */, + 6BDE7A891ECB6E8400CC951F /* RCTDataRequestHandler.m in Sources */, + 6BDE7A8A1ECB6E8400CC951F /* RCTNetInfo.m in Sources */, + 6BDE7A8B1ECB6E8400CC951F /* RCTNetworking.mm in Sources */, + 6BDE7A8C1ECB6E8400CC951F /* RCTHTTPRequestHandler.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -244,6 +316,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -289,6 +362,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -321,6 +395,29 @@ }; name = Release; }; + 6BDE7A8E1ECB6E8400CC951F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTNetwork; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 6BDE7A8F1ECB6E8400CC951F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTNetwork; + SDKROOT = macosx; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -351,6 +448,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 6BDE7A8D1ECB6E8400CC951F /* Build configuration list for PBXNativeTarget "RCTNetwork-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6BDE7A8E1ECB6E8400CC951F /* Debug */, + 6BDE7A8F1ECB6E8400CC951F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 58B511D31A9E6C8500147676 /* Project object */; diff --git a/Libraries/Network/RCTNetworking.js b/Libraries/Network/RCTNetworking.js new file mode 100644 index 00000000000000..35d432dee08222 --- /dev/null +++ b/Libraries/Network/RCTNetworking.js @@ -0,0 +1,90 @@ +/** + * 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 RCTNetworking + * @flow + */ +'use strict'; + +const MissingNativeEventEmitterShim = require('MissingNativeEventEmitterShim'); +const NativeEventEmitter = require('NativeEventEmitter'); +const RCTNetworkingNative = require('NativeModules').Networking; +const convertRequestBody = require('convertRequestBody'); + +import type {RequestBody} from 'convertRequestBody'; + +class RCTNetworking extends NativeEventEmitter { + + isAvailable: boolean = true; + + constructor() { + super(RCTNetworkingNative); + } + + sendRequest( + method: string, + trackingName: string, + url: string, + headers: Object, + data: RequestBody, + responseType: 'text' | 'base64', + incrementalUpdates: boolean, + timeout: number, + callback: (requestId: number) => any, + withCredentials: boolean + ) { + const body = convertRequestBody(data); + RCTNetworkingNative.sendRequest({ + method, + url, + data: {...body, trackingName}, + headers, + responseType, + incrementalUpdates, + timeout, + withCredentials + }, callback); + } + + abortRequest(requestId: number) { + RCTNetworkingNative.abortRequest(requestId); + } + + clearCookies(callback: (result: boolean) => any) { + RCTNetworkingNative.clearCookies(callback); + } +} + +if (__DEV__ && !RCTNetworkingNative) { + class MissingNativeRCTNetworkingShim extends MissingNativeEventEmitterShim { + constructor() { + super('RCTNetworking', 'Networking'); + } + + sendRequest(...args: Array) { + this.throwMissingNativeModule(); + } + + abortRequest(...args: Array) { + this.throwMissingNativeModule(); + } + + clearCookies(...args: Array) { + this.throwMissingNativeModule(); + } + } + + // This module depends on the native `RCTNetworkingNative` module. If you don't include it, + // `RCTNetworking.isAvailable` will return `false`, and any method calls will throw. + // We reassign the class variable to keep the autodoc generator happy. + RCTNetworking = new MissingNativeRCTNetworkingShim(); +} else { + RCTNetworking = new RCTNetworking(); +} + +module.exports = RCTNetworking; diff --git a/Libraries/Network/RCTNetworking.windesktop.js b/Libraries/Network/RCTNetworking.windesktop.js new file mode 100644 index 00000000000000..7feb753df65670 --- /dev/null +++ b/Libraries/Network/RCTNetworking.windesktop.js @@ -0,0 +1,88 @@ +'use strict'; + +// TODO Hx: Implement this module + +// Do not require the native RCTNetworking module directly! Use this wrapper module instead. +// It will add the necessary requestId, so that you don't have to generate it yourself. +const FormData = require('FormData'); +const NativeEventEmitter = require('NativeEventEmitter'); +//const RCTNetworkingNative = require('NativeModules').Networking; +const convertRequestBody = require('convertRequestBody'); + +import type {RequestBody} from 'convertRequestBody'; + +type Header = [string, string]; + +// Convert FormData headers to arrays, which are easier to consume in +// native on Android. +function convertHeadersMapToArray(headers: Object): Array
{ + const headerArray = []; + for (const name in headers) { + headerArray.push([name, headers[name]]); + } + return headerArray; +} + +let _requestId = 1; +function generateRequestId(): number { + return _requestId++; +} + +/** + * This class is a wrapper around the native RCTNetworking module. It adds a necessary unique + * requestId to each network request that can be used to abort that request later on. + */ +class RCTNetworking extends NativeEventEmitter { + + /* + constructor() { + super(RCTNetworkingNative); + } + */ + + sendRequest( + method: string, + trackingName: string, + url: string, + headers: Object, + data: RequestBody, + responseType: 'text' | 'base64', + incrementalUpdates: boolean, + timeout: number, + callback: (requestId: number) => any, + withCredentials: boolean + ) { + /* + const body = convertRequestBody(data); + if (body && body.formData) { + body.formData = body.formData.map((part) => ({ + ...part, + headers: convertHeadersMapToArray(part.headers), + })); + } + const requestId = generateRequestId(); + RCTNetworkingNative.sendRequest( + method, + url, + requestId, + convertHeadersMapToArray(headers), + {...body, trackingName}, + responseType, + incrementalUpdates, + timeout, + withCredentials + ); + callback(requestId); + */ + } + + abortRequest(requestId: number) { + //RCTNetworkingNative.abortRequest(requestId); + } + + clearCookies(callback: (result: boolean) => any) { + //RCTNetworkingNative.clearCookies(callback); + } +} + +module.exports = new RCTNetworking(); diff --git a/Libraries/Network/__tests__/XMLHttpRequest-test.js b/Libraries/Network/__tests__/XMLHttpRequest-test.js index eba667d4b23b3b..b53bc24d385511 100644 --- a/Libraries/Network/__tests__/XMLHttpRequest-test.js +++ b/Libraries/Network/__tests__/XMLHttpRequest-test.js @@ -13,7 +13,7 @@ const Platform = require('Platform'); let requestId = 1; function setRequestId(id){ - if (Platform.OS === 'ios') { + if (Platform.OS === 'ios' || Platform.OS === 'macos') { return; } requestId = id; diff --git a/Libraries/PushNotificationIOS/PushNotificationIOS.js b/Libraries/PushNotificationIOS/PushNotificationIOS.js index fc09fef39ccb0c..fbfd25e17c5b30 100644 --- a/Libraries/PushNotificationIOS/PushNotificationIOS.js +++ b/Libraries/PushNotificationIOS/PushNotificationIOS.js @@ -12,6 +12,7 @@ 'use strict'; const NativeEventEmitter = require('NativeEventEmitter'); +const Platform = require('Platform'); const RCTPushNotificationManager = require('NativeModules').PushNotificationManager; const invariant = require('fbjs/lib/invariant'); @@ -164,9 +165,9 @@ class PushNotificationIOS { * - `alertAction` : The "action" displayed beneath an actionable notification. Defaults to "view"; * - `soundName` : The sound played when the notification is fired (optional). * - `isSilent` : If true, the notification will appear without sound (optional). - * - `category` : The category of this notification, required for actionable notifications (optional). + * - `category` : The category of this notification, required for actionable notifications (optional, iOS only). * - `userInfo` : An optional object containing additional notification data. - * - `applicationIconBadgeNumber` (optional) : The number to display as the app's icon badge. Setting the number to 0 removes the icon badge. + * - `applicationIconBadgeNumber` (optional, iOS only) : The number to display as the app's icon badge. Setting the number to 0 removes the icon badge. * - `repeatInterval` : The interval to repeat as a string. Possible values: `minute`, `hour`, `day`, `week`, `month`, `year`. */ static scheduleLocalNotification(details: Object) { @@ -452,7 +453,7 @@ class PushNotificationIOS { * be throttled, to read more about it see the above documentation link. */ finish(fetchResult: FetchResult) { - if (!this._isRemote || !this._notificationId || this._remoteNotificationCompleteCallbackCalled) { + if (!this._isRemote || !this._notificationId || this._remoteNotificationCompleteCallbackCalled || Platform.OS === 'macos') { return; } this._remoteNotificationCompleteCallbackCalled = true; diff --git a/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj/project.pbxproj b/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj/project.pbxproj index c9318f7841c09d..090905759a07a2 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj/project.pbxproj +++ b/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 148699CF1ABD045300480536 /* RCTPushNotificationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 148699CE1ABD045300480536 /* RCTPushNotificationManager.m */; }; 3D0574931DE6009C00184BB4 /* RCTPushNotificationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 148699CE1ABD045300480536 /* RCTPushNotificationManager.m */; }; + 6424F7A61F669A3A0025D741 /* RCTPushNotificationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 148699CE1ABD045300480536 /* RCTPushNotificationManager.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -30,6 +31,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6424F7A81F669A3A0025D741 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -37,6 +47,7 @@ 148699CD1ABD045300480536 /* RCTPushNotificationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTPushNotificationManager.h; sourceTree = ""; }; 148699CE1ABD045300480536 /* RCTPushNotificationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPushNotificationManager.m; sourceTree = ""; }; 3D05745F1DE6004600184BB4 /* libRCTPushNotification-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTPushNotification-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6424F7AC1F669A3A0025D741 /* libRCTPushNotification-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTPushNotification-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,6 +65,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6424F7A71F669A3A0025D741 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -72,6 +90,7 @@ 148699CE1ABD045300480536 /* RCTPushNotificationManager.m */, 134814211AA4EA7D00B7C361 /* Products */, 3D05745F1DE6004600184BB4 /* libRCTPushNotification-tvOS.a */, + 6424F7AC1F669A3A0025D741 /* libRCTPushNotification-macOS.a */, ); indentWidth = 2; sourceTree = ""; @@ -115,6 +134,23 @@ productReference = 134814201AA4EA6300B7C361 /* libRCTPushNotification.a */; productType = "com.apple.product-type.library.static"; }; + 6424F7A41F669A3A0025D741 /* RCTPushNotification-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6424F7A91F669A3A0025D741 /* Build configuration list for PBXNativeTarget "RCTPushNotification-macOS" */; + buildPhases = ( + 6424F7A51F669A3A0025D741 /* Sources */, + 6424F7A71F669A3A0025D741 /* Frameworks */, + 6424F7A81F669A3A0025D741 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RCTPushNotification-macOS"; + productName = RCTDataManager; + productReference = 6424F7AC1F669A3A0025D741 /* libRCTPushNotification-macOS.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -147,6 +183,7 @@ targets = ( 58B511DA1A9E6C8500147676 /* RCTPushNotification */, 3D05745E1DE6004600184BB4 /* RCTPushNotification-tvOS */, + 6424F7A41F669A3A0025D741 /* RCTPushNotification-macOS */, ); }; /* End PBXProject section */ @@ -168,6 +205,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6424F7A51F669A3A0025D741 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6424F7A61F669A3A0025D741 /* RCTPushNotificationManager.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -243,6 +288,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -286,6 +332,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -319,6 +366,30 @@ }; name = Release; }; + 6424F7AA1F669A3A0025D741 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 6424F7AB1F669A3A0025D741 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -349,6 +420,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 6424F7A91F669A3A0025D741 /* Build configuration list for PBXNativeTarget "RCTPushNotification-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6424F7AA1F669A3A0025D741 /* Debug */, + 6424F7AB1F669A3A0025D741 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 58B511D31A9E6C8500147676 /* Project object */; diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h index 82605468f1867d..43c0ae5178e414 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h @@ -13,14 +13,23 @@ extern NSString *const RCTRemoteNotificationReceived; @interface RCTPushNotificationManager : RCTEventEmitter +#if !TARGET_OS_OSX typedef void (^RCTRemoteNotificationCallback)(UIBackgroundFetchResult result); +#endif #if !TARGET_OS_TV +#if !TARGET_OS_OSX + (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; +#endif + (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; + (void)didReceiveRemoteNotification:(NSDictionary *)notification; +#if !TARGET_OS_OSX + (void)didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(RCTRemoteNotificationCallback)completionHandler; + (void)didReceiveLocalNotification:(UILocalNotification *)notification; +#endif +#if TARGET_OS_OSX ++ (void)didReceiveUserNotification:(NSUserNotification *)notification; +#endif + (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; #endif diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m index aeb8af20224087..1f68f0ae029826 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m @@ -9,7 +9,9 @@ #import "RCTPushNotificationManager.h" +#if !TARGET_OS_OSX #import +#endif #import #import @@ -42,12 +44,15 @@ @implementation RCTConvert (NSCalendarUnit) @end +#if !TARGET_OS_OSX @interface RCTPushNotificationManager () @property (nonatomic, strong) NSMutableDictionary *remoteNotificationCallbacks; @end +#endif @implementation RCTConvert (UILocalNotification) +#if !TARGET_OS_OSX + (UILocalNotification *)UILocalNotification:(id)json { NSDictionary *details = [self NSDictionary:json]; @@ -68,12 +73,43 @@ + (UILocalNotification *)UILocalNotification:(id)json } return notification; } +#else ++ (NSUserNotification *)NSUserNotification:(id)json +{ + NSDictionary *details = [self NSDictionary:json]; + BOOL isSilent = [RCTConvert BOOL:details[@"isSilent"]]; + NSUserNotification *notification = [NSUserNotification new]; + notification.deliveryDate = [RCTConvert NSDate:details[@"fireDate"]] ?: [NSDate date]; + notification.informativeText = [RCTConvert NSString:details[@"alertBody"]]; + notification.actionButtonTitle = [RCTConvert NSString:details[@"alertAction"]]; + notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]]; + + NSCalendarUnit calendarUnit = [RCTConvert NSCalendarUnit:details[@"repeatInterval"]]; + if (calendarUnit > 0) { + NSDateComponents *dateComponents = [[NSDateComponents alloc] init]; + [dateComponents setValue:1 forComponent:calendarUnit]; + notification.deliveryRepeatInterval = dateComponents; + } + if (!isSilent) { + notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: NSUserNotificationDefaultSoundName; + } + + NSString *identifier = [RCTConvert NSString:details[@"identifier"]]; + if (identifier == nil) { + identifier = [[NSUUID UUID] UUIDString]; + } + notification.identifier = identifier; + return notification; +} +#endif +#if !TARGET_OS_OSX RCT_ENUM_CONVERTER(UIBackgroundFetchResult, (@{ @"UIBackgroundFetchResultNewData": @(UIBackgroundFetchResultNewData), @"UIBackgroundFetchResultNoData": @(UIBackgroundFetchResultNoData), @"UIBackgroundFetchResultFailed": @(UIBackgroundFetchResultFailed), }), UIBackgroundFetchResultNoData, integerValue) +#endif @end #endif //TARGET_OS_TV @@ -83,7 +119,7 @@ @implementation RCTPushNotificationManager RCTPromiseResolveBlock _requestPermissionsResolveBlock; } -#if !TARGET_OS_TV +#if !TARGET_OS_TV && !TARGET_OS_OSX static NSDictionary *RCTFormatLocalNotification(UILocalNotification *notification) { @@ -129,6 +165,28 @@ @implementation RCTPushNotificationManager #endif //TARGET_OS_TV +#if TARGET_OS_OSX + +static NSDictionary *RCTFormatUserNotification(NSUserNotification *notification) +{ + NSMutableDictionary *formattedUserNotification = [NSMutableDictionary dictionary]; + if (notification.deliveryDate) { + NSDateFormatter *formatter = [NSDateFormatter new]; + [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; + NSString *fireDateString = [formatter stringFromDate:notification.deliveryDate]; + formattedUserNotification[@"fireDate"] = fireDateString; + } + formattedUserNotification[@"alertAction"] = RCTNullIfNil(notification.actionButtonTitle); + formattedUserNotification[@"alertBody"] = RCTNullIfNil(notification.informativeText); + formattedUserNotification[@"soundName"] = RCTNullIfNil(notification.soundName); + formattedUserNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(notification.userInfo)); + formattedUserNotification[@"remote"] = @(notification.isRemote); + formattedUserNotification[@"identifier"] = notification.identifier; + return formattedUserNotification; +} + +#endif + RCT_EXPORT_MODULE() - (dispatch_queue_t)methodQueue @@ -174,6 +232,7 @@ - (void)stopObserving @"remoteNotificationRegistrationError"]; } +#if !TARGET_OS_OSX + (void)didRegisterUserNotificationSettings:(__unused UIUserNotificationSettings *)notificationSettings { if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) { @@ -183,9 +242,16 @@ + (void)didRegisterUserNotificationSettings:(__unused UIUserNotificationSettings userInfo:@{@"notificationSettings": notificationSettings}]; } } +#endif + (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { +#if TARGET_OS_OSX + [[NSNotificationCenter defaultCenter] postNotificationName:kRegisterUserNotificationSettings + object:self + userInfo:@{@"notificationSettings": @(RCTSharedApplication().enabledRemoteNotificationTypes)}]; +#endif + NSMutableString *hexString = [NSMutableString string]; NSUInteger deviceTokenLength = deviceToken.length; const unsigned char *bytes = deviceToken.bytes; @@ -212,6 +278,7 @@ + (void)didReceiveRemoteNotification:(NSDictionary *)notification userInfo:userInfo]; } +#if !TARGET_OS_OSX + (void)didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(RCTRemoteNotificationCallback)completionHandler { @@ -228,6 +295,19 @@ + (void)didReceiveLocalNotification:(UILocalNotification *)notification userInfo:RCTFormatLocalNotification(notification)]; } +#else + ++ (void)didReceiveUserNotification:(NSUserNotification *)notification +{ + NSString *notificationName = notification.isRemote ? RCTRemoteNotificationReceived : kLocalNotificationReceived; + NSDictionary *userInfo = notification.isRemote ? @{@"notification": notification.userInfo} : RCTFormatUserNotification(notification); + [[NSNotificationCenter defaultCenter] postNotificationName:notificationName + object:self + userInfo:userInfo]; +} + +#endif + - (void)handleLocalNotificationReceived:(NSNotification *)notification { [self sendEventWithName:@"localNotificationReceived" body:notification.userInfo]; @@ -236,10 +316,13 @@ - (void)handleLocalNotificationReceived:(NSNotification *)notification - (void)handleRemoteNotificationReceived:(NSNotification *)notification { NSMutableDictionary *remoteNotification = [NSMutableDictionary dictionaryWithDictionary:notification.userInfo[@"notification"]]; +#if !TARGET_OS_OSX RCTRemoteNotificationCallback completionHandler = notification.userInfo[@"completionHandler"]; +#endif NSString *notificationId = [[NSUUID UUID] UUIDString]; remoteNotification[@"notificationId"] = notificationId; remoteNotification[@"remote"] = @YES; +#if !TARGET_OS_OSX if (completionHandler) { if (!self.remoteNotificationCallbacks) { // Lazy initialization @@ -247,6 +330,7 @@ - (void)handleRemoteNotificationReceived:(NSNotification *)notification } self.remoteNotificationCallbacks[notificationId] = completionHandler; } +#endif [self sendEventWithName:@"remoteNotificationReceived" body:remoteNotification]; } @@ -273,12 +357,21 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification return; } +#if !TARGET_OS_OSX UIUserNotificationSettings *notificationSettings = notification.userInfo[@"notificationSettings"]; NSDictionary *notificationTypes = @{ @"alert": @((notificationSettings.types & UIUserNotificationTypeAlert) > 0), @"sound": @((notificationSettings.types & UIUserNotificationTypeSound) > 0), @"badge": @((notificationSettings.types & UIUserNotificationTypeBadge) > 0), }; +#else + NSRemoteNotificationType remoteNotificationType = [notification.userInfo[@"notificationSettings"] unsignedIntegerValue]; + NSDictionary *notificationTypes = @{ + @"alert": @((remoteNotificationType & NSRemoteNotificationTypeAlert) > 0), + @"sound": @((remoteNotificationType & NSRemoteNotificationTypeSound) > 0), + @"badge": @((remoteNotificationType & NSRemoteNotificationTypeBadge) > 0), + }; +#endif _requestPermissionsResolveBlock(notificationTypes); // Clean up listener added in requestPermissions @@ -286,6 +379,7 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification _requestPermissionsResolveBlock = nil; } +#if !TARGET_OS_OSX RCT_EXPORT_METHOD(onFinishRemoteNotification:(NSString *)notificationId fetchResult:(UIBackgroundFetchResult)result) { RCTRemoteNotificationCallback completionHandler = self.remoteNotificationCallbacks[notificationId]; if (!completionHandler) { @@ -295,13 +389,20 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification completionHandler(result); [self.remoteNotificationCallbacks removeObjectForKey:notificationId]; } +#endif /** * Update the application icon badge number on the home screen */ RCT_EXPORT_METHOD(setApplicationIconBadgeNumber:(NSInteger)number) { +#if !TARGET_OS_OSX RCTSharedApplication().applicationIconBadgeNumber = number; +#else + NSDockTile *tile = [NSApp dockTile]; + tile.showsApplicationBadge = number > 0; + tile.badgeLabel = number > 0 ? [NSString stringWithFormat:@"%ld", number] : nil; +#endif } /** @@ -309,17 +410,23 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification */ RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback) { +#if !TARGET_OS_OSX callback(@[@(RCTSharedApplication().applicationIconBadgeNumber)]); +#else + callback(@[@([NSApp dockTile].badgeLabel.integerValue)]); +#endif } RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { +#if !TARGET_OS_OSX if (RCTRunningInAppExtension()) { reject(kErrorUnableToRequestPermissions, nil, RCTErrorWithMessage(@"Requesting push notifications is currently unavailable in an app extension")); return; } +#endif if (_requestPermissionsResolveBlock != nil) { RCTLogError(@"Cannot call requestPermissions twice before the first has returned."); @@ -330,6 +437,7 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification [self addListener:@"remoteNotificationsRegistered"]; _requestPermissionsResolveBlock = resolve; +#if !TARGET_OS_OSX UIUserNotificationType types = UIUserNotificationTypeNone; if (permissions) { if ([RCTConvert BOOL:permissions[@"alert"]]) { @@ -348,6 +456,23 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; [RCTSharedApplication() registerUserNotificationSettings:notificationSettings]; +#else + NSRemoteNotificationType types = NSRemoteNotificationTypeNone; + if (permissions) { + if ([RCTConvert BOOL:permissions[@"alert"]]) { + types |= NSRemoteNotificationTypeAlert; + } + if ([RCTConvert BOOL:permissions[@"badge"]]) { + types |= NSRemoteNotificationTypeBadge; + } + if ([RCTConvert BOOL:permissions[@"sound"]]) { + types |= NSRemoteNotificationTypeSound; + } + } else { + types = NSRemoteNotificationTypeAlert | NSRemoteNotificationTypeBadge | NSRemoteNotificationTypeSound; + } + [RCTSharedApplication() registerForRemoteNotificationTypes:types]; +#endif } RCT_EXPORT_METHOD(abandonPermissions) @@ -357,37 +482,72 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback) { +#if !TARGET_OS_OSX if (RCTRunningInAppExtension()) { callback(@[@{@"alert": @NO, @"badge": @NO, @"sound": @NO}]); return; } +#endif +#if !TARGET_OS_OSX NSUInteger types = [RCTSharedApplication() currentUserNotificationSettings].types; callback(@[@{ @"alert": @((types & UIUserNotificationTypeAlert) > 0), @"badge": @((types & UIUserNotificationTypeBadge) > 0), @"sound": @((types & UIUserNotificationTypeSound) > 0), }]); +#else + NSRemoteNotificationType types = RCTSharedApplication().enabledRemoteNotificationTypes; + callback(@[@{ + @"alert": @((types & NSRemoteNotificationTypeAlert) > 0), + @"badge": @((types & NSRemoteNotificationTypeBadge) > 0), + @"sound": @((types & NSRemoteNotificationTypeSound) > 0), + }]); +#endif } +#if !TARGET_OS_OSX RCT_EXPORT_METHOD(presentLocalNotification:(UILocalNotification *)notification) { [RCTSharedApplication() presentLocalNotificationNow:notification]; } +#else +RCT_EXPORT_METHOD(presentLocalNotification:(NSUserNotification *)notification) +{ + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; +} +#endif +#if !TARGET_OS_OSX RCT_EXPORT_METHOD(scheduleLocalNotification:(UILocalNotification *)notification) { [RCTSharedApplication() scheduleLocalNotification:notification]; } +#else +RCT_EXPORT_METHOD(scheduleLocalNotification:(NSUserNotification *)notification) +{ + [[NSUserNotificationCenter defaultUserNotificationCenter] scheduleNotification:notification]; +} +#endif RCT_EXPORT_METHOD(cancelAllLocalNotifications) { +#if !TARGET_OS_OSX [RCTSharedApplication() cancelAllLocalNotifications]; +#else + for (NSUserNotification *notif in [NSUserNotificationCenter defaultUserNotificationCenter].scheduledNotifications) { + [[NSUserNotificationCenter defaultUserNotificationCenter] removeScheduledNotification:notif]; + } +#endif } RCT_EXPORT_METHOD(cancelLocalNotifications:(NSDictionary *)userInfo) { +#if !TARGET_OS_OSX for (UILocalNotification *notification in RCTSharedApplication().scheduledLocalNotifications) { +#else + for (NSUserNotification *notification in [NSUserNotificationCenter defaultUserNotificationCenter].scheduledNotifications) { +#endif __block BOOL matchesAll = YES; NSDictionary *notificationInfo = notification.userInfo; // Note: we do this with a loop instead of just `isEqualToDictionary:` @@ -400,15 +560,22 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification *stop = YES; } }]; +#if !TARGET_OS_OSX if (matchesAll) { [RCTSharedApplication() cancelLocalNotification:notification]; } +#else + if ([notification.identifier isEqualToString:userInfo[@"identifier"]] || matchesAll) { + [[NSUserNotificationCenter defaultUserNotificationCenter] removeScheduledNotification:notification]; + } +#endif } } RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(__unused RCTPromiseRejectBlock)reject) { +#if !TARGET_OS_OSX NSMutableDictionary *initialNotification = [self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] mutableCopy]; @@ -423,36 +590,63 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification } else { resolve((id)kCFNull); } +#else + NSUserNotification *initialNotification = self.bridge.launchOptions[NSApplicationLaunchUserNotificationKey]; + if (initialNotification) { + resolve(RCTFormatUserNotification(initialNotification)); + } else { + resolve((id)kCFNull); + } +#endif } RCT_EXPORT_METHOD(getScheduledLocalNotifications:(RCTResponseSenderBlock)callback) { - NSArray *scheduledLocalNotifications = RCTSharedApplication().scheduledLocalNotifications; NSMutableArray *formattedScheduledLocalNotifications = [NSMutableArray new]; - for (UILocalNotification *notification in scheduledLocalNotifications) { +#if !TARGET_OS_OSX + for (UILocalNotification *notification in RCTSharedApplication().scheduledLocalNotifications) { [formattedScheduledLocalNotifications addObject:RCTFormatLocalNotification(notification)]; } +#else + for (NSUserNotification *notification in [NSUserNotificationCenter defaultUserNotificationCenter].scheduledNotifications) { + [formattedScheduledLocalNotifications addObject:RCTFormatUserNotification(notification)]; + } +#endif callback(@[formattedScheduledLocalNotifications]); } RCT_EXPORT_METHOD(removeAllDeliveredNotifications) { +#if !TARGET_OS_OSX if ([UNUserNotificationCenter class]) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center removeAllDeliveredNotifications]; } +#else + [[NSUserNotificationCenter defaultUserNotificationCenter] removeAllDeliveredNotifications]; +#endif } RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray *)identifiers) { +#if !TARGET_OS_OSX if ([UNUserNotificationCenter class]) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center removeDeliveredNotificationsWithIdentifiers:identifiers]; } +#else + NSArray *notificationsToRemove = [[NSUserNotificationCenter defaultUserNotificationCenter].deliveredNotifications filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSUserNotification* evaluatedObject, NSDictionary * _Nullable bindings) { + return [identifiers containsObject:evaluatedObject.identifier]; + }]]; + for (NSUserNotification *notification in notificationsToRemove) { + [[NSUserNotificationCenter defaultUserNotificationCenter] removeDeliveredNotification:notification]; + } +#endif } RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback) { +#if !TARGET_OS_OSX if ([UNUserNotificationCenter class]) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center getDeliveredNotificationsWithCompletionHandler:^(NSArray *_Nonnull notifications) { @@ -464,6 +658,13 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification callback(@[formattedNotifications]); }]; } +#else + NSMutableArray *formattedNotifications = [NSMutableArray new]; + for (NSUserNotification *notification in [NSUserNotificationCenter defaultUserNotificationCenter].deliveredNotifications) { + [formattedNotifications addObject:RCTFormatUserNotification(notification)]; + } + callback(@[formattedNotifications]); +#endif } #else //TARGET_OS_TV diff --git a/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.h b/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.h index 6ae45d2313d629..45ef4174b1eaee 100644 --- a/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.h +++ b/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.h @@ -9,7 +9,7 @@ */ #import -#import +#import typedef NS_ENUM(NSInteger, FBSnapshotTestControllerErrorCode) { FBSnapshotTestControllerErrorCodeUnknown, diff --git a/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.m b/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.m index 73af891598867d..626018ab756660 100644 --- a/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.m +++ b/Libraries/RCTTest/FBSnapshotTestCase/FBSnapshotTestController.m @@ -12,7 +12,7 @@ #import -#import +#import #import "UIImage+Compare.h" #import "UIImage+Diff.h" @@ -69,7 +69,7 @@ - (UIImage *)referenceImageForSelector:(SEL)selector error:(NSError **)errorPtr { NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier]; - UIImage *image = [UIImage imageWithContentsOfFile:filePath]; + UIImage *image = UIImageWithContentsOfFile(filePath); if (nil == image && NULL != errorPtr) { BOOL exists = [_fileManager fileExistsAtPath:filePath]; if (!exists) { @@ -240,11 +240,19 @@ - (NSString *)_fileNameForSelector:(SEL)selector if (0 < identifier.length) { fileName = [fileName stringByAppendingFormat:@"_%@", identifier]; } - if ([[UIScreen mainScreen] scale] > 1.0) { - fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]]; + CGFloat scale; +#if !TARGET_OS_OSX + scale = [[UIScreen mainScreen] scale]; +#else + scale = [[NSScreen mainScreen] backingScaleFactor]; +#endif + if (scale > 1.0) { + fileName = [fileName stringByAppendingFormat:@"@%.fx", scale]; } #if TARGET_OS_TV fileName = [fileName stringByAppendingString:@"_tvOS"]; +#elif TARGET_OS_OSX + fileName = [fileName stringByAppendingString:@"_macOS"]; #endif fileName = [fileName stringByAppendingPathExtension:@"png"]; return fileName; @@ -322,13 +330,18 @@ - (BOOL)_recordSnapshotOfView:(UIView *)view - (UIImage *)_snapshotView:(UIView *)view { +#if !TARGET_OS_OSX [view layoutIfNeeded]; - +#else + [view layoutSubtreeIfNeeded]; +#endif + CGRect bounds = view.bounds; NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view); NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view); +#if !TARGET_OS_OSX UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0); CGContextRef context = UIGraphicsGetCurrentContext(); NSAssert1(context, @"Could not generate context for view %@", view); @@ -344,7 +357,15 @@ - (UIImage *)_snapshotView:(UIView *)view UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); - +#else // TARGET_OS_OSX + // The macOS snapshot bitmap will *not* be scaled to the machine's current screen. + // The snapshot image is used for integration testing so the consistent scale makes the test results machine independent. + NSBitmapImageRep *rep = [view bitmapImageRepForCachingDisplayInRect:bounds]; + [view cacheDisplayInRect:bounds toBitmapImageRep:rep]; + UIImage *snapshot = [[NSImage alloc] initWithSize:bounds.size]; + [snapshot addRepresentation:rep]; +#endif + return snapshot; } diff --git a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.h b/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.h index 11c6fa6385e2d5..0ef101753d6bb5 100644 --- a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.h +++ b/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.h @@ -28,7 +28,7 @@ // OTHER DEALINGS IN THE SOFTWARE. // -#import +#import @interface UIImage (Compare) diff --git a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.m b/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.m index 24635509d82dd6..a804c4f04b1923 100644 --- a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.m +++ b/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Compare.m @@ -36,9 +36,11 @@ - (BOOL)compareWithImage:(UIImage *)image { NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size."); + CGImageRef imageRef = UIImageGetCGImageRef(image); + // The images have the equal size, so we could use the smallest amount of bytes because of byte padding - size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage)); - size_t referenceImageSizeBytes = CGImageGetHeight(self.CGImage) * minBytesPerRow; + size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(imageRef), CGImageGetBytesPerRow(imageRef)); + size_t referenceImageSizeBytes = CGImageGetHeight(imageRef) * minBytesPerRow; void *referenceImagePixels = calloc(1, referenceImageSizeBytes); void *imagePixels = calloc(1, referenceImageSizeBytes); @@ -49,23 +51,31 @@ - (BOOL)compareWithImage:(UIImage *)image } CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels, - CGImageGetWidth(self.CGImage), - CGImageGetHeight(self.CGImage), - CGImageGetBitsPerComponent(self.CGImage), + CGImageGetWidth(imageRef), + CGImageGetHeight(imageRef), + CGImageGetBitsPerComponent(imageRef), minBytesPerRow, - CGImageGetColorSpace(self.CGImage), + CGImageGetColorSpace(imageRef), (CGBitmapInfo)kCGImageAlphaPremultipliedLast ); CGContextRef imageContext = CGBitmapContextCreate(imagePixels, - CGImageGetWidth(image.CGImage), - CGImageGetHeight(image.CGImage), - CGImageGetBitsPerComponent(image.CGImage), + CGImageGetWidth(imageRef), + CGImageGetHeight(imageRef), + CGImageGetBitsPerComponent(imageRef), minBytesPerRow, - CGImageGetColorSpace(image.CGImage), + CGImageGetColorSpace(imageRef), (CGBitmapInfo)kCGImageAlphaPremultipliedLast ); +#if !TARGET_OS_OSX CGFloat scaleFactor = [UIScreen mainScreen].scale; +#else + // The compareWithImage: method is used for integration test snapshot image comparison. + // The _snapshotView: method that creates snapshot images that are *not* scaled for the screen. + // By not using the screen scale factor in this method the test results are machine independent. + CGFloat scaleFactor = 1; +#endif + CGContextScaleCTM(referenceImageContext, scaleFactor, scaleFactor); CGContextScaleCTM(imageContext, scaleFactor, scaleFactor); @@ -77,8 +87,8 @@ - (BOOL)compareWithImage:(UIImage *)image return NO; } - CGContextDrawImage(referenceImageContext, CGRectMake(0.0f, 0.0f, self.size.width, self.size.height), self.CGImage); - CGContextDrawImage(imageContext, CGRectMake(0.0f, 0.0f, image.size.width, image.size.height), image.CGImage); + CGContextDrawImage(referenceImageContext, CGRectMake(0.0f, 0.0f, self.size.width, self.size.height), imageRef); + CGContextDrawImage(imageContext, CGRectMake(0.0f, 0.0f, image.size.width, image.size.height), imageRef); CGContextRelease(referenceImageContext); CGContextRelease(imageContext); diff --git a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.h b/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.h index 35595843f35f22..0bd413c9e6d2ad 100644 --- a/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.h +++ b/Libraries/RCTTest/FBSnapshotTestCase/UIImage+Diff.h @@ -28,7 +28,7 @@ // OTHER DEALINGS IN THE SOFTWARE. // -#import +#import @interface UIImage (Diff) diff --git a/Libraries/RCTTest/RCTTest.xcodeproj/project.pbxproj b/Libraries/RCTTest/RCTTest.xcodeproj/project.pbxproj index 3fa7ee8b6805cf..c47963fc309ec1 100644 --- a/Libraries/RCTTest/RCTTest.xcodeproj/project.pbxproj +++ b/Libraries/RCTTest/RCTTest.xcodeproj/project.pbxproj @@ -7,6 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + 183938D21F9A563A00930D92 /* RCTTestRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 585135361AB3C56F00882537 /* RCTTestRunner.m */; }; + 183938D61F9A58A500930D92 /* FBSnapshotTestController.m in Sources */ = {isa = PBXBuildFile; fileRef = 58E64FE71AB964CD007446E2 /* FBSnapshotTestController.m */; }; + 1851280120AE30C600270B2D /* UIImage+Compare.m in Sources */ = {isa = PBXBuildFile; fileRef = 58E64FE91AB964CD007446E2 /* UIImage+Compare.m */; }; + 18DF57141EFA052000BF4666 /* RCTTestRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = 585135351AB3C56F00882537 /* RCTTestRunner.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 18DF57161EFA052000BF4666 /* RCTTestRunner.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 585135351AB3C56F00882537 /* RCTTestRunner.h */; }; + 18DF57191EFA052000BF4666 /* RCTSnapshotManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9913A84A1BBE833400D70E66 /* RCTSnapshotManager.m */; }; + 18DF571A1EFA052000BF4666 /* RCTTestModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 585135341AB3C56F00882537 /* RCTTestModule.m */; }; 2D3B5F2D1D9B0F2800451313 /* RCTSnapshotManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9913A84A1BBE833400D70E66 /* RCTSnapshotManager.m */; }; 2D3B5F2E1D9B0F2B00451313 /* RCTTestModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 585135341AB3C56F00882537 /* RCTTestModule.m */; }; 2D3B5F2F1D9B0F2E00451313 /* RCTTestRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 585135361AB3C56F00882537 /* RCTTestRunner.m */; }; @@ -26,6 +33,17 @@ /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ + 18DF57151EFA052000BF4666 /* Copy Headers */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/RCTTest; + dstSubfolderSpec = 16; + files = ( + 18DF57161EFA052000BF4666 /* RCTTestRunner.h in Copy Headers */, + ); + name = "Copy Headers"; + runOnlyForDeploymentPostprocessing = 0; + }; 3D3030271DF8299E00D6DDAE /* Copy Headers */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -51,6 +69,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 18DF57221EFA052000BF4666 /* libRCTTest-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTTest-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 2D2A286E1D9B047700D4039D /* libRCTTest-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTTest-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 580C376F1AB104AF0015E709 /* libRCTTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTTest.a; sourceTree = BUILT_PRODUCTS_DIR; }; 585135331AB3C56F00882537 /* RCTTestModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTestModule.h; sourceTree = ""; }; @@ -70,6 +89,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 18DF571E1EFA052000BF4666 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2D2A286B1D9B047700D4039D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -109,6 +135,7 @@ children = ( 580C376F1AB104AF0015E709 /* libRCTTest.a */, 2D2A286E1D9B047700D4039D /* libRCTTest-tvOS.a */, + 18DF57221EFA052000BF4666 /* libRCTTest-macOS.a */, ); name = Products; sourceTree = ""; @@ -131,6 +158,14 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 18DF57131EFA052000BF4666 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 18DF57141EFA052000BF4666 /* RCTTestRunner.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 3DED3AA11DE6FC2200336DD7 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -150,6 +185,24 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 18DF57121EFA052000BF4666 /* RCTTest-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 18DF571F1EFA052000BF4666 /* Build configuration list for PBXNativeTarget "RCTTest-macOS" */; + buildPhases = ( + 18DF57131EFA052000BF4666 /* Headers */, + 18DF57151EFA052000BF4666 /* Copy Headers */, + 18DF57171EFA052000BF4666 /* Sources */, + 18DF571E1EFA052000BF4666 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RCTTest-macOS"; + productName = RCTTest; + productReference = 18DF57221EFA052000BF4666 /* libRCTTest-macOS.a */; + productType = "com.apple.product-type.library.static"; + }; 2D2A286D1D9B047700D4039D /* RCTTest-tvOS */ = { isa = PBXNativeTarget; buildConfigurationList = 2D2A28761D9B047700D4039D /* Build configuration list for PBXNativeTarget "RCTTest-tvOS" */; @@ -218,11 +271,24 @@ targets = ( 580C376E1AB104AF0015E709 /* RCTTest */, 2D2A286D1D9B047700D4039D /* RCTTest-tvOS */, + 18DF57121EFA052000BF4666 /* RCTTest-macOS */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ + 18DF57171EFA052000BF4666 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1851280120AE30C600270B2D /* UIImage+Compare.m in Sources */, + 18DF57191EFA052000BF4666 /* RCTSnapshotManager.m in Sources */, + 183938D61F9A58A500930D92 /* FBSnapshotTestController.m in Sources */, + 183938D21F9A563A00930D92 /* RCTTestRunner.m in Sources */, + 18DF571A1EFA052000BF4666 /* RCTTestModule.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2D2A286A1D9B047700D4039D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -252,6 +318,37 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ + 18DF57201EFA052000BF4666 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + OTHER_LDFLAGS = ( + "-ObjC", + "-framework", + XCTest, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/RCTTest; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 18DF57211EFA052000BF4666 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + OTHER_LDFLAGS = ( + "-ObjC", + "-framework", + XCTest, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/RCTTest; + SDKROOT = macosx; + }; + name = Release; + }; 2D2A28741D9B047700D4039D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -325,6 +422,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -367,6 +465,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -410,6 +509,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 18DF571F1EFA052000BF4666 /* Build configuration list for PBXNativeTarget "RCTTest-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 18DF57201EFA052000BF4666 /* Debug */, + 18DF57211EFA052000BF4666 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 2D2A28761D9B047700D4039D /* Build configuration list for PBXNativeTarget "RCTTest-tvOS" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Libraries/RCTTest/RCTTestModule.h b/Libraries/RCTTest/RCTTestModule.h index 7bf93034e24d7d..982ddb7dae413d 100644 --- a/Libraries/RCTTest/RCTTestModule.h +++ b/Libraries/RCTTest/RCTTestModule.h @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import #import #import diff --git a/Libraries/RCTTest/RCTTestRunner.m b/Libraries/RCTTest/RCTTestRunner.m index cb79087c321da7..4e41aa32ccedcc 100644 --- a/Libraries/RCTTest/RCTTestRunner.m +++ b/Libraries/RCTTest/RCTTestRunner.m @@ -15,6 +15,7 @@ #import #import #import +#import #import "FBSnapshotTestController.h" #import "RCTTestModule.h" @@ -56,7 +57,7 @@ - (instancetype)initWithApp:(NSString *)app - (void)updateScript { if (getenv("CI_USE_PACKAGER") || _useBundler) { - _scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", _appPath]]; + _scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=%@&dev=true", _appPath, kRCTPlatformName]]; } else { _scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"]; } @@ -140,9 +141,11 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName testModule.testSuffix = _testSuffix; testModule.view = rootView; +#if !TARGET_OS_OSX UIViewController *vc = RCTSharedApplication().delegate.window.rootViewController; vc.view = [UIView new]; [vc.view addSubview:rootView]; // Add as subview so it doesn't get resized +#endif if (configurationBlock) { configurationBlock(rootView); @@ -158,7 +161,7 @@ - (void)runTest:(SEL)test module:(NSString *)moduleName RCTSetLogFunction(defaultLogFunction); -#if RCT_DEV +#if RCT_DEV && !TARGET_OS_OSX NSArray *nonLayoutSubviews = [vc.view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id subview, NSDictionary *bindings) { return ![NSStringFromClass([subview class]) isEqualToString:@"_UILayoutGuide"]; }]]; diff --git a/Libraries/RCTTest/RNTester-macOS/AppDelegate.h b/Libraries/RCTTest/RNTester-macOS/AppDelegate.h new file mode 100644 index 00000000000000..450e41a73ad352 --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOS/AppDelegate.h @@ -0,0 +1,15 @@ +// +// AppDelegate.h +// RNTester-macOS +// +// Created by Tom Underhill on 6/16/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +@interface AppDelegate : NSObject + + +@end + diff --git a/Libraries/RCTTest/RNTester-macOS/AppDelegate.m b/Libraries/RCTTest/RNTester-macOS/AppDelegate.m new file mode 100644 index 00000000000000..81f2ea63ef02ad --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOS/AppDelegate.m @@ -0,0 +1,27 @@ +// +// AppDelegate.m +// RNTester-macOS +// +// Created by Tom Underhill on 6/16/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Insert code here to initialize your application +} + + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + // Insert code here to tear down your application +} + + +@end diff --git a/Libraries/RCTTest/RNTester-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/Libraries/RCTTest/RNTester-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000000..2db2b1c7c6c316 --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Libraries/RCTTest/RNTester-macOS/Base.lproj/Main.storyboard b/Libraries/RCTTest/RNTester-macOS/Base.lproj/Main.storyboard new file mode 100644 index 00000000000000..8d4ef98f878b82 --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOS/Base.lproj/Main.storyboard @@ -0,0 +1,693 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Libraries/RCTTest/RNTester-macOS/Info.plist b/Libraries/RCTTest/RNTester-macOS/Info.plist new file mode 100644 index 00000000000000..bef0d7670abd99 --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOS/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2017 Facebook. All rights reserved. + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + + diff --git a/Libraries/RCTTest/RNTester-macOS/ViewController.h b/Libraries/RCTTest/RNTester-macOS/ViewController.h new file mode 100644 index 00000000000000..1d10e79294c203 --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOS/ViewController.h @@ -0,0 +1,15 @@ +// +// ViewController.h +// RNTester-macOS +// +// Created by Tom Underhill on 6/16/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +@interface ViewController : NSViewController + + +@end + diff --git a/Libraries/RCTTest/RNTester-macOS/ViewController.m b/Libraries/RCTTest/RNTester-macOS/ViewController.m new file mode 100644 index 00000000000000..046a08e1ce18f3 --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOS/ViewController.m @@ -0,0 +1,27 @@ +// +// ViewController.m +// RNTester-macOS +// +// Created by Tom Underhill on 6/16/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import "ViewController.h" + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Do any additional setup after loading the view. +} + + +- (void)setRepresentedObject:(id)representedObject { + [super setRepresentedObject:representedObject]; + + // Update the view, if already loaded. +} + + +@end diff --git a/Libraries/RCTTest/RNTester-macOS/main.m b/Libraries/RCTTest/RNTester-macOS/main.m new file mode 100644 index 00000000000000..62bfe06a4a5fe7 --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOS/main.m @@ -0,0 +1,13 @@ +// +// main.m +// RNTester-macOS +// +// Created by Tom Underhill on 6/16/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +int main(int argc, const char * argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/Libraries/RCTTest/RNTester-macOSTests/Info.plist b/Libraries/RCTTest/RNTester-macOSTests/Info.plist new file mode 100644 index 00000000000000..6c6c23c43adc88 --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOSTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Libraries/RCTTest/RNTester-macOSTests/RNTester_macOSTests.m b/Libraries/RCTTest/RNTester-macOSTests/RNTester_macOSTests.m new file mode 100644 index 00000000000000..18c1f7d13b16c0 --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOSTests/RNTester_macOSTests.m @@ -0,0 +1,39 @@ +// +// RNTester_macOSTests.m +// RNTester-macOSTests +// +// Created by Tom Underhill on 6/16/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +@interface RNTester_macOSTests : XCTestCase + +@end + +@implementation RNTester_macOSTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/Libraries/RCTTest/RNTester-macOSUITests/Info.plist b/Libraries/RCTTest/RNTester-macOSUITests/Info.plist new file mode 100644 index 00000000000000..6c6c23c43adc88 --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOSUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Libraries/RCTTest/RNTester-macOSUITests/RNTester_macOSUITests.m b/Libraries/RCTTest/RNTester-macOSUITests/RNTester_macOSUITests.m new file mode 100644 index 00000000000000..3bf5fe540e61eb --- /dev/null +++ b/Libraries/RCTTest/RNTester-macOSUITests/RNTester_macOSUITests.m @@ -0,0 +1,40 @@ +// +// RNTester_macOSUITests.m +// RNTester-macOSUITests +// +// Created by Tom Underhill on 6/16/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +@interface RNTester_macOSUITests : XCTestCase + +@end + +@implementation RNTester_macOSUITests + +- (void)setUp { + [super setUp]; + + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + self.continueAfterFailure = NO; + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + [[[XCUIApplication alloc] init] launch]; + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +@end diff --git a/Libraries/ReactNative/UIManager.js b/Libraries/ReactNative/UIManager.js index ed56fb626862b5..acfe00f63a2330 100644 --- a/Libraries/ReactNative/UIManager.js +++ b/Libraries/ReactNative/UIManager.js @@ -44,7 +44,7 @@ UIManager.takeSnapshot = function() { * only needed for iOS, which puts the constants in the ViewManager * namespace instead of UIManager, unlike Android. */ -if (Platform.OS === 'ios') { +if (Platform.OS === 'ios' || Platform.OS === 'macos') { Object.keys(UIManager).forEach(viewName => { const viewConfig = UIManager[viewName]; if (viewConfig.Manager) { diff --git a/Libraries/Settings/RCTSettings.xcodeproj/project.pbxproj b/Libraries/Settings/RCTSettings.xcodeproj/project.pbxproj index 5901229e3362a1..7f75f9b87127bc 100644 --- a/Libraries/Settings/RCTSettings.xcodeproj/project.pbxproj +++ b/Libraries/Settings/RCTSettings.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 13DBA45E1AEE749000A17CF8 /* RCTSettingsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DBA45D1AEE749000A17CF8 /* RCTSettingsManager.m */; }; 2D3B5F2C1D9B0ECA00451313 /* RCTSettingsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DBA45D1AEE749000A17CF8 /* RCTSettingsManager.m */; }; + 6448A5C91F292E63006FF1F5 /* RCTSettingsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DBA45D1AEE749000A17CF8 /* RCTSettingsManager.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -16,6 +17,7 @@ 13DBA45C1AEE749000A17CF8 /* RCTSettingsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSettingsManager.h; sourceTree = ""; }; 13DBA45D1AEE749000A17CF8 /* RCTSettingsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSettingsManager.m; sourceTree = ""; }; 2D2A28611D9B046600D4039D /* libRCTSettings-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTSettings-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6448A5CD1F292E63006FF1F5 /* libRCTSettings-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTSettings-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ @@ -34,6 +36,7 @@ 13DBA45D1AEE749000A17CF8 /* RCTSettingsManager.m */, 134814211AA4EA7D00B7C361 /* Products */, 2D2A28611D9B046600D4039D /* libRCTSettings-tvOS.a */, + 6448A5CD1F292E63006FF1F5 /* libRCTSettings-macOS.a */, ); indentWidth = 2; sourceTree = ""; @@ -73,6 +76,21 @@ productReference = 134814201AA4EA6300B7C361 /* libRCTSettings.a */; productType = "com.apple.product-type.library.static"; }; + 6448A5C71F292E63006FF1F5 /* RCTSettings-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6448A5CA1F292E63006FF1F5 /* Build configuration list for PBXNativeTarget "RCTSettings-macOS" */; + buildPhases = ( + 6448A5C81F292E63006FF1F5 /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RCTSettings-macOS"; + productName = RCTDataManager; + productReference = 6448A5CD1F292E63006FF1F5 /* libRCTSettings-macOS.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -105,6 +123,7 @@ targets = ( 58B511DA1A9E6C8500147676 /* RCTSettings */, 2D2A28601D9B046600D4039D /* RCTSettings-tvOS */, + 6448A5C71F292E63006FF1F5 /* RCTSettings-macOS */, ); }; /* End PBXProject section */ @@ -126,6 +145,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6448A5C81F292E63006FF1F5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6448A5C91F292E63006FF1F5 /* RCTSettingsManager.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -200,6 +227,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -242,6 +270,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -271,6 +300,26 @@ }; name = Release; }; + 6448A5CB1F292E63006FF1F5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + 6448A5CC1F292E63006FF1F5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -301,6 +350,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 6448A5CA1F292E63006FF1F5 /* Build configuration list for PBXNativeTarget "RCTSettings-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6448A5CB1F292E63006FF1F5 /* Debug */, + 6448A5CC1F292E63006FF1F5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 58B511D31A9E6C8500147676 /* Project object */; diff --git a/Libraries/Settings/RCTSettingsManager.m b/Libraries/Settings/RCTSettingsManager.m index d41dc3a9d0e934..496c20ccc0e0d4 100644 --- a/Libraries/Settings/RCTSettingsManager.m +++ b/Libraries/Settings/RCTSettingsManager.m @@ -18,6 +18,10 @@ @implementation RCTSettingsManager { BOOL _ignoringUpdates; NSUserDefaults *_defaults; + +#if TARGET_OS_OSX + BOOL _isListeningForUpdates; +#endif } @synthesize bridge = _bridge; @@ -39,11 +43,12 @@ - (instancetype)initWithUserDefaults:(NSUserDefaults *)defaults if ((self = [super init])) { _defaults = defaults; - +#if !TARGET_OS_OSX [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:NSUserDefaultsDidChangeNotification object:_defaults]; +#endif } return self; } @@ -106,4 +111,29 @@ - (void)userDefaultsDidChange:(NSNotification *)note _ignoringUpdates = NO; } +#if TARGET_OS_OSX +/** + * Enable or disable monitoring of changes to NSUserDefaults + */ +RCT_EXPORT_METHOD(setIsMonitoringEnabled:(BOOL)isEnabled) +{ + if (isEnabled) { + if (!_isListeningForUpdates) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(userDefaultsDidChange:) + name:NSUserDefaultsDidChangeNotification + object:_defaults]; + _isListeningForUpdates = YES; + } + } + else + { + if (_isListeningForUpdates) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + _isListeningForUpdates = NO; + } + } +} +#endif + @end diff --git a/Libraries/Settings/Settings.macos.js b/Libraries/Settings/Settings.macos.js new file mode 100644 index 00000000000000..d4c3375fe267e8 --- /dev/null +++ b/Libraries/Settings/Settings.macos.js @@ -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. + * + * @providesModule Settings + * @flow + */ +'use strict'; + +var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); +var RCTSettingsManager = require('NativeModules').SettingsManager; + +var invariant = require('fbjs/lib/invariant'); + +var subscriptions: Array<{keys: Array, callback: ?Function}> = []; + +var Settings = { + _settings: RCTSettingsManager && RCTSettingsManager.settings, + + get(key: string): mixed { + return this._settings[key]; + }, + + set(settings: Object) { + this._settings = Object.assign(this._settings, settings); + RCTSettingsManager.setValues(settings); + }, + + watchKeys(keys: string | Array, callback: Function): number { + if (typeof keys === 'string') { + keys = [keys]; + } + + invariant( + Array.isArray(keys), + 'keys should be a string or array of strings' + ); + + //start monitoring for changes to NSUserDefaults + RCTSettingsManager.setIsMonitoringEnabled(true); + + var sid = subscriptions.length; + subscriptions.push({keys: keys, callback: callback}); + return sid; + }, + + clearWatch(watchId: number) { + if (watchId < subscriptions.length) { + subscriptions[watchId] = {keys: [], callback: null}; + + if (!subscriptions.some(subscription => subscription.callback != null)) { + // no subscribers present, stop listening for changes. + RCTSettingsManager.setIsMonitoringEnabled(false); + } + } + }, + + _sendObservations(body: Object) { + Object.keys(body).forEach((key) => { + var newValue = body[key]; + var didChange = this._settings[key] !== newValue; + this._settings[key] = newValue; + + if (didChange) { + subscriptions.forEach((sub) => { + if (sub.keys.indexOf(key) !== -1 && sub.callback) { + sub.callback(); + } + }); + } + }); + }, +}; + +RCTDeviceEventEmitter.addListener( + 'settingsUpdated', + Settings._sendObservations.bind(Settings) +); + +module.exports = Settings; diff --git a/Libraries/StyleSheet/StyleSheet.js b/Libraries/StyleSheet/StyleSheet.js index 8fd9b1f6cd5141..f4859e10f88bd7 100644 --- a/Libraries/StyleSheet/StyleSheet.js +++ b/Libraries/StyleSheet/StyleSheet.js @@ -15,6 +15,7 @@ const PixelRatio = require('PixelRatio'); const ReactNativePropRegistry = require('ReactNativePropRegistry'); const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes'); const StyleSheetValidation = require('StyleSheetValidation'); +const Platform = require('Platform'); const flatten = require('flattenStyle'); @@ -23,7 +24,7 @@ export type StyleSheet = {[key: $Keys]: number}; export type StyleValue = {[key: string]: Object} | number | false | null; export type StyleProp = StyleValue | Array; -let hairlineWidth = PixelRatio.roundToNearestPixel(0.4); +let hairlineWidth = (Platform.OS === 'win32' || Platform.OS === 'windesktop') ? 0.5 : PixelRatio.roundToNearestPixel(0.4); if (hairlineWidth === 0) { hairlineWidth = 1 / PixelRatio.get(); } diff --git a/Libraries/StyleSheet/processTransform.js b/Libraries/StyleSheet/processTransform.js index eed0f16caa2aa7..82cac6b67a2072 100644 --- a/Libraries/StyleSheet/processTransform.js +++ b/Libraries/StyleSheet/processTransform.js @@ -33,7 +33,7 @@ function processTransform(transform: Array): Array | Array +#import @protocol RCTBackedTextInputViewProtocol; @@ -27,4 +27,9 @@ - (void)textInputDidChangeSelection; +- (BOOL)textInputShouldHandleDeleteBackward:(id)sender; // Return `YES` to have the deleteBackward event handled normally. Return `NO` to disallow it and handle it yourself. +#if TARGET_OS_OSX +- (BOOL)textInputShouldHandleDeleteForward:(id)sender; // Return `YES` to have the deleteForward event handled normally. Return `NO` to disallow it and handle it yourself. +#endif + @end diff --git a/Libraries/Text/RCTBackedTextInputDelegateAdapter.h b/Libraries/Text/RCTBackedTextInputDelegateAdapter.h index dce127bbbad514..92d3ada8639113 100644 --- a/Libraries/Text/RCTBackedTextInputDelegateAdapter.h +++ b/Libraries/Text/RCTBackedTextInputDelegateAdapter.h @@ -7,8 +7,6 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import - #import "RCTBackedTextInputViewProtocol.h" #import "RCTBackedTextInputDelegate.h" @@ -18,7 +16,11 @@ - (instancetype)initWithTextField:(UITextField *)backedTextInput; +#if !TARGET_OS_OSX - (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(UITextRange *)textRange; +#else +- (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(NSRange)textRange; +#endif - (void)selectedTextRangeWasSet; @end @@ -29,6 +31,10 @@ - (instancetype)initWithTextView:(UITextView *)backedTextInput; +#if !TARGET_OS_OSX - (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(UITextRange *)textRange; +#else +- (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(NSRange)textRange; +#endif @end diff --git a/Libraries/Text/RCTBackedTextInputDelegateAdapter.m b/Libraries/Text/RCTBackedTextInputDelegateAdapter.m index f902f4fd46eb55..23d58412cce63c 100644 --- a/Libraries/Text/RCTBackedTextInputDelegateAdapter.m +++ b/Libraries/Text/RCTBackedTextInputDelegateAdapter.m @@ -13,13 +13,23 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingContext; -@interface RCTBackedTextFieldDelegateAdapter () +@interface RCTBackedTextFieldDelegateAdapter () +#if !TARGET_OS_OSX + +#else + +#endif + @end @implementation RCTBackedTextFieldDelegateAdapter { __weak UITextField *_backedTextInput; BOOL _textDidChangeIsComing; +#if !TARGET_OS_OSX UITextRange *_previousSelectedTextRange; +#else + NSRange _previousSelectedTextRange; +#endif } - (instancetype)initWithTextField:(UITextField *)backedTextInput @@ -28,8 +38,10 @@ - (instancetype)initWithTextField:(UITextField * _backedTextInput = backedTextInput; backedTextInput.delegate = self; +#if !TARGET_OS_OSX [_backedTextInput addTarget:self action:@selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged]; [_backedTextInput addTarget:self action:@selector(textFieldDidEndEditingOnExit) forControlEvents:UIControlEventEditingDidEndOnExit]; +#endif } return self; @@ -37,8 +49,10 @@ - (instancetype)initWithTextField:(UITextField * - (void)dealloc { +#if !TARGET_OS_OSX [_backedTextInput removeTarget:self action:nil forControlEvents:UIControlEventEditingChanged]; [_backedTextInput removeTarget:self action:nil forControlEvents:UIControlEventEditingDidEndOnExit]; +#endif } #pragma mark - UITextFieldDelegate @@ -112,7 +126,11 @@ - (BOOL)keyboardInputShouldDelete:(__unused UITextField *)textField #pragma mark - Public Interface +#if !TARGET_OS_OSX - (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(UITextRange *)textRange +#else +- (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(NSRange)textRange +#endif { _previousSelectedTextRange = textRange; } @@ -126,14 +144,74 @@ - (void)selectedTextRangeWasSet - (void)textFieldProbablyDidChangeSelection { - if ([_backedTextInput.selectedTextRange isEqual:_previousSelectedTextRange]) { + if (RCTTextSelectionEqual([_backedTextInput selectedTextRange], _previousSelectedTextRange)) { return; } - _previousSelectedTextRange = _backedTextInput.selectedTextRange; + _previousSelectedTextRange = [_backedTextInput selectedTextRange]; [_backedTextInput.textInputDelegate textInputDidChangeSelection]; } +#if TARGET_OS_OSX + +#pragma mark - NSTextFieldDelegate + +- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor +{ + return [self textFieldShouldEndEditing:_backedTextInput]; +} + +- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector +{ + BOOL commandHandled = NO; + // enter/return + if (commandSelector == @selector(insertNewline:) || commandSelector == @selector(insertNewlineIgnoringFieldEditor:)) { + [self textFieldDidEndEditingOnExit]; + commandHandled = YES; + //backspace + } else if (commandSelector == @selector(deleteBackward:)) { + id textInputDelegate = [_backedTextInput textInputDelegate]; + if (textInputDelegate != nil && ![textInputDelegate textInputShouldHandleDeleteBackward:_backedTextInput]) { + commandHandled = YES; + } else { + [self keyboardInputShouldDelete:_backedTextInput]; + } + //deleteForward + } else if (commandSelector == @selector(deleteForward:)) { + id textInputDelegate = [_backedTextInput textInputDelegate]; + if (textInputDelegate != nil && ![textInputDelegate textInputShouldHandleDeleteForward:_backedTextInput]) { + commandHandled = YES; + } else { + [self keyboardInputShouldDelete:_backedTextInput]; + } + //paste + } else if (commandSelector == @selector(paste:)) { + _backedTextInput.textWasPasted = YES; + } + return commandHandled; +} + +- (void)textFieldBeginEditing:(NSTextField *)textField +{ + [self textFieldDidBeginEditing:_backedTextInput]; +} + +- (void)textFieldDidChange:(NSTextField *)textField +{ + [self textFieldDidChange]; +} + +- (void)textFieldEndEditing:(NSTextField *)textField +{ + [self textFieldDidEndEditing:_backedTextInput]; +} + +- (void)textFieldDidChangeSelection:(NSTextField *)textField +{ + [self selectedTextRangeWasSet]; +} +#endif // TARGET_OS_OSX + @end #pragma mark - RCTBackedTextViewDelegateAdapter (for UITextView) @@ -142,9 +220,18 @@ @interface RCTBackedTextViewDelegateAdapter () @end @implementation RCTBackedTextViewDelegateAdapter { +#if !TARGET_OS_OSX __weak UITextView *_backedTextInput; +#else + // TODO(tomun): NSTextView cannot be __weak + __unsafe_unretained UITextView *_backedTextInput; +#endif BOOL _textDidChangeIsComing; +#if !TARGET_OS_OSX UITextRange *_previousSelectedTextRange; +#else + NSRange _previousSelectedTextRange; +#endif } - (instancetype)initWithTextView:(UITextView *)backedTextInput @@ -192,7 +279,11 @@ - (BOOL)textView:(__unused UITextView *)textView shouldChangeTextInRange:(NSRang if (!_backedTextInput.textWasPasted && [text isEqualToString:@"\n"]) { if ([_backedTextInput.textInputDelegate textInputShouldReturn]) { [_backedTextInput.textInputDelegate textInputDidReturn]; +#if !TARGET_OS_OSX [_backedTextInput endEditing:NO]; +#else + [[_backedTextInput window] endEditingFor:nil]; +#endif return NO; } } @@ -210,14 +301,72 @@ - (void)textViewDidChange:(__unused UITextView *)textView [_backedTextInput.textInputDelegate textInputDidChange]; } +#if !TARGET_OS_OSX + - (void)textViewDidChangeSelection:(__unused UITextView *)textView { [self textViewProbablyDidChangeSelection]; } +#endif // !TARGET_OS_OSX + +#if TARGET_OS_OSX + +#pragma mark - NSTextViewDelegate + +- (BOOL)textView:(NSTextView *)textView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(nullable NSString *)replacementString +{ + return [self textView:textView shouldChangeTextInRange:affectedCharRange replacementText:replacementString]; +} + +- (void)textViewDidChangeSelection:(NSNotification *)notification +{ + [self textViewProbablyDidChangeSelection]; +} + +- (void)textDidBeginEditing:(NSNotification *)notification +{ + [self textViewDidBeginEditing:_backedTextInput]; +} + +- (void)textDidChange:(NSNotification *)notification +{ + [self textViewDidChange:_backedTextInput]; +} + +- (void)textDidEndEditing:(NSNotification *)notification +{ + [self textViewDidEndEditing:_backedTextInput]; +} + +- (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector +{ + BOOL commandHandled = NO; + id textInputDelegate = [_backedTextInput textInputDelegate]; + // enter/return + if (textInputDelegate.textInputShouldReturn && (commandSelector == @selector(insertNewline:) || commandSelector == @selector(insertNewlineIgnoringFieldEditor:))) { + [_backedTextInput.window makeFirstResponder:nil]; + commandHandled = YES; + //backspace + } else if (commandSelector == @selector(deleteBackward:)) { + commandHandled = textInputDelegate != nil && ![textInputDelegate textInputShouldHandleDeleteBackward:_backedTextInput]; + //deleteForward + } else if (commandSelector == @selector(deleteForward:)) { + commandHandled = textInputDelegate != nil && ![textInputDelegate textInputShouldHandleDeleteForward:_backedTextInput]; + } + + return commandHandled; +} + +#endif // TARGET_OS_OSX + #pragma mark - Public Interface +#if !TARGET_OS_OSX - (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(UITextRange *)textRange +#else +- (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(NSRange)textRange +#endif { _previousSelectedTextRange = textRange; } @@ -226,11 +375,11 @@ - (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(UITextRange *)tex - (void)textViewProbablyDidChangeSelection { - if ([_backedTextInput.selectedTextRange isEqual:_previousSelectedTextRange]) { + if (RCTTextSelectionEqual([_backedTextInput selectedTextRange], _previousSelectedTextRange)) { return; } - _previousSelectedTextRange = _backedTextInput.selectedTextRange; + _previousSelectedTextRange = [_backedTextInput selectedTextRange]; [_backedTextInput.textInputDelegate textInputDidChangeSelection]; } diff --git a/Libraries/Text/RCTBackedTextInputViewProtocol.h b/Libraries/Text/RCTBackedTextInputViewProtocol.h index 90e0aa4d67ee71..7c4921bfd2b85d 100644 --- a/Libraries/Text/RCTBackedTextInputViewProtocol.h +++ b/Libraries/Text/RCTBackedTextInputViewProtocol.h @@ -7,20 +7,45 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import +#import "RCTTextUIKit.h" + +#if TARGET_OS_OSX +NS_ASSUME_NONNULL_BEGIN +@protocol RCTUITextFieldDelegate +@optional +- (BOOL)textField:(NSTextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; // return NO to not change text +- (void)textFieldBeginEditing:(NSTextField *)textField; +- (void)textFieldDidChange:(NSTextField *)textField; +- (void)textFieldEndEditing:(NSTextField *)textField; +- (void)textFieldDidChangeSelection:(NSTextField *)textField; +@end +NS_ASSUME_NONNULL_END +#endif // TARGET_OS_OSX @protocol RCTBackedTextInputDelegate; +#if !TARGET_OS_OSX @protocol RCTBackedTextInputViewProtocol +#else +@protocol RCTBackedTextInputViewProtocol +#endif @property (nonatomic, copy, nullable) NSString *text; @property (nonatomic, strong, nullable) UIColor *textColor; @property (nonatomic, copy, nullable) NSString *placeholder; @property (nonatomic, strong, nullable) UIColor *placeholderColor; +#if !TARGET_OS_OSX @property (nonatomic, assign, readonly) BOOL textWasPasted; +#else +@property (nonatomic, assign) BOOL textWasPasted; +#endif + @property (nonatomic, strong, nullable) UIFont *font; @property (nonatomic, assign) UIEdgeInsets textContainerInset; +#if !TARGET_OS_OSX @property (nonatomic, strong, nullable) UIView *inputAccessoryView; +#endif @property (nonatomic, weak, nullable) id textInputDelegate; @property (nonatomic, readonly) CGSize contentSize; @@ -29,7 +54,18 @@ // explicitly specify should `delegate` be notified about the change or not. // If the change was initiated programmatically, we must NOT notify the delegate. // If the change was a result of user actions (like typing or touches), we MUST notify the delegate. +#if !TARGET_OS_OSX - (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange NS_UNAVAILABLE; - (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange notifyDelegate:(BOOL)notifyDelegate; +#else +- (NSRange)selectedTextRange; +- (void)setSelectedTextRange:(NSRange)selectedTextRange NS_UNAVAILABLE; +- (void)setSelectedTextRange:(NSRange)selectedTextRange notifyDelegate:(BOOL)notifyDelegate; +#endif + +#if TARGET_OS_OSX +// UITextInput method for OSX +- (CGSize)sizeThatFits:(CGSize)size; +#endif @end diff --git a/Libraries/Text/RCTFontAttributes.h b/Libraries/Text/RCTFontAttributes.h index 37b9954cd3dda0..91a214605af07e 100644 --- a/Libraries/Text/RCTFontAttributes.h +++ b/Libraries/Text/RCTFontAttributes.h @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import #import "RCTFontAttributesDelegate.h" diff --git a/Libraries/Text/RCTFontAttributes.m b/Libraries/Text/RCTFontAttributes.m index 3cb8da119ee4ab..65bd729de21294 100644 --- a/Libraries/Text/RCTFontAttributes.m +++ b/Libraries/Text/RCTFontAttributes.m @@ -16,7 +16,9 @@ @interface RCTFontAttributes () { +#if !TARGET_OS_OSX RCTAccessibilityManager *_accessibilityManager; +#endif } @property (nonatomic, strong) UIFont *font; @@ -27,9 +29,12 @@ @implementation RCTFontAttributes - (instancetype)initWithAccessibilityManager:(RCTAccessibilityManager *)accessibilityManager { +#if !TARGET_OS_OSX RCTAssertParam(accessibilityManager); - +#endif + if (self = [super init]) { +#if !TARGET_OS_OSX _accessibilityManager = accessibilityManager; _fontSizeMultiplier = _accessibilityManager.multiplier; @@ -37,6 +42,9 @@ - (instancetype)initWithAccessibilityManager:(RCTAccessibilityManager *)accessib selector:@selector(contentSizeMultiplierDidChange) name:RCTAccessibilityManagerDidUpdateMultiplierNotification object:_accessibilityManager]; +#else + _fontSizeMultiplier = 1; +#endif [self updateFont]; } @@ -48,10 +56,12 @@ - (void)dealloc [[NSNotificationCenter defaultCenter] removeObserver:self]; } +#if !TARGET_OS_OSX - (void)contentSizeMultiplierDidChange { self.fontSizeMultiplier = _accessibilityManager.multiplier; } +#endif - (void)setAllowFontScaling:(BOOL)allowFontScaling { diff --git a/Libraries/Text/RCTShadowRawText.m b/Libraries/Text/RCTShadowRawText.m index f163c799b9858a..5d25e14187bf77 100644 --- a/Libraries/Text/RCTShadowRawText.m +++ b/Libraries/Text/RCTShadowRawText.m @@ -13,6 +13,7 @@ @implementation RCTShadowRawText +#if !TARGET_OS_OSX - (instancetype)init { if ((self = [super init])) { @@ -33,6 +34,7 @@ - (void)contentSizeMultiplierDidChange:(NSNotification *)note { [self dirtyText]; } +#endif - (void)setText:(NSString *)text { diff --git a/Libraries/Text/RCTShadowText.m b/Libraries/Text/RCTShadowText.m index fe2b037724f7a3..268fe309260d4d 100644 --- a/Libraries/Text/RCTShadowText.m +++ b/Libraries/Text/RCTShadowText.m @@ -9,7 +9,9 @@ #import "RCTShadowText.h" +#if !TARGET_OS_OSX #import +#endif #import #import #import @@ -51,11 +53,19 @@ static YGSize RCTMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, f CGSize computedSize = [layoutManager usedRectForTextContainer:textContainer].size; YGSize result; +#if !TARGET_OS_OSX result.width = RCTCeilPixelValue(computedSize.width); +#else + result.width = RCTCeilPixelValue(computedSize.width, shadowText.scale); +#endif if (shadowText->_effectiveLetterSpacing < 0) { result.width -= shadowText->_effectiveLetterSpacing; } +#if !TARGET_OS_OSX result.height = RCTCeilPixelValue(computedSize.height); +#else + result.height = RCTCeilPixelValue(computedSize.height, shadowText.scale); +#endif return result; } @@ -76,10 +86,12 @@ - (instancetype)init YGNodeSetMeasureFunc(self.yogaNode, RCTMeasure); +#if !TARGET_OS_OSX [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contentSizeMultiplierDidChange:) name:RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification object:nil]; +#endif } return self; } @@ -100,11 +112,13 @@ - (BOOL)isYogaLeafNode return YES; } +#if !TARGET_OS_OSX - (void)contentSizeMultiplierDidChange:(NSNotification *)note { YGNodeMarkDirty(self.yogaNode); [self dirtyText]; } +#endif - (NSDictionary *)processUpdatedProperties:(NSMutableSet *)applierBlocks parentProperties:(NSDictionary *)parentProperties @@ -174,11 +188,21 @@ - (void)applyLayoutToChildren:(YGNodeRef)node UIFont *font = [textStorage attribute:NSFontAttributeName atIndex:range.location effectiveRange:nil]; CGRect glyphRect = [layoutManager boundingRectForGlyphRange:range inTextContainer:textContainer]; CGRect childFrame = {{ +#if !TARGET_OS_OSX RCTRoundPixelValue(glyphRect.origin.x), RCTRoundPixelValue(glyphRect.origin.y + glyphRect.size.height - height + font.descender) +#else + RCTRoundPixelValue(glyphRect.origin.x, self.scale), + RCTRoundPixelValue(glyphRect.origin.y + glyphRect.size.height - height + font.descender, self.scale) +#endif }, { +#if !TARGET_OS_OSX RCTRoundPixelValue(width), RCTRoundPixelValue(height) +#else + RCTRoundPixelValue(width, self.scale), + RCTRoundPixelValue(height, self.scale) +#endif }}; NSRange truncatedGlyphRange = [layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:range.location]; @@ -361,8 +385,18 @@ - (NSAttributedString *)_attributedStringWithFontFamily:(NSString *)fontFamily [self _addAttribute:NSFontAttributeName withValue:font toAttributedString:attributedString]; [self _addAttribute:NSKernAttributeName withValue:letterSpacing toAttributedString:attributedString]; [self _addAttribute:RCTReactTagAttributeName withValue:self.reactTag toAttributedString:attributedString]; +#if !TARGET_OS_OSX + CGFloat lineHeight = font.lineHeight; +#else + CGFloat lineHeight = 0.0; + if ([NSString respondsToSelector:@selector(defaultLineHeightForFont:)]) { + lineHeight = [(id)[NSString self] defaultLineHeightForFont:font]; + } else { + lineHeight = [[NSLayoutManager new] defaultLineHeightForFont:font]; + } +#endif [self _setParagraphStyleOnAttributedString:attributedString - fontLineHeight:font.lineHeight + fontLineHeight:lineHeight heightOfTallestSubview:heightOfTallestSubview]; // create a non-mutable attributedString for use by the Text system which avoids copies down the line @@ -504,7 +538,11 @@ - (CGRect)updateStorage:(NSTextStorage *)textStorage toFitFrame:(CGRect)frame } // Vertically center draw position for new text sizing. +#if !TARGET_OS_OSX frame.origin.y = self.compoundInsets.top + RCTRoundPixelValue((CGRectGetHeight(frame) - requiredSize.height) / 2.0f); +#else + frame.origin.y = self.compoundInsets.top + RCTRoundPixelValue((CGRectGetHeight(frame) - requiredSize.height) / 2.0f, self.scale); +#endif return frame; } @@ -558,7 +596,7 @@ - (RCTSizeComparison)attemptScale:(CGFloat)scale UIFont *originalFont = [self.attributedString attribute:NSFontAttributeName atIndex:range.location effectiveRange:&range]; - UIFont *newFont = [font fontWithSize:originalFont.pointSize * scale]; + UIFont *newFont = UIFontWithSize(font, originalFont.pointSize * scale); [textStorage removeAttribute:NSFontAttributeName range:range]; [textStorage addAttribute:NSFontAttributeName value:newFont range:range]; } diff --git a/Libraries/Text/RCTText.h b/Libraries/Text/RCTText.h index cff9c5c52dbb88..cee48c9f2944fa 100644 --- a/Libraries/Text/RCTText.h +++ b/Libraries/Text/RCTText.h @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import @interface RCTText : UIView diff --git a/Libraries/Text/RCTText.m b/Libraries/Text/RCTText.m index ec1ee979a7fb38..6883c87834ac3a 100644 --- a/Libraries/Text/RCTText.m +++ b/Libraries/Text/RCTText.m @@ -9,13 +9,18 @@ #import "RCTText.h" +#if !TARGET_OS_OSX #import +#endif +#import #import #import #import "RCTShadowText.h" +#import + static void collectNonTextDescendants(RCTText *view, NSMutableArray *nonTextDescendants) { for (UIView *child in view.reactSubviews) { @@ -31,28 +36,46 @@ @implementation RCTText { NSTextStorage *_textStorage; CAShapeLayer *_highlightLayer; +#if !TARGET_OS_OSX UILongPressGestureRecognizer *_longPressGestureRecognizer; +#endif } - (instancetype)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { _textStorage = [NSTextStorage new]; +#if !TARGET_OS_OSX self.isAccessibilityElement = YES; self.accessibilityTraits |= UIAccessibilityTraitStaticText; +#else + self.accessibilityRole = NSAccessibilityStaticTextRole; +#endif self.opaque = NO; - self.contentMode = UIViewContentModeRedraw; + UIViewSetContentModeRedraw(self); } return self; } +#if TARGET_OS_OSX +-(BOOL)canBecomeKeyView +{ + //RCTText should not get any keyboard focus + return NO; +} +#endif + - (NSString *)description { NSString *superDescription = super.description; NSRange semicolonRange = [superDescription rangeOfString:@";"]; - NSString *replacement = [NSString stringWithFormat:@"; reactTag: %@; text: %@", self.reactTag, self.textStorage.string]; - return [superDescription stringByReplacingCharactersInRange:semicolonRange withString:replacement]; + if (semicolonRange.location == NSNotFound) { + return [[superDescription substringToIndex:superDescription.length - 1] stringByAppendingFormat:@"; reactTag: %@; text: %@, frame = %@; layer = %@>", self.reactTag, self.textStorage.string, NSStringFromCGRect(self.frame), self.layer]; + } else { + NSString *replacement = [NSString stringWithFormat:@"; reactTag: %@; text: %@", self.reactTag, self.textStorage.string]; + return [superDescription stringByReplacingCharactersInRange:semicolonRange withString:replacement]; + } } - (void)setSelectable:(BOOL)selectable @@ -63,14 +86,17 @@ - (void)setSelectable:(BOOL)selectable _selectable = selectable; +#if !TARGET_OS_OSX if (_selectable) { [self enableContextMenu]; } else { [self disableContextMenu]; } +#endif } +#if !TARGET_OS_OSX - (void)reactSetFrame:(CGRect)frame { // Text looks super weird if its frame is animated. @@ -79,6 +105,7 @@ - (void)reactSetFrame:(CGRect)frame [super reactSetFrame:frame]; }]; } +#endif - (void)reactSetInheritedBackgroundColor:(UIColor *)inheritedBackgroundColor { @@ -124,7 +151,7 @@ - (void)drawRect:(CGRect)rect [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin]; [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin]; - __block UIBezierPath *highlightPath = nil; + __block CGMutablePathRef highlightPath = NULL; NSRange characterRange = [layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; [layoutManager.textStorage enumerateAttribute:RCTIsHighlightedAttributeName inRange:characterRange options:0 usingBlock:^(NSNumber *value, NSRange range, BOOL *_) { if (!value.boolValue) { @@ -132,12 +159,10 @@ - (void)drawRect:(CGRect)rect } [layoutManager enumerateEnclosingRectsForGlyphRange:range withinSelectedGlyphRange:range inTextContainer:textContainer usingBlock:^(CGRect enclosingRect, __unused BOOL *__) { - UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(enclosingRect, -2, -2) cornerRadius:2]; - if (highlightPath) { - [highlightPath appendPath:path]; - } else { - highlightPath = path; + if (highlightPath == NULL) { + highlightPath = CGPathCreateMutable(); } + CGPathAddRoundedRect(highlightPath, NULL, CGRectInset(enclosingRect, -2, -2), 2, 2); }]; }]; @@ -148,7 +173,8 @@ - (void)drawRect:(CGRect)rect [self.layer addSublayer:_highlightLayer]; } _highlightLayer.position = (CGPoint){_contentInset.left, _contentInset.top}; - _highlightLayer.path = highlightPath.CGPath; + _highlightLayer.path = highlightPath; + CFRelease(highlightPath); } else { [_highlightLayer removeFromSuperlayer]; _highlightLayer = nil; @@ -203,6 +229,7 @@ - (NSString *)accessibilityLabel #pragma mark - Context Menu +#if !TARGET_OS_OSX - (void)enableContextMenu { _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)]; @@ -232,12 +259,47 @@ - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture [menuController setMenuVisible:YES animated:YES]; #endif } +#else + +- (void)rightMouseDown:(NSEvent *)event +{ + if (_selectable == NO) { + return; + } + NSText *fieldEditor = [self.window fieldEditor:YES forObject:self]; + NSMenu *fieldEditorMenu = [fieldEditor menuForEvent:event]; + + RCTAssert(fieldEditorMenu, @"Unable to obtain fieldEditor's context menu"); + + if (fieldEditorMenu) { + NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; + + for (NSMenuItem *fieldEditorMenuItem in fieldEditorMenu.itemArray) { + if (fieldEditorMenuItem.action == @selector(copy:)) { + NSMenuItem *item = [fieldEditorMenuItem copy]; + + item.target = self; + [menu addItem:item]; + + break; + } + } + + RCTAssert(menu.numberOfItems > 0, @"Unable to create context menu with \"Copy\" item"); + + if (menu.numberOfItems > 0) { + [NSMenu popUpContextMenu:menu withEvent:event forView:self]; + } + } +} +#endif - (BOOL)canBecomeFirstResponder { return _selectable; } +#if !TARGET_OS_OSX - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { if (_selectable && action == @selector(copy:)) { @@ -246,18 +308,20 @@ - (BOOL)canPerformAction:(SEL)action withSender:(id)sender return [self.nextResponder canPerformAction:action withSender:sender]; } +#endif - (void)copy:(id)sender { #if !TARGET_OS_TV NSAttributedString *attributedString = _textStorage; - NSMutableDictionary *item = [NSMutableDictionary new]; - NSData *rtf = [attributedString dataFromRange:NSMakeRange(0, attributedString.length) documentAttributes:@{NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType} error:nil]; +#if TARGET_OS_IPHONE + NSMutableDictionary *item = [NSMutableDictionary new]; + if (rtf) { [item setObject:rtf forKey:(id)kUTTypeFlatRTFD]; } @@ -266,6 +330,11 @@ - (void)copy:(id)sender UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; pasteboard.items = @[item]; +#elif TARGET_OS_OSX + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard clearContents]; + [pasteboard writeObjects:[NSArray arrayWithObjects:attributedString.string, rtf, nil]]; +#endif #endif } diff --git a/Libraries/Text/RCTText.xcodeproj/project.pbxproj b/Libraries/Text/RCTText.xcodeproj/project.pbxproj index cb8b92e4f38c7a..ee53f229a605a3 100644 --- a/Libraries/Text/RCTText.xcodeproj/project.pbxproj +++ b/Libraries/Text/RCTText.xcodeproj/project.pbxproj @@ -11,6 +11,40 @@ 131B6AC11AF0CD0600FFC3E0 /* RCTTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6ABF1AF0CD0600FFC3E0 /* RCTTextViewManager.m */; }; 1362F1001B4D51F400E06D8C /* RCTTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 1362F0FD1B4D51F400E06D8C /* RCTTextField.m */; }; 1362F1011B4D51F400E06D8C /* RCTTextFieldManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1362F0FF1B4D51F400E06D8C /* RCTTextFieldManager.m */; }; + 1821879A1FE97C8600E978D7 /* RCTTextInput.m in Sources */ = {isa = PBXBuildFile; fileRef = 599DF2631F03076D0079B53E /* RCTTextInput.m */; }; + 183496EB1F5DF0C900C0A1B4 /* RCTTextUIKit.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 645E36D71F01D523002EF2C2 /* RCTTextUIKit.h */; }; + 183496EC1F5DF0C900C0A1B4 /* RCTTextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 131B6ABC1AF0CD0600FFC3E0 /* RCTTextView.h */; }; + 183496ED1F5DF0C900C0A1B4 /* RCTTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 131B6ABE1AF0CD0600FFC3E0 /* RCTTextViewManager.h */; }; + 183496EF1F5DF0F100C0A1B4 /* RCTTextUIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 645E36D71F01D523002EF2C2 /* RCTTextUIKit.h */; }; + 183496F01F5DF0F100C0A1B4 /* RCTTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 131B6ABC1AF0CD0600FFC3E0 /* RCTTextView.h */; }; + 183496F11F5DF0F100C0A1B4 /* RCTTextViewManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 131B6ABE1AF0CD0600FFC3E0 /* RCTTextViewManager.h */; }; + 18642E0C1F9E4F01006C2EED /* RCTTextUIKit.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 645E36D71F01D523002EF2C2 /* RCTTextUIKit.h */; }; + 1891B3211FEC3BA300CEDA30 /* RCTBackedTextInputViewProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 599DF25D1F0304B30079B53E /* RCTBackedTextInputViewProtocol.h */; }; + 1891B3221FEC3BAB00CEDA30 /* RCTTextInput.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 599DF2621F03076D0079B53E /* RCTTextInput.h */; }; + 18B17EE11FFEC21F00ECC5C5 /* RCTBackedTextInputViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 599DF25D1F0304B30079B53E /* RCTBackedTextInputViewProtocol.h */; }; + 18B17EE21FFEC22400ECC5C5 /* RCTBackedTextInputViewProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 599DF25D1F0304B30079B53E /* RCTBackedTextInputViewProtocol.h */; }; + 18B17EE31FFEC22B00ECC5C5 /* RCTBackedTextInputViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 599DF25D1F0304B30079B53E /* RCTBackedTextInputViewProtocol.h */; }; + 18B17EE41FFEC23000ECC5C5 /* RCTBackedTextInputViewProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 599DF25D1F0304B30079B53E /* RCTBackedTextInputViewProtocol.h */; }; + 18B17EE51FFEC25200ECC5C5 /* RCTBackedTextInputDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 598F41231F145D4900B8495B /* RCTBackedTextInputDelegate.h */; }; + 18B17EE61FFEC25700ECC5C5 /* RCTBackedTextInputDelegate.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 598F41231F145D4900B8495B /* RCTBackedTextInputDelegate.h */; }; + 18B17EE71FFEC25E00ECC5C5 /* RCTBackedTextInputDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 598F41231F145D4900B8495B /* RCTBackedTextInputDelegate.h */; }; + 18B17EE81FFEC26500ECC5C5 /* RCTBackedTextInputDelegate.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 598F41231F145D4900B8495B /* RCTBackedTextInputDelegate.h */; }; + 18B17EE91FFEC27A00ECC5C5 /* RCTTextInput.h in Headers */ = {isa = PBXBuildFile; fileRef = 599DF2621F03076D0079B53E /* RCTTextInput.h */; }; + 18B17EEA1FFEC28100ECC5C5 /* RCTTextInput.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 599DF2621F03076D0079B53E /* RCTTextInput.h */; }; + 18B17EEB1FFEC28900ECC5C5 /* RCTTextInput.h in Headers */ = {isa = PBXBuildFile; fileRef = 599DF2621F03076D0079B53E /* RCTTextInput.h */; }; + 18B17EEC1FFEC29100ECC5C5 /* RCTTextInput.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 599DF2621F03076D0079B53E /* RCTTextInput.h */; }; + 18B17EED1FFEC2A100ECC5C5 /* RCTFontAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = A85C82981F742AA20036C019 /* RCTFontAttributes.h */; }; + 18B17EEE1FFEC2A700ECC5C5 /* RCTFontAttributes.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A85C82981F742AA20036C019 /* RCTFontAttributes.h */; }; + 18B17EEF1FFEC2AE00ECC5C5 /* RCTFontAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = A85C82981F742AA20036C019 /* RCTFontAttributes.h */; }; + 18B17EF01FFEC2B500ECC5C5 /* RCTFontAttributes.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A85C82981F742AA20036C019 /* RCTFontAttributes.h */; }; + 18B17EF11FFEC2C200ECC5C5 /* RCTFontAttributesDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */; }; + 18B17EF21FFEC2D100ECC5C5 /* RCTFontAttributesDelegate.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */; }; + 18B17EF31FFEC2D900ECC5C5 /* RCTFontAttributesDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */; }; + 18B17EF41FFEC2E000ECC5C5 /* RCTFontAttributesDelegate.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */; }; + 18B20FCB1FEB4B7200032F71 /* RCTBackedTextInputDelegateAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 598F41251F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m */; }; + 18DF92161F5EFA6A00DFCFC5 /* RCTUITextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 59B125C71E6E4E15004E2A67 /* RCTUITextView.h */; }; + 18DF92171F5EFA7300DFCFC5 /* RCTUITextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59B125C71E6E4E15004E2A67 /* RCTUITextView.h */; }; + 18FBD5261FEB4A6D0006B0E3 /* RCTFontAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = A85C82991F742AA20036C019 /* RCTFontAttributes.m */; }; 19FC5C851D41A4120090108F /* RCTTextSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FC5C841D41A4120090108F /* RCTTextSelection.m */; }; 2D3B5F331D9B102D00451313 /* RCTTextSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FC5C841D41A4120090108F /* RCTTextSelection.m */; }; 2D3B5F341D9B103100451313 /* RCTRawTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511C71A9E6C5C00147676 /* RCTRawTextManager.m */; }; @@ -44,12 +78,64 @@ 59F60E921E661BDD0081153B /* RCTShadowTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */; }; 59F60E931E661BDD0081153B /* RCTShadowTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E901E661BDD0081153B /* RCTShadowTextView.m */; }; 59F60E941E661BDD0081153B /* RCTShadowTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E901E661BDD0081153B /* RCTShadowTextView.m */; }; + 6417F9291F057FEF00564C9B /* RCTUITextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59B125C81E6E4E15004E2A67 /* RCTUITextView.m */; }; + 64215ECF1EF8927D0089A305 /* RCTTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511CD1A9E6C5C00147676 /* RCTTextManager.m */; }; + 64215ED01EF892E30089A305 /* RCTRawTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511C71A9E6C5C00147676 /* RCTRawTextManager.m */; }; + 6436853C1F07C194003CFC7C /* RCTTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6ABD1AF0CD0600FFC3E0 /* RCTTextView.m */; }; + 643A6E801EFAE1740047EC8B /* RCTTextSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FC5C841D41A4120090108F /* RCTTextSelection.m */; }; + 643A6E8C1EFB23460047EC8B /* RCTTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 1362F0FD1B4D51F400E06D8C /* RCTTextField.m */; }; + 645982B11EF95E8D00549B89 /* RCTShadowTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */; }; + 645982B21EF95EEF00549B89 /* RCTUITextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59AF89A91EDCBCC700F004B1 /* RCTUITextField.m */; }; + 645D71061EFC6FE600D9C2FA /* RCTTextFieldManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1362F0FF1B4D51F400E06D8C /* RCTTextFieldManager.m */; }; + 6490EA791F683C2C00E20046 /* RCTTextUIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 645E36D71F01D523002EF2C2 /* RCTTextUIKit.h */; }; + 6490EA7A1F683C5C00E20046 /* RCTTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 131B6ABC1AF0CD0600FFC3E0 /* RCTTextView.h */; }; + 6490EA7B1F683C5C00E20046 /* RCTTextViewManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 131B6ABE1AF0CD0600FFC3E0 /* RCTTextViewManager.h */; }; + 6490EA7C1F683C5C00E20046 /* RCTUITextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 59B125C71E6E4E15004E2A67 /* RCTUITextView.h */; }; + 6490EA7D1F683C8500E20046 /* RCTTextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 131B6ABC1AF0CD0600FFC3E0 /* RCTTextView.h */; }; + 6490EA7E1F683C8500E20046 /* RCTTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 131B6ABE1AF0CD0600FFC3E0 /* RCTTextViewManager.h */; }; + 6490EA7F1F683C8500E20046 /* RCTUITextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59B125C71E6E4E15004E2A67 /* RCTUITextView.h */; }; + 649B1AFD1F0962EC00DF6E3A /* RCTTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6ABF1AF0CD0600FFC3E0 /* RCTTextViewManager.m */; }; + 64BF1F391EF8464D00B83E07 /* RCTText.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512141A9E6EFF00147676 /* RCTText.m */; }; + 64BF1F3A1EF8467800B83E07 /* RCTShadowRawText.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511C91A9E6C5C00147676 /* RCTShadowRawText.m */; }; + 64BF1F3B1EF8468E00B83E07 /* RCTShadowText.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511CB1A9E6C5C00147676 /* RCTShadowText.m */; }; + 64BF1F3C1EF8469400B83E07 /* RCTShadowTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E901E661BDD0081153B /* RCTShadowTextView.m */; }; A85C829A1F742AA20036C019 /* RCTFontAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = A85C82991F742AA20036C019 /* RCTFontAttributes.m */; }; AF3225F91DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */; }; AF3225FA1DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ + 183496EA1F5DF07600C0A1B4 /* Copy Headers */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/React; + dstSubfolderSpec = 16; + files = ( + 18B17EF41FFEC2E000ECC5C5 /* RCTFontAttributesDelegate.h in Copy Headers */, + 18B17EEE1FFEC2A700ECC5C5 /* RCTFontAttributes.h in Copy Headers */, + 18B17EEC1FFEC29100ECC5C5 /* RCTTextInput.h in Copy Headers */, + 18B17EE61FFEC25700ECC5C5 /* RCTBackedTextInputDelegate.h in Copy Headers */, + 18B17EE41FFEC23000ECC5C5 /* RCTBackedTextInputViewProtocol.h in Copy Headers */, + 18DF92171F5EFA7300DFCFC5 /* RCTUITextView.h in Copy Headers */, + 183496EB1F5DF0C900C0A1B4 /* RCTTextUIKit.h in Copy Headers */, + 183496EC1F5DF0C900C0A1B4 /* RCTTextView.h in Copy Headers */, + 183496ED1F5DF0C900C0A1B4 /* RCTTextViewManager.h in Copy Headers */, + ); + name = "Copy Headers"; + runOnlyForDeploymentPostprocessing = 0; + }; + 1891B31E1FEC3B6500CEDA30 /* Copy Headers */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/RCTText; + dstSubfolderSpec = 16; + files = ( + 1891B3221FEC3BAB00CEDA30 /* RCTTextInput.h in Copy Headers */, + 1891B3211FEC3BA300CEDA30 /* RCTBackedTextInputViewProtocol.h in Copy Headers */, + ); + name = "Copy Headers"; + runOnlyForDeploymentPostprocessing = 0; + }; 599DF25E1F0306540079B53E /* Copy Headers */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -74,6 +160,25 @@ name = "Copy Headers"; runOnlyForDeploymentPostprocessing = 0; }; + 6490EA781F683C2000E20046 /* Copy Headers */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/React; + dstSubfolderSpec = 16; + files = ( + 18B17EF21FFEC2D100ECC5C5 /* RCTFontAttributesDelegate.h in Copy Headers */, + 18B17EF01FFEC2B500ECC5C5 /* RCTFontAttributes.h in Copy Headers */, + 18B17EEA1FFEC28100ECC5C5 /* RCTTextInput.h in Copy Headers */, + 18B17EE81FFEC26500ECC5C5 /* RCTBackedTextInputDelegate.h in Copy Headers */, + 18B17EE21FFEC22400ECC5C5 /* RCTBackedTextInputViewProtocol.h in Copy Headers */, + 18642E0C1F9E4F01006C2EED /* RCTTextUIKit.h in Copy Headers */, + 6490EA7D1F683C8500E20046 /* RCTTextView.h in Copy Headers */, + 6490EA7E1F683C8500E20046 /* RCTTextViewManager.h in Copy Headers */, + 6490EA7F1F683C8500E20046 /* RCTUITextView.h in Copy Headers */, + ); + name = "Copy Headers"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -113,6 +218,8 @@ 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowTextField.m; sourceTree = ""; }; 59F60E8F1E661BDD0081153B /* RCTShadowTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowTextView.h; sourceTree = ""; }; 59F60E901E661BDD0081153B /* RCTShadowTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowTextView.m; sourceTree = ""; }; + 645E36D71F01D523002EF2C2 /* RCTTextUIKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTTextUIKit.h; sourceTree = ""; }; + 6BDE7AC21ECB8D6200CC951F /* libRCTText.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTText.a; sourceTree = BUILT_PRODUCTS_DIR; }; A85C82981F742AA20036C019 /* RCTFontAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFontAttributes.h; sourceTree = ""; }; A85C82991F742AA20036C019 /* RCTFontAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFontAttributes.m; sourceTree = ""; }; A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFontAttributesDelegate.h; sourceTree = ""; }; @@ -144,6 +251,7 @@ 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */, 59F60E8F1E661BDD0081153B /* RCTShadowTextView.h */, 59F60E901E661BDD0081153B /* RCTShadowTextView.m */, + 645E36D71F01D523002EF2C2 /* RCTTextUIKit.h */, 58B512151A9E6EFF00147676 /* RCTText.h */, 58B512141A9E6EFF00147676 /* RCTText.m */, 1362F0FC1B4D51F400E06D8C /* RCTTextField.h */, @@ -175,12 +283,48 @@ children = ( 58B5119B1A9E6C1200147676 /* libRCTText.a */, 2D2A287B1D9B048500D4039D /* libRCTText-tvOS.a */, + 6BDE7AC21ECB8D6200CC951F /* libRCTText.a */, ); name = Products; sourceTree = ""; }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 183496EE1F5DF0D600C0A1B4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 18B17EF31FFEC2D900ECC5C5 /* RCTFontAttributesDelegate.h in Headers */, + 18B17EED1FFEC2A100ECC5C5 /* RCTFontAttributes.h in Headers */, + 18B17EEB1FFEC28900ECC5C5 /* RCTTextInput.h in Headers */, + 18B17EE51FFEC25200ECC5C5 /* RCTBackedTextInputDelegate.h in Headers */, + 18B17EE31FFEC22B00ECC5C5 /* RCTBackedTextInputViewProtocol.h in Headers */, + 18DF92161F5EFA6A00DFCFC5 /* RCTUITextView.h in Headers */, + 183496EF1F5DF0F100C0A1B4 /* RCTTextUIKit.h in Headers */, + 183496F01F5DF0F100C0A1B4 /* RCTTextView.h in Headers */, + 183496F11F5DF0F100C0A1B4 /* RCTTextViewManager.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6490EA771F683C1800E20046 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 18B17EF11FFEC2C200ECC5C5 /* RCTFontAttributesDelegate.h in Headers */, + 18B17EEF1FFEC2AE00ECC5C5 /* RCTFontAttributes.h in Headers */, + 18B17EE91FFEC27A00ECC5C5 /* RCTTextInput.h in Headers */, + 18B17EE71FFEC25E00ECC5C5 /* RCTBackedTextInputDelegate.h in Headers */, + 18B17EE11FFEC21F00ECC5C5 /* RCTBackedTextInputViewProtocol.h in Headers */, + 6490EA791F683C2C00E20046 /* RCTTextUIKit.h in Headers */, + 6490EA7A1F683C5C00E20046 /* RCTTextView.h in Headers */, + 6490EA7B1F683C5C00E20046 /* RCTTextViewManager.h in Headers */, + 6490EA7C1F683C5C00E20046 /* RCTUITextView.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ 2D2A287A1D9B048500D4039D /* RCTText-tvOS */ = { isa = PBXNativeTarget; @@ -204,6 +348,8 @@ buildPhases = ( 599DF25E1F0306540079B53E /* Copy Headers */, 58B511971A9E6C1200147676 /* Sources */, + 183496EE1F5DF0D600C0A1B4 /* Headers */, + 183496EA1F5DF07600C0A1B4 /* Copy Headers */, ); buildRules = ( ); @@ -214,6 +360,24 @@ productReference = 58B5119B1A9E6C1200147676 /* libRCTText.a */; productType = "com.apple.product-type.library.static"; }; + 6BDE7AB21ECB8D6200CC951F /* RCTText-macos */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6BDE7ABF1ECB8D6200CC951F /* Build configuration list for PBXNativeTarget "RCTText-macos" */; + buildPhases = ( + 1891B31E1FEC3B6500CEDA30 /* Copy Headers */, + 6BDE7AB31ECB8D6200CC951F /* Sources */, + 6490EA771F683C1800E20046 /* Headers */, + 6490EA781F683C2000E20046 /* Copy Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RCTText-macos"; + productName = RCTText; + productReference = 6BDE7AC21ECB8D6200CC951F /* libRCTText.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -245,6 +409,7 @@ projectRoot = ""; targets = ( 58B5119A1A9E6C1200147676 /* RCTText */, + 6BDE7AB21ECB8D6200CC951F /* RCTText-macos */, 2D2A287A1D9B048500D4039D /* RCTText-tvOS */, ); }; @@ -301,6 +466,30 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6BDE7AB31ECB8D6200CC951F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1821879A1FE97C8600E978D7 /* RCTTextInput.m in Sources */, + 64BF1F391EF8464D00B83E07 /* RCTText.m in Sources */, + 6436853C1F07C194003CFC7C /* RCTTextView.m in Sources */, + 64215ED01EF892E30089A305 /* RCTRawTextManager.m in Sources */, + 6417F9291F057FEF00564C9B /* RCTUITextView.m in Sources */, + 18FBD5261FEB4A6D0006B0E3 /* RCTFontAttributes.m in Sources */, + 18B20FCB1FEB4B7200032F71 /* RCTBackedTextInputDelegateAdapter.m in Sources */, + 645D71061EFC6FE600D9C2FA /* RCTTextFieldManager.m in Sources */, + 64BF1F3C1EF8469400B83E07 /* RCTShadowTextView.m in Sources */, + 645982B21EF95EEF00549B89 /* RCTUITextField.m in Sources */, + 64215ECF1EF8927D0089A305 /* RCTTextManager.m in Sources */, + 643A6E8C1EFB23460047EC8B /* RCTTextField.m in Sources */, + 643A6E801EFAE1740047EC8B /* RCTTextSelection.m in Sources */, + 649B1AFD1F0962EC00DF6E3A /* RCTTextViewManager.m in Sources */, + 64BF1F3A1EF8467800B83E07 /* RCTShadowRawText.m in Sources */, + 645982B11EF95E8D00549B89 /* RCTShadowTextField.m in Sources */, + 64BF1F3B1EF8468E00B83E07 /* RCTShadowText.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -375,6 +564,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -417,6 +607,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -447,6 +638,27 @@ }; name = Release; }; + 6BDE7AC01ECB8D6200CC951F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTText; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 6BDE7AC11ECB8D6200CC951F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_STATIC_ANALYZER_MODE = deep; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTText; + SDKROOT = macosx; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -477,6 +689,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 6BDE7ABF1ECB8D6200CC951F /* Build configuration list for PBXNativeTarget "RCTText-macos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6BDE7AC01ECB8D6200CC951F /* Debug */, + 6BDE7AC11ECB8D6200CC951F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 58B511931A9E6C1200147676 /* Project object */; diff --git a/Libraries/Text/RCTTextField.h b/Libraries/Text/RCTTextField.h index 975c926b2ab00b..fb71b373bbeded 100644 --- a/Libraries/Text/RCTTextField.h +++ b/Libraries/Text/RCTTextField.h @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import #import #import diff --git a/Libraries/Text/RCTTextField.m b/Libraries/Text/RCTTextField.m index e6addcaf155708..c6a8e24a0f7caf 100644 --- a/Libraries/Text/RCTTextField.m +++ b/Libraries/Text/RCTTextField.m @@ -68,6 +68,24 @@ - (void)sendKeyValueForString:(NSString *)string #pragma mark - Properties +#if TARGET_OS_OSX +- (void)setReactPaddingInsets:(UIEdgeInsets)reactPaddingInsets +{ + [super setReactPaddingInsets:reactPaddingInsets]; + // We apply `paddingInsets` as `backedTextInputView`'s `textContainerInsets` on mac. + ((RCTUITextField*)self.backedTextInputView).textContainerInset = reactPaddingInsets; + [self setNeedsLayout]; +} + +- (void)setReactBorderInsets:(UIEdgeInsets)reactBorderInsets +{ + [super setReactBorderInsets:reactBorderInsets]; + // We apply `borderInsets` as `backedTextInputView`'s layout offset on mac. + ((RCTUITextField*)self.backedTextInputView).frame = UIEdgeInsetsInsetRect(self.bounds, reactBorderInsets); + [self setNeedsLayout]; +} +#endif // TARGET_OS_OSX + - (NSString *)text { return _backedTextInput.text; @@ -77,6 +95,7 @@ - (void)setText:(NSString *)text { NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; if (eventLag == 0 && ![text isEqualToString:self.text]) { +#if !TARGET_OS_OSX UITextRange *selection = _backedTextInput.selectedTextRange; NSInteger oldTextLength = _backedTextInput.text.length; @@ -91,11 +110,110 @@ - (void)setText:(NSString *)text [_backedTextInput setSelectedTextRange:[_backedTextInput textRangeFromPosition:position toPosition:position] notifyDelegate:YES]; } +#else + NSRange selection = _backedTextInput.currentEditor.selectedRange; + NSInteger oldTextLength = _backedTextInput.text.length; + + _backedTextInput.text = text; + + if (selection.length == 0) { + // maintain cursor position relative to the end of the old text + NSInteger offsetStart = selection.location; + NSInteger offsetFromEnd = oldTextLength - offsetStart; + NSInteger newOffset = MAX(0, text.length - offsetFromEnd); + [_backedTextInput setSelectedTextRange:NSMakeRange(newOffset, 0) + notifyDelegate:YES]; + } +#endif } else if (eventLag > RCTTextUpdateLagWarningThreshold) { RCTLogWarn(@"Native TextInput(%@) is %lld events ahead of JS - try to make your JS faster.", _backedTextInput.text, (long long)eventLag); } } +#if 0 // TODO(tomun): refactored away by facebook, integrate into new classes + +#pragma mark - Events + +- (void)textFieldDidChange +{ + _nativeEventCount++; + [_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange + reactTag:self.reactTag + text:_textField.text + key:nil + eventCount:_nativeEventCount]; +#if !TARGET_OS_OSX + // selectedTextRange observer isn't triggered when you type even though the + // cursor position moves, so we send event again here. + [self sendSelectionEvent]; +#endif +} + +- (void)textFieldEndEditing +{ + if (![_finalText isEqualToString:_textField.text]) { + _finalText = nil; + // iOS does't send event `UIControlEventEditingChanged` if the change was happened because of autocorrection + // which was triggered by loosing focus. We assume that if `text` was changed in the middle of loosing focus process, + // we did not receive that event. So, we call `textFieldDidChange` manually. + [self textFieldDidChange]; + } +} + +- (void)textFieldSubmitEditing +{ + _submitted = YES; + [_eventDispatcher sendTextEventWithType:RCTTextEventTypeSubmit + reactTag:self.reactTag + text:_textField.text + key:nil + eventCount:_nativeEventCount]; + +#if TARGET_OS_OSX + if (_blurOnSubmit) { + [self.window makeFirstResponder:nil]; + } +#endif +} + +- (void)textFieldBeginEditing +{ + [_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus + reactTag:self.reactTag + text:_textField.text + key:nil + eventCount:_nativeEventCount]; + + dispatch_async(dispatch_get_main_queue(), ^{ +#if !TARGET_OS_OSX + if (self->_selectTextOnFocus) { + [self->_textField selectAll:nil]; + } +#endif + + [self sendSelectionEvent]; + }); +} + +- (void)textFieldDidChangeSelection +{ + [self sendSelectionEvent]; +} + +#if !TARGET_OS_OSX +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(__unused UITextField *)textField + change:(__unused NSDictionary *)change + context:(__unused void *)context +{ + if ([keyPath isEqualToString:@"selectedTextRange"]) { + [self textFieldDidChangeSelection]; + } +} +#endif + +#endif // TODO(tomun): refactored away by facebook, integrate into new classes + #pragma mark - RCTBackedTextInputDelegate - (BOOL)textInputShouldChangeTextInRange:(NSRange)range replacementText:(NSString *)string @@ -116,10 +234,15 @@ - (BOOL)textInputShouldChangeTextInRange:(NSRange)range replacementText:(NSStrin _backedTextInput.text = newString; // Collapse selection at end of insert to match normal paste behavior. +#if !TARGET_OS_OSX UITextPosition *insertEnd = [_backedTextInput positionFromPosition:_backedTextInput.beginningOfDocument offset:(range.location + allowedLength)]; [_backedTextInput setSelectedTextRange:[_backedTextInput textRangeFromPosition:insertEnd toPosition:insertEnd] notifyDelegate:YES]; +#else + [_backedTextInput setSelectedTextRange:NSMakeRange(range.location + allowedLength, 0) + notifyDelegate:YES]; +#endif [self textInputDidChange]; } return NO; @@ -139,4 +262,16 @@ - (void)textInputDidChange eventCount:_nativeEventCount]; } +#if TARGET_OS_OSX + +#pragma mark - NSResponder chain + +- (BOOL)canBecomeKeyView +{ + return NO; // Enclosed _textField can become the key view +} + +#endif // TARGET_OS_OSX + + @end diff --git a/Libraries/Text/RCTTextFieldManager.m b/Libraries/Text/RCTTextFieldManager.m index 7e0d1dc2492927..b629b5361539ba 100644 --- a/Libraries/Text/RCTTextFieldManager.m +++ b/Libraries/Text/RCTTextFieldManager.m @@ -14,7 +14,9 @@ #import #import +#if !TARGET_OS_OSX #import "RCTConvert+Text.h" +#endif #import "RCTShadowTextField.h" #import "RCTTextField.h" #import "RCTUITextField.h" @@ -36,35 +38,39 @@ - (UIView *)view #pragma mark - Unified properties RCT_REMAP_VIEW_PROPERTY(allowFontScaling, fontAttributes.allowFontScaling, BOOL) -RCT_REMAP_VIEW_PROPERTY(autoCapitalize, backedTextInputView.autocapitalizationType, UITextAutocapitalizationType) -RCT_REMAP_VIEW_PROPERTY(autoCorrect, backedTextInputView.autocorrectionType, UITextAutocorrectionType) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(autoCapitalize, backedTextInputView.autocapitalizationType, UITextAutocapitalizationType) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(autoCorrect, backedTextInputView.autocorrectionType, UITextAutocorrectionType) +RCT_REMAP_OSX_VIEW_PROPERTY(autoCorrect, backedTextInputView.automaticTextReplacementEnabled, BOOL) + RCT_REMAP_VIEW_PROPERTY(color, backedTextInputView.textColor, UIColor) RCT_REMAP_VIEW_PROPERTY(editable, backedTextInputView.editable, BOOL) -RCT_REMAP_VIEW_PROPERTY(enablesReturnKeyAutomatically, backedTextInputView.enablesReturnKeyAutomatically, BOOL) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(enablesReturnKeyAutomatically, backedTextInputView.enablesReturnKeyAutomatically, BOOL) RCT_REMAP_VIEW_PROPERTY(fontSize, fontAttributes.fontSize, NSNumber) RCT_REMAP_VIEW_PROPERTY(fontWeight, fontAttributes.fontWeight, NSString) RCT_REMAP_VIEW_PROPERTY(fontStyle, fontAttributes.fontStyle, NSString) RCT_REMAP_VIEW_PROPERTY(fontFamily, fontAttributes.fontFamily, NSString) -RCT_REMAP_VIEW_PROPERTY(keyboardAppearance, backedTextInputView.keyboardAppearance, UIKeyboardAppearance) -RCT_REMAP_VIEW_PROPERTY(keyboardType, backedTextInputView.keyboardType, UIKeyboardType) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(keyboardAppearance, backedTextInputView.keyboardAppearance, UIKeyboardAppearance) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(keyboardType, backedTextInputView.keyboardType, UIKeyboardType) RCT_REMAP_VIEW_PROPERTY(placeholder, backedTextInputView.placeholder, NSString) RCT_REMAP_VIEW_PROPERTY(placeholderTextColor, backedTextInputView.placeholderColor, UIColor) -RCT_REMAP_VIEW_PROPERTY(returnKeyType, backedTextInputView.returnKeyType, UIReturnKeyType) -RCT_REMAP_VIEW_PROPERTY(secureTextEntry, backedTextInputView.secureTextEntry, BOOL) -RCT_REMAP_VIEW_PROPERTY(selectionColor, backedTextInputView.tintColor, UIColor) -RCT_REMAP_VIEW_PROPERTY(spellCheck, backedTextInputView.spellCheckingType, UITextSpellCheckingType) -RCT_REMAP_VIEW_PROPERTY(textAlign, backedTextInputView.textAlignment, NSTextAlignment) -RCT_EXPORT_VIEW_PROPERTY(blurOnSubmit, BOOL) -RCT_EXPORT_VIEW_PROPERTY(clearTextOnFocus, BOOL) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(returnKeyType, backedTextInputView.returnKeyType, UIReturnKeyType) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(secureTextEntry, backedTextInputView.secureTextEntry, BOOL) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(selectionColor, backedTextInputView.tintColor, UIColor) +RCT_REMAP_OSX_VIEW_PROPERTY(selectionColor, backedTextInputView.selectionColor, UIColor) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(spellCheck, backedTextInputView.spellCheckingType, UITextSpellCheckingType) +RCT_REMAP_OSX_VIEW_PROPERTY(spellCheck, backedTextInputView.automaticSpellingCorrectionEnabled, BOOL) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(textAlign, backedTextInputView.textAlignment, NSTextAlignment) +RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(blurOnSubmit, BOOL) +RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(clearTextOnFocus, BOOL) RCT_EXPORT_VIEW_PROPERTY(maxLength, NSNumber) -RCT_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL) +RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(selectTextOnFocus, BOOL) RCT_EXPORT_VIEW_PROPERTY(selection, RCTTextSelection) RCT_EXPORT_VIEW_PROPERTY(text, NSString) #pragma mark - Singleline (aka TextField) specific properties -RCT_REMAP_VIEW_PROPERTY(caretHidden, backedTextInputView.caretHidden, BOOL) -RCT_REMAP_VIEW_PROPERTY(clearButtonMode, backedTextInputView.clearButtonMode, UITextFieldViewMode) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(caretHidden, backedTextInputView.caretHidden, BOOL) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(clearButtonMode, backedTextInputView.clearButtonMode, UITextFieldViewMode) RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) diff --git a/Libraries/Text/RCTTextInput.h b/Libraries/Text/RCTTextInput.h index 47fcf4263af8b1..6c3bbdce31e601 100644 --- a/Libraries/Text/RCTTextInput.h +++ b/Libraries/Text/RCTTextInput.h @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import #import @@ -47,15 +47,17 @@ @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, assign) BOOL blurOnSubmit; +#if !TARGET_OS_OSX @property (nonatomic, assign) BOOL selectTextOnFocus; @property (nonatomic, assign) BOOL clearTextOnFocus; +#endif @property (nonatomic, copy) RCTTextSelection *selection; - (void)setFont:(UIFont *)font; - (void)invalidateContentSize; -// Temporary exposure of particial `RCTBackedTextInputDelegate` support. +// Temporary exposure of partial `RCTBackedTextInputDelegate` support. // In the future all methods of the protocol should move to this class. - (BOOL)textInputShouldBeginEditing; - (void)textInputDidBeginEditing; @@ -64,5 +66,11 @@ - (void)textInputDidChangeSelection; - (BOOL)textInputShouldEndEditing; - (void)textInputDidEndEditing; +- (BOOL)textInputShouldChangeTextInRange:(NSRange)range replacementText:(NSString *)string; +- (void)textInputDidChange; +- (BOOL)textInputShouldHandleDeleteBackward:(id)sender; +#if TARGET_OS_OSX +- (BOOL)textInputShouldHandleDeleteForward:(id)sender; +#endif @end diff --git a/Libraries/Text/RCTTextInput.m b/Libraries/Text/RCTTextInput.m index 8cf7ff60895cd5..0a79f007788ce0 100644 --- a/Libraries/Text/RCTTextInput.m +++ b/Libraries/Text/RCTTextInput.m @@ -31,7 +31,11 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge if (self = [super initWithFrame:CGRectZero]) { _bridge = bridge; _eventDispatcher = bridge.eventDispatcher; +#if !TARGET_OS_OSX _fontAttributes = [[RCTFontAttributes alloc] initWithAccessibilityManager:bridge.accessibilityManager]; +#else + _fontAttributes = [[RCTFontAttributes alloc] initWithAccessibilityManager:nil]; +#endif _fontAttributes.delegate = self; } @@ -64,25 +68,35 @@ - (void)fontAttributesDidChangeWithFont:(UIFont *)font - (void)setReactPaddingInsets:(UIEdgeInsets)reactPaddingInsets { _reactPaddingInsets = reactPaddingInsets; +#if !TARGET_OS_OSX // We apply `paddingInsets` as `backedTextInputView`'s `textContainerInset`. self.backedTextInputView.textContainerInset = reactPaddingInsets; [self setNeedsLayout]; +#endif } - (void)setReactBorderInsets:(UIEdgeInsets)reactBorderInsets { _reactBorderInsets = reactBorderInsets; +#if !TARGET_OS_OSX // We apply `borderInsets` as `backedTextInputView` layout offset. self.backedTextInputView.frame = UIEdgeInsetsInsetRect(self.bounds, reactBorderInsets); [self setNeedsLayout]; +#endif } - (RCTTextSelection *)selection { id backedTextInput = self.backedTextInputView; +#if !TARGET_OS_OSX UITextRange *selectedTextRange = backedTextInput.selectedTextRange; return [[RCTTextSelection new] initWithStart:[backedTextInput offsetFromPosition:backedTextInput.beginningOfDocument toPosition:selectedTextRange.start] end:[backedTextInput offsetFromPosition:backedTextInput.beginningOfDocument toPosition:selectedTextRange.end]]; +#else + NSRange selectedTextRange = backedTextInput.selectedTextRange; + return [[RCTTextSelection new] initWithStart:selectedTextRange.location + end:selectedTextRange.location + selectedTextRange.length]; +#endif } - (void)setSelection:(RCTTextSelection *)selection @@ -93,13 +107,21 @@ - (void)setSelection:(RCTTextSelection *)selection id backedTextInput = self.backedTextInputView; +#if !TARGET_OS_OSX UITextRange *previousSelectedTextRange = backedTextInput.selectedTextRange; UITextPosition *start = [backedTextInput positionFromPosition:backedTextInput.beginningOfDocument offset:selection.start]; UITextPosition *end = [backedTextInput positionFromPosition:backedTextInput.beginningOfDocument offset:selection.end]; UITextRange *selectedTextRange = [backedTextInput textRangeFromPosition:start toPosition:end]; +#else + NSRange previousSelectedTextRange = backedTextInput.selectedTextRange; + NSInteger start = MIN(selection.start, selection.end); + NSInteger end = MAX(selection.start, selection.end); + NSInteger length = end - selection.start; + NSRange selectedTextRange = NSMakeRange(start, length); +#endif NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; - if (eventLag == 0 && ![previousSelectedTextRange isEqual:selectedTextRange]) { + if (eventLag == 0 && !RCTTextSelectionEqual(previousSelectedTextRange, selectedTextRange)) { [backedTextInput setSelectedTextRange:selectedTextRange notifyDelegate:NO]; } else if (eventLag > RCTTextUpdateLagWarningThreshold) { RCTLogWarn(@"Native TextInput(%@) is %lld events ahead of JS - try to make your JS faster.", backedTextInput.text, (long long)eventLag); @@ -115,6 +137,7 @@ - (BOOL)textInputShouldBeginEditing - (void)textInputDidBeginEditing { +#if !TARGET_OS_OSX if (_clearTextOnFocus) { self.backedTextInputView.text = @""; } @@ -122,6 +145,7 @@ - (void)textInputDidBeginEditing if (_selectTextOnFocus) { [self.backedTextInputView selectAll:nil]; } +#endif [_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus reactTag:self.reactTag @@ -186,6 +210,26 @@ - (void)textInputDidEndEditing eventCount:_nativeEventCount]; } +- (BOOL)textInputShouldChangeTextInRange:(NSRange)range replacementText:(NSString *)string +{ + RCTAssert(NO, @"textInputShouldChangeTextInRange:replacementText: must be overridden in subclasses."); + return NO; +} + +- (void)textInputDidChange +{ + RCTAssert(NO, @"textInputDidChange must be overridden in subclasses."); +} + +- (BOOL)textInputShouldHandleDeleteBackward:(__unused id)sender { + return YES; +} +#if TARGET_OS_OSX +- (BOOL)textInputShouldHandleDeleteForward:(__unused id)sender { + return YES; +} +#endif + #pragma mark - Content Size (in Yoga terms, without any insets) - (CGSize)contentSize @@ -283,6 +327,7 @@ - (void)didMoveToWindow #pragma mark - Custom Input Accessory View +#if !TARGET_OS_OSX - (void)didSetProps:(NSArray *)changedProps { [self invalidateInputAccessoryView]; @@ -342,5 +387,6 @@ - (void)handleInputAccessoryDoneButton [self.backedTextInputView endEditing:YES]; } } +#endif // !TARGET_OS_OSX @end diff --git a/Libraries/Text/RCTTextManager.m b/Libraries/Text/RCTTextManager.m index b26a29d7e0a767..96fbf911a50943 100644 --- a/Libraries/Text/RCTTextManager.m +++ b/Libraries/Text/RCTTextManager.m @@ -9,7 +9,9 @@ #import "RCTTextManager.h" +#if !TARGET_OS_OSX #import +#endif #import #import #import @@ -20,7 +22,6 @@ #import "RCTShadowRawText.h" #import "RCTShadowText.h" #import "RCTText.h" -#import "RCTTextView.h" static void collectDirtyNonTextDescendants(RCTShadowText *shadowView, NSMutableArray *nonTextDescendants) { for (RCTShadowView *child in shadowView.reactSubviews) { @@ -101,7 +102,9 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary +#if !TARGET_OS_OSX +#import + +UIKIT_STATIC_INLINE BOOL RCTTextSelectionEqual(UITextRange *range1, UITextRange *range2) +{ + return [range1 isEqual:range2]; +} + +#else +#import + +NS_INLINE BOOL RCTTextSelectionEqual(NSRange range1, NSRange range2) +{ + return NSEqualRanges(range1, range2); +} + +// +// semantically equivalent constants +// + +// UITextView.h/NSTextView.h +#define UITextViewTextDidChangeNotification NSTextDidChangeNotification +#define UITextFieldTextDidChangeNotification NSControlTextDidChangeNotification + +// +// functionally equivalent types +// + +// These types have the same purpose but may differ semantically. Use with care! + +// UITextField + +#define UITextField NSTextField +#define UITextFieldDelegate NSTextFieldDelegate + +// UITextView +#define UITextView NSTextView +#define UITextViewDelegate NSTextViewDelegate + +#endif diff --git a/Libraries/Text/RCTTextView.h b/Libraries/Text/RCTTextView.h index 09c67faba6adaf..4a95170242d859 100644 --- a/Libraries/Text/RCTTextView.h +++ b/Libraries/Text/RCTTextView.h @@ -7,21 +7,26 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import #import #import +#import "RCTTextUIKit.h" +#import "RCTUITextView.h" #import "RCTTextInput.h" @class RCTBridge; @interface RCTTextView : RCTTextInput +#if !TARGET_OS_OSX @property (nonatomic, assign) UITextAutocorrectionType autocorrectionType; @property (nonatomic, assign) UITextSpellCheckingType spellCheckingType; +#endif @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; @property (nonatomic, copy) NSString *text; +@property (nonatomic, copy) NSString *predictedText; @property (nonatomic, strong) UIColor *placeholderTextColor; @property (nonatomic, copy) NSString *placeholder; @property (nonatomic, strong) UIFont *font; diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index f3f19067c0bc49..4d557ee5477f07 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -19,7 +19,6 @@ #import "RCTShadowText.h" #import "RCTText.h" #import "RCTTextSelection.h" -#import "RCTUITextView.h" @interface RCTTextView () @@ -27,12 +26,13 @@ @interface RCTTextView () @implementation RCTTextView { +#if TARGET_OS_OSX + UIScrollView *_scrollView; +#endif RCTUITextView *_backedTextInput; RCTText *_richTextView; NSAttributedString *_pendingAttributedText; - NSString *_predictedText; - BOOL _blockTextShouldChange; BOOL _nativeUpdatesInFlight; } @@ -51,18 +51,56 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge _backedTextInput.textColor = [UIColor blackColor]; // This line actually removes 5pt (default value) left and right padding in UITextView. _backedTextInput.textContainer.lineFragmentPadding = 0; +#if !TARGET_OS_OSX #if !TARGET_OS_TV _backedTextInput.scrollsToTop = NO; #endif _backedTextInput.scrollEnabled = YES; +#else + _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds]; + _scrollView.backgroundColor = [UIColor clearColor]; + _scrollView.drawsBackground = NO; + _scrollView.borderType = NSNoBorder; + _scrollView.hasHorizontalRuler = NO; + _scrollView.hasVerticalRuler = NO; + _scrollView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + + _backedTextInput.verticallyResizable = YES; + _backedTextInput.horizontallyResizable = YES; + _backedTextInput.textContainer.containerSize = NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX); + _backedTextInput.textContainer.widthTracksTextView = YES; +#endif +#if !TARGET_OS_OSX + _backedTextInput.scrollEnabled = YES; +#endif _backedTextInput.textInputDelegate = self; _backedTextInput.font = self.fontAttributes.font; +#if !TARGET_OS_OSX [self addSubview:_backedTextInput]; +#else + _scrollView.documentView = _backedTextInput; + _scrollView.contentView.postsBoundsChangedNotifications = YES; + [self addSubview:_scrollView]; + + // a register for those notifications on the content view. + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(boundDidChange:) + name:NSViewBoundsDidChangeNotification + object:_scrollView.contentView]; + +#endif } return self; } +#if TARGET_OS_OSX +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} +#endif + RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) @@ -172,13 +210,18 @@ - (void)performPendingTextUpdate // we temporarily block all textShouldChange events so they are not applied. _blockTextShouldChange = YES; +#if !TARGET_OS_OSX UITextRange *selection = _backedTextInput.selectedTextRange; +#else + NSRange selection = _backedTextInput.selectedRange; +#endif NSInteger oldTextLength = _backedTextInput.attributedText.length; _backedTextInput.attributedText = _pendingAttributedText; - _predictedText = _pendingAttributedText.string; + [self setPredictedText:_pendingAttributedText.string]; _pendingAttributedText = nil; - + +#if !TARGET_OS_OSX if (selection.empty) { // maintain cursor position relative to the end of the old text NSInteger start = [_backedTextInput offsetFromPosition:_backedTextInput.beginningOfDocument toPosition:selection.start]; @@ -188,8 +231,19 @@ - (void)performPendingTextUpdate [_backedTextInput setSelectedTextRange:[_backedTextInput textRangeFromPosition:position toPosition:position] notifyDelegate:YES]; } - + [_backedTextInput layoutIfNeeded]; +#else + if (selection.length == 0) { + // maintain cursor position relative to the end of the old text + NSInteger start = selection.location; + NSInteger offsetFromEnd = oldTextLength - start; + NSInteger newOffset = MAX(0, _backedTextInput.attributedText.length - offsetFromEnd); + [_backedTextInput setSelectedTextRange:NSMakeRange(newOffset, 0) + notifyDelegate:YES]; + } + [_backedTextInput layoutSubtreeIfNeeded]; +#endif [self invalidateContentSize]; @@ -209,6 +263,24 @@ - (void)setFont:(UIFont *)font [self setNeedsLayout]; } +#if TARGET_OS_OSX +- (void)setReactPaddingInsets:(UIEdgeInsets)reactPaddingInsets +{ + [super setReactPaddingInsets:reactPaddingInsets]; + // We apply `paddingInsets` as `backedTextInputView`'s `textContainerInsets` on mac. + ((RCTUITextView*)self.backedTextInputView).textContainerInsets = reactPaddingInsets; + [self setNeedsLayout]; +} + +- (void)setReactBorderInsets:(UIEdgeInsets)reactBorderInsets +{ + [super setReactBorderInsets:reactBorderInsets]; + // We apply `borderInsets` as `_scrollView` layout offset on mac. + _scrollView.frame = UIEdgeInsetsInsetRect(self.frame, reactBorderInsets); + [self setNeedsLayout]; +} +#endif // TARGET_OS_OSX + - (NSString *)text { return _backedTextInput.text; @@ -218,12 +290,16 @@ - (void)setText:(NSString *)text { NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; if (eventLag == 0 && ![text isEqualToString:_backedTextInput.text]) { +#if !TARGET_OS_OSX UITextRange *selection = _backedTextInput.selectedTextRange; +#else + NSRange selection = _backedTextInput.selectedRange; +#endif NSInteger oldTextLength = _backedTextInput.text.length; - _predictedText = text; + [self setPredictedText:text]; _backedTextInput.text = text; - +#if !TARGET_OS_OSX if (selection.empty) { // maintain cursor position relative to the end of the old text NSInteger start = [_backedTextInput offsetFromPosition:_backedTextInput.beginningOfDocument toPosition:selection.start]; @@ -233,6 +309,16 @@ - (void)setText:(NSString *)text [_backedTextInput setSelectedTextRange:[_backedTextInput textRangeFromPosition:position toPosition:position] notifyDelegate:YES]; } +#else + if (selection.length == 0) { + // maintain cursor position relative to the end of the old text + NSInteger start = selection.location; + NSInteger offsetFromEnd = oldTextLength - start; + NSInteger newOffset = _backedTextInput.attributedText.length - offsetFromEnd; + [_backedTextInput setSelectedTextRange:NSMakeRange(newOffset, 0) + notifyDelegate:YES]; + } +#endif [self invalidateContentSize]; } else if (eventLag > RCTTextUpdateLagWarningThreshold) { @@ -269,13 +355,18 @@ - (BOOL)textInputShouldChangeTextInRange:(NSRange)range replacementText:(NSStrin NSMutableString *newString = _backedTextInput.text.mutableCopy; [newString replaceCharactersInRange:range withString:limitedString]; _backedTextInput.text = newString; - _predictedText = newString; + [self setPredictedText:newString]; // Collapse selection at end of insert to match normal paste behavior +#if !TARGET_OS_OSX UITextPosition *insertEnd = [_backedTextInput positionFromPosition:_backedTextInput.beginningOfDocument offset:(range.location + allowedLength)]; [_backedTextInput setSelectedTextRange:[_backedTextInput textRangeFromPosition:insertEnd toPosition:insertEnd] notifyDelegate:YES]; +#else + [_backedTextInput setSelectedTextRange:NSMakeRange(range.location + allowedLength, 0) + notifyDelegate:YES]; +#endif [self textInputDidChange]; } @@ -285,17 +376,18 @@ - (BOOL)textInputShouldChangeTextInRange:(NSRange)range replacementText:(NSStrin _nativeUpdatesInFlight = YES; - if (range.location + range.length > _predictedText.length) { - // _predictedText got out of sync in a bad way, so let's just force sync it. Haven't been able to repro this, but + if (range.location + range.length > [[self predictedText] length]) { + // predictedText got out of sync in a bad way, so let's just force sync it. Haven't been able to repro this, but // it's causing a real crash here: #6523822 - _predictedText = _backedTextInput.text; + [self setPredictedText:_backedTextInput.text]; + } - - NSString *previousText = [_predictedText substringWithRange:range]; - if (_predictedText) { - _predictedText = [_predictedText stringByReplacingCharactersInRange:range withString:text]; + NSString *predictedText = [self predictedText]; + NSString *previousText = [predictedText substringWithRange:range]; + if (predictedText) { + [self setPredictedText:[predictedText stringByReplacingCharactersInRange:range withString:text]]; } else { - _predictedText = text; + [self setPredictedText:text]; } if (_onTextInput) { @@ -352,12 +444,12 @@ - (void)textInputDidChange // update the mismatched range. NSRange currentRange; NSRange predictionRange; - if (findMismatch(_backedTextInput.text, _predictedText, ¤tRange, &predictionRange)) { + if (findMismatch(_backedTextInput.text, [self predictedText], ¤tRange, &predictionRange)) { NSString *replacement = [_backedTextInput.text substringWithRange:currentRange]; [self textInputShouldChangeTextInRange:predictionRange replacementText:replacement]; // JS will assume the selection changed based on the location of our shouldChangeTextInRange, so reset it. [self textInputDidChangeSelection]; - _predictedText = _backedTextInput.text; + [self setPredictedText:_backedTextInput.text]; } _nativeUpdatesInFlight = NO; @@ -407,5 +499,23 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView }); } } + +#if TARGET_OS_OSX + +#pragma mark - Notification handling + +- (void)boundDidChange:(NSNotification*)NSNotification +{ + [self scrollViewDidScroll:_scrollView]; +} + +#pragma mark - NSResponder chain + +- (BOOL)acceptsFirstResponder +{ + return _backedTextInput.acceptsFirstResponder; +} + +#endif @end diff --git a/Libraries/Text/RCTTextViewManager.m b/Libraries/Text/RCTTextViewManager.m index 814f2070f7b4b6..199c30096219d7 100644 --- a/Libraries/Text/RCTTextViewManager.m +++ b/Libraries/Text/RCTTextViewManager.m @@ -15,7 +15,9 @@ #import #import +#if !TARGET_OS_OSX #import "RCTConvert+Text.h" +#endif #import "RCTShadowTextView.h" #import "RCTTextView.h" @@ -36,28 +38,29 @@ - (UIView *)view #pragma mark - Unified properties RCT_REMAP_VIEW_PROPERTY(allowFontScaling, fontAttributes.allowFontScaling, BOOL) -RCT_REMAP_VIEW_PROPERTY(autoCapitalize, backedTextInputView.autocapitalizationType, UITextAutocapitalizationType) -RCT_REMAP_VIEW_PROPERTY(autoCorrect, backedTextInputView.autocorrectionType, UITextAutocorrectionType) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(autoCapitalize, backedTextInputView.autocapitalizationType, UITextAutocapitalizationType) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(autoCorrect, backedTextInputView.autocorrectionType, UITextAutocorrectionType) RCT_REMAP_VIEW_PROPERTY(color, backedTextInputView.textColor, UIColor) RCT_REMAP_VIEW_PROPERTY(editable, backedTextInputView.editable, BOOL) -RCT_REMAP_VIEW_PROPERTY(enablesReturnKeyAutomatically, backedTextInputView.enablesReturnKeyAutomatically, BOOL) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(enablesReturnKeyAutomatically, backedTextInputView.enablesReturnKeyAutomatically, BOOL) RCT_REMAP_VIEW_PROPERTY(fontSize, fontAttributes.fontSize, NSNumber) RCT_REMAP_VIEW_PROPERTY(fontWeight, fontAttributes.fontWeight, NSString) RCT_REMAP_VIEW_PROPERTY(fontStyle, fontAttributes.fontStyle, NSString) RCT_REMAP_VIEW_PROPERTY(fontFamily, fontAttributes.fontFamily, NSString) -RCT_REMAP_VIEW_PROPERTY(keyboardAppearance, backedTextInputView.keyboardAppearance, UIKeyboardAppearance) -RCT_REMAP_VIEW_PROPERTY(keyboardType, backedTextInputView.keyboardType, UIKeyboardType) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(keyboardAppearance, backedTextInputView.keyboardAppearance, UIKeyboardAppearance) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(keyboardType, backedTextInputView.keyboardType, UIKeyboardType) RCT_REMAP_VIEW_PROPERTY(placeholder, backedTextInputView.placeholder, NSString) RCT_REMAP_VIEW_PROPERTY(placeholderTextColor, backedTextInputView.placeholderColor, UIColor) -RCT_REMAP_VIEW_PROPERTY(returnKeyType, backedTextInputView.returnKeyType, UIReturnKeyType) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(returnKeyType, backedTextInputView.returnKeyType, UIReturnKeyType) RCT_REMAP_VIEW_PROPERTY(secureTextEntry, backedTextInputView.secureTextEntry, BOOL) -RCT_REMAP_VIEW_PROPERTY(selectionColor, backedTextInputView.tintColor, UIColor) -RCT_REMAP_VIEW_PROPERTY(spellCheck, backedTextInputView.spellCheckingType, UITextSpellCheckingType) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(selectionColor, backedTextInputView.tintColor, UIColor) +RCT_REMAP_OSX_VIEW_PROPERTY(selectionColor, backedTextInputView.selectionColor, UIColor) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(spellCheck, backedTextInputView.spellCheckingType, UITextSpellCheckingType) RCT_REMAP_VIEW_PROPERTY(textAlign, backedTextInputView.textAlignment, NSTextAlignment) RCT_EXPORT_VIEW_PROPERTY(blurOnSubmit, BOOL) -RCT_EXPORT_VIEW_PROPERTY(clearTextOnFocus, BOOL) +RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(clearTextOnFocus, BOOL) RCT_EXPORT_VIEW_PROPERTY(maxLength, NSNumber) -RCT_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL) +RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(selectTextOnFocus, BOOL) RCT_EXPORT_VIEW_PROPERTY(selection, RCTTextSelection) RCT_EXPORT_VIEW_PROPERTY(text, NSString) @@ -72,7 +75,8 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) #if !TARGET_OS_TV -RCT_REMAP_VIEW_PROPERTY(dataDetectorTypes, backedTextInputView.dataDetectorTypes, UIDataDetectorTypes) +RCT_REMAP_NOT_OSX_VIEW_PROPERTY(dataDetectorTypes, backedTextInputView.dataDetectorTypes, UIDataDetectorTypes) +RCT_REMAP_OSX_VIEW_PROPERTY(dataDetectorTypes, backedTextInputView.enabledTextCheckingTypes, NSTextCheckingTypes) #endif - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView diff --git a/Libraries/Text/RCTUITextField.h b/Libraries/Text/RCTUITextField.h index 27ae073006f3f8..6257dc5e3e7083 100644 --- a/Libraries/Text/RCTUITextField.h +++ b/Libraries/Text/RCTUITextField.h @@ -7,7 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import + +#import "RCTTextUIKit.h" #import "RCTBackedTextInputViewProtocol.h" @@ -23,10 +25,25 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, weak) id textInputDelegate; @property (nonatomic, assign) BOOL caretHidden; -@property (nonatomic, assign, readonly) BOOL textWasPasted; +#if !TARGET_OS_OSX +@property (nonatomic, readonly) BOOL textWasPasted; +#else +@property (nonatomic, assign) BOOL textWasPasted; +#endif @property (nonatomic, strong, nullable) UIColor *placeholderColor; @property (nonatomic, assign) UIEdgeInsets textContainerInset; +#if !TARGET_OS_OSX @property (nonatomic, assign, getter=isEditable) BOOL editable; +#else +@property (assign, getter=isEditable) BOOL editable; +#endif +#if TARGET_OS_OSX +@property (nonatomic, copy, nullable) NSString *text; +@property (nonatomic, getter=isAutomaticTextReplacementEnabled) BOOL automaticTextReplacementEnabled; +@property (nonatomic, getter=isAutomaticSpellingCorrectionEnabled) BOOL automaticSpellingCorrectionEnabled; +@property (nonatomic, strong, nullable) UIColor *selectionColor; +@property (weak, nullable) id delegate; +#endif // TARGET_OS_OSX @end diff --git a/Libraries/Text/RCTUITextField.m b/Libraries/Text/RCTUITextField.m index 4b21e594b1bfd0..78cb5fe073cd12 100644 --- a/Libraries/Text/RCTUITextField.m +++ b/Libraries/Text/RCTUITextField.m @@ -11,21 +11,104 @@ #import #import - #import "RCTBackedTextInputDelegateAdapter.h" +#if TARGET_OS_OSX + +@interface RCTUITextFieldCell : NSTextFieldCell + +@property (nonatomic, assign) UIEdgeInsets textContainerInset; +@property (nonatomic, getter=isAutomaticTextReplacementEnabled) BOOL automaticTextReplacementEnabled; +@property (nonatomic, getter=isAutomaticSpellingCorrectionEnabled) BOOL automaticSpellingCorrectionEnabled; +@property (nonatomic, strong, nullable) UIColor *selectionColor; + +@end + +@implementation RCTUITextFieldCell + +- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset +{ + _textContainerInset = textContainerInset; +} + +- (NSRect)titleRectForBounds:(NSRect)rect +{ + return UIEdgeInsetsInsetRect([super titleRectForBounds:rect], self.textContainerInset); +} + +- (void)editWithFrame:(NSRect)rect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)delegate event:(NSEvent *)event +{ + [super editWithFrame:[self titleRectForBounds:rect] inView:controlView editor:textObj delegate:delegate event:event]; +} + +- (void)selectWithFrame:(NSRect)rect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)delegate start:(NSInteger)selStart length:(NSInteger)selLength +{ + [super selectWithFrame:[self titleRectForBounds:rect] inView:controlView editor:textObj delegate:delegate start:selStart length:selLength]; +} + +- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView +{ + if (self.drawsBackground) { + if (self.backgroundColor && self.backgroundColor.alphaComponent > 0) { + + [self.backgroundColor set]; + NSRectFill(cellFrame); + } + } + + [super drawInteriorWithFrame:[self titleRectForBounds:cellFrame] inView:controlView]; +} + +- (NSText *)setUpFieldEditorAttributes:(NSText *)textObj +{ + NSTextView *fieldEditor = (NSTextView *)[super setUpFieldEditorAttributes:textObj]; + fieldEditor.automaticSpellingCorrectionEnabled = self.isAutomaticSpellingCorrectionEnabled; + fieldEditor.automaticTextReplacementEnabled = self.isAutomaticTextReplacementEnabled; + NSMutableDictionary *selectTextAttributes = fieldEditor.selectedTextAttributes.mutableCopy; + selectTextAttributes[NSBackgroundColorAttributeName] = self.selectionColor ?: [NSColor selectedControlColor]; + fieldEditor.selectedTextAttributes = selectTextAttributes; + fieldEditor.insertionPointColor = self.selectionColor ?: [NSColor selectedControlColor]; + return fieldEditor; +} + +@end + +#endif // TARGET_OS_OSX + @implementation RCTUITextField { RCTBackedTextFieldDelegateAdapter *_textInputDelegateAdapter; } +#if TARGET_OS_OSX +@dynamic delegate; + +static UIFont *defaultPlaceholderFont() +{ + return [UIFont systemFontOfSize:17]; +} + +static UIColor *defaultPlaceholderTextColor() +{ + // Default placeholder color from UITextField. + return [UIColor colorWithRed:0 green:0 blue:0.0980392 alpha:0.22]; +} + +#endif + - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_textDidChange) name:UITextFieldTextDidChangeNotification object:self]; +#if TARGET_OS_OSX + self.bordered = NO; + self.accessibilityRole = NSAccessibilityTextFieldRole; +#endif + _textInputDelegateAdapter = [[RCTBackedTextFieldDelegateAdapter alloc] initWithTextField:self]; } @@ -47,20 +130,96 @@ - (void)_textDidChange - (void)setTextContainerInset:(UIEdgeInsets)textContainerInset { _textContainerInset = textContainerInset; +#if !TARGET_OS_OSX [self setNeedsLayout]; +#else + ((RCTUITextFieldCell*)self.cell).textContainerInset = _textContainerInset; +#endif } -- (void)setPlaceholder:(NSString *)placeholder +#if TARGET_OS_OSX + ++ (Class)cellClass { - [super setPlaceholder:placeholder]; - [self _updatePlaceholder]; + return RCTUITextFieldCell.class; +} + +- (void)setText:(NSString *)text +{ + self.stringValue = text; +} + +- (NSString*)text +{ + return self.stringValue; +} + +- (void)setAutomaticTextReplacementEnabled:(BOOL)automaticTextReplacementEnabled +{ + ((RCTUITextFieldCell*)self.cell).automaticTextReplacementEnabled = automaticTextReplacementEnabled; +} + +- (BOOL)isAutomaticTextReplacementEnabled +{ + return ((RCTUITextFieldCell*)self.cell).isAutomaticTextReplacementEnabled; +} + +- (void)setAutomaticSpellingCorrectionEnabled:(BOOL)automaticSpellingCorrectionEnabled +{ + ((RCTUITextFieldCell*)self.cell).automaticSpellingCorrectionEnabled = automaticSpellingCorrectionEnabled; } +- (BOOL)isAutomaticSpellingCorrectionEnabled +{ + return ((RCTUITextFieldCell*)self.cell).isAutomaticSpellingCorrectionEnabled; +} + +- (void)setSelectionColor:(UIColor *)selectionColor +{ + ((RCTUITextFieldCell*)self.cell).selectionColor = selectionColor; +} + +- (UIColor*)selectionColor +{ + return ((RCTUITextFieldCell*)self.cell).selectionColor; +} + +- (void)setFont:(UIFont *)font +{ + ((RCTUITextFieldCell*)self.cell).font = font; +} + +- (UIFont *)font +{ + return ((RCTUITextFieldCell*)self.cell).font; +} + +#endif // TARGET_OS_OSX + +- (void)setPlaceholder:(NSString *)placeholderString +{ +#if !TARGET_OS_OSX + [super setPlaceholder:placeholderString]; +#else + [super setPlaceholderString:placeholderString]; +#endif + [self _updatePlaceholder]; +} + - (void)setPlaceholderColor:(UIColor *)placeholderColor { _placeholderColor = placeholderColor; [self _updatePlaceholder]; } + +- (NSString*)placeholder +{ +#if !TARGET_OS_OSX + return super.placeholder; +#else + return self.placeholderAttributedString.string ?: self.placeholderString; +#endif +} - (void)_updatePlaceholder { @@ -69,12 +228,21 @@ - (void)_updatePlaceholder } NSMutableDictionary *attributes = [NSMutableDictionary new]; + +#if !TARGET_OS_OSX if (_placeholderColor) { [attributes setObject:_placeholderColor forKey:NSForegroundColorAttributeName]; } self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:attributes]; +#else + attributes[NSForegroundColorAttributeName] = _placeholderColor ?: defaultPlaceholderTextColor(); + attributes[NSFontAttributeName] = self.font ?: defaultPlaceholderFont(); + + self.placeholderAttributedString = [[NSAttributedString alloc] initWithString:self.placeholder + attributes:attributes]; +#endif } - (BOOL)isEditable @@ -84,9 +252,15 @@ - (BOOL)isEditable - (void)setEditable:(BOOL)editable { +#if TARGET_OS_OSX + // on macos the super must be called otherwise its NSTextFieldCell editable property doesn't get set. + [super setEditable:editable]; +#endif self.enabled = editable; } +#if !TARGET_OS_OSX + #pragma mark - Caret Manipulation - (CGRect)caretRectForPosition:(UITextPosition *)position @@ -109,9 +283,75 @@ - (CGRect)editingRectForBounds:(CGRect)bounds { return [self textRectForBounds:bounds]; } + +#else + +#pragma mark - NSTextViewDelegate methods + +- (void)textDidBeginEditing:(NSNotification *)notification +{ + [super textDidBeginEditing:notification]; + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(textFieldBeginEditing:)]) { + [delegate textFieldBeginEditing:self]; + } +} + +- (void)textDidChange:(NSNotification *)notification +{ + [super textDidChange:notification]; + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(textFieldDidChange:)]) { + [delegate textFieldDidChange:self]; + } +} + +- (void)textDidEndEditing:(NSNotification *)notification +{ + [super textDidEndEditing:notification]; + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(textFieldEndEditing:)]) { + [delegate textFieldEndEditing:self]; + } +} + +- (void)textViewDidChangeSelection:(NSNotification *)notification +{ + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(textFieldDidChangeSelection:)]) { + [delegate textFieldDidChangeSelection:self]; + } +} + +- (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)aRange replacementString:(NSString *)aString +{ + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) { + return [delegate textField:self shouldChangeCharactersInRange:aRange replacementString:aString]; + } + return NO; +} + +#endif #pragma mark - Overrides +#if TARGET_OS_OSX +- (BOOL)becomeFirstResponder +{ + BOOL isFirstResponder = [super becomeFirstResponder]; + if (isFirstResponder) { + NSScrollView *scrollView = [self enclosingScrollView]; + if (scrollView != nil) { + NSRect visibleRect = [[scrollView documentView] convertRect:self.frame fromView:self]; + [[scrollView documentView] scrollRectToVisible:visibleRect]; + } + } + return isFirstResponder; +} +#endif + +#if !TARGET_OS_OSX - (void)setSelectedTextRange:(UITextRange *)selectedTextRange { [super setSelectedTextRange:selectedTextRange]; @@ -134,6 +374,23 @@ - (void)paste:(id)sender [super paste:sender]; _textWasPasted = YES; } +#else +- (void)setSelectedTextRange:(NSRange)selectedTextRange notifyDelegate:(BOOL)notifyDelegate +{ + if (!notifyDelegate) { + // We have to notify an adapter that following selection change was initiated programmatically, + // so the adapter must not generate a notification for it. + [_textInputDelegateAdapter skipNextTextInputDidChangeSelectionEventWithTextRange:selectedTextRange]; + } + + [[self currentEditor] setSelectedRange:selectedTextRange]; +} + +- (NSRange)selectedTextRange +{ + return [[self currentEditor] selectedRange]; +} +#endif #pragma mark - Layout @@ -148,7 +405,18 @@ - (CGSize)intrinsicContentSize // Note: `placeholder` defines intrinsic size for ``. NSString *text = self.placeholder ?: @""; CGSize size = [text sizeWithAttributes:@{NSFontAttributeName: self.font}]; + +#if !TARGET_OS_OSX size = CGSizeMake(RCTCeilPixelValue(size.width), RCTCeilPixelValue(size.height)); +#else + CGFloat scale = self.window.backingScaleFactor; + // TODO(tomun): a layout pass now happens before the view is in a window + //RCTAssert(scale != 0.0, @"Layout occurs before the view is in a window?"); + if (scale == 0) { + scale = [[NSScreen mainScreen] backingScaleFactor]; + } + size = CGSizeMake(RCTCeilPixelValue(size.width, scale), RCTCeilPixelValue(size.height, scale)); +#endif size.width += _textContainerInset.left + _textContainerInset.right; size.height += _textContainerInset.top + _textContainerInset.bottom; // Returning size DOES contain `textContainerInset` (aka `padding`). @@ -162,4 +430,13 @@ - (CGSize)sizeThatFits:(CGSize)size return CGSizeMake(MIN(size.width, intrinsicSize.width), MIN(size.height, intrinsicSize.height)); } +#if !TARGET_OS_OSX +- (void)deleteBackward { + id textInputDelegate = [self textInputDelegate]; + if ([textInputDelegate textInputShouldHandleDeleteBackward:self]) { + [super deleteBackward]; + } +} +#endif + @end diff --git a/Libraries/Text/RCTUITextView.h b/Libraries/Text/RCTUITextView.h index 47b81c32cacc7d..79961eb2730149 100644 --- a/Libraries/Text/RCTUITextView.h +++ b/Libraries/Text/RCTUITextView.h @@ -7,7 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import + +#import "RCTTextUIKit.h" #import "RCTBackedTextInputViewProtocol.h" @@ -25,12 +27,26 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, weak) id textInputDelegate; +#if !TARGET_OS_OSX @property (nonatomic, assign, readonly) BOOL textWasPasted; +#else +@property (nonatomic, assign) BOOL textWasPasted; +#endif @property (nonatomic, copy, nullable) NSString *placeholder; + @property (nonatomic, strong, nullable) UIColor *placeholderColor; @property (nonatomic, assign) CGFloat preferredMaxLayoutWidth; +#if TARGET_OS_OSX +@property (nonatomic, strong, nullable) UIColor *selectionColor; +@property (nonatomic, assign) UIEdgeInsets textContainerInsets; +@property (nonatomic, copy) NSString *text; +@property (nonatomic, assign) NSTextAlignment textAlignment; +@property (nonatomic, copy, nullable) NSAttributedString *attributedText; +- (NSSize)sizeThatFits:(NSSize)size; +#endif // TARGET_OS_OSX + @end NS_ASSUME_NONNULL_END diff --git a/Libraries/Text/RCTUITextView.m b/Libraries/Text/RCTUITextView.m index 45d3cf6dfe6104..0dfe69d001f27b 100644 --- a/Libraries/Text/RCTUITextView.m +++ b/Libraries/Text/RCTUITextView.m @@ -16,8 +16,10 @@ @implementation RCTUITextView { +#if !TARGET_OS_OSX UILabel *_placeholderView; UITextView *_detachedTextView; +#endif RCTBackedTextViewDelegateAdapter *_textInputDelegateAdapter; } @@ -39,12 +41,16 @@ - (instancetype)initWithFrame:(CGRect)frame selector:@selector(textDidChange) name:UITextViewTextDidChangeNotification object:self]; - +#if !TARGET_OS_OSX _placeholderView = [[UILabel alloc] initWithFrame:self.bounds]; _placeholderView.isAccessibilityElement = NO; _placeholderView.numberOfLines = 0; _placeholderView.textColor = defaultPlaceholderColor(); [self addSubview:_placeholderView]; +#else + NSTextCheckingTypes checkingTypes = 0; + self.enabledTextCheckingTypes = checkingTypes; +#endif _textInputDelegateAdapter = [[RCTBackedTextViewDelegateAdapter alloc] initWithTextView:self]; } @@ -81,15 +87,64 @@ - (NSString *)accessibilityLabel - (void)setPlaceholder:(NSString *)placeholder { _placeholder = placeholder; +#if !TARGET_OS_OSX _placeholderView.text = _placeholder; +#else + [self setNeedsDisplay:YES]; +#endif } - (void)setPlaceholderColor:(UIColor *)placeholderColor { _placeholderColor = placeholderColor; +#if !TARGET_OS_OSX _placeholderView.textColor = _placeholderColor ?: defaultPlaceholderColor(); +#else + [self setNeedsDisplay:YES]; +#endif +} + +#if TARGET_OS_OSX +- (void)setSelectionColor:(UIColor *)selectionColor +{ + NSMutableDictionary *selectTextAttributes = self.selectedTextAttributes.mutableCopy; + selectTextAttributes[NSBackgroundColorAttributeName] = selectionColor ?: [NSColor selectedControlColor]; + self.selectedTextAttributes = selectTextAttributes.copy; +} + +- (UIColor*)selectionColor +{ + return (UIColor*)self.selectedTextAttributes[NSBackgroundColorAttributeName]; +} + +- (void)setEnabledTextCheckingTypes:(NSTextCheckingTypes)checkingType +{ + [super setEnabledTextCheckingTypes:checkingType]; + self.automaticDataDetectionEnabled = checkingType != 0; +} + +- (NSTextAlignment)textAlignment +{ + return self.alignment; } +- (NSString*)text +{ + return self.string; +} + +- (NSAttributedString*)attributedText +{ + return self.textStorage; +} + +- (BOOL)becomeFirstResponder +{ + return [self.window makeFirstResponder:self]; +} + +#endif // TARGET_OS_OSX + - (void)textDidChange { _textWasPasted = NO; @@ -101,30 +156,53 @@ - (void)textDidChange - (void)setFont:(UIFont *)font { [super setFont:font]; +#if !TARGET_OS_OSX _placeholderView.font = font ?: defaultPlaceholderFont(); +#else + [self setNeedsDisplay:YES]; +#endif } - (void)setTextAlignment:(NSTextAlignment)textAlignment { +#if !TARGET_OS_OSX [super setTextAlignment:textAlignment]; _placeholderView.textAlignment = textAlignment; +#else + self.alignment = textAlignment; + [self setNeedsDisplay:YES]; +#endif } - (void)setText:(NSString *)text { +#if !TARGET_OS_OSX [super setText:text]; +#else + self.string = text.copy; +#endif + [self textDidChange]; } - (void)setAttributedText:(NSAttributedString *)attributedText { +#if !TARGET_OS_OSX [super setAttributedText:attributedText]; +#else + [self.textStorage setAttributedString:attributedText]; +#endif + [self textDidChange]; } #pragma mark - Overrides +#if !TARGET_OS_OSX - (void)setSelectedTextRange:(UITextRange *)selectedTextRange notifyDelegate:(BOOL)notifyDelegate +#else +- (void)setSelectedTextRange:(NSRange)selectedTextRange notifyDelegate:(BOOL)notifyDelegate +#endif { if (!notifyDelegate) { // We have to notify an adapter that following selection change was initiated programmatically, @@ -132,21 +210,88 @@ - (void)setSelectedTextRange:(UITextRange *)selectedTextRange notifyDelegate:(BO [_textInputDelegateAdapter skipNextTextInputDidChangeSelectionEventWithTextRange:selectedTextRange]; } +#if !TARGET_OS_OSX [super setSelectedTextRange:selectedTextRange]; +#else + [super setSelectedRange:selectedTextRange]; +#endif } +#if TARGET_OS_OSX +- (NSRange)selectedTextRange +{ + return [super selectedRange]; +} +#endif + - (void)paste:(id)sender { [super paste:sender]; _textWasPasted = YES; } +#if !TARGET_OS_OSX - (void)setContentOffset:(CGPoint)contentOffset animated:(__unused BOOL)animated { // Turning off scroll animation. // This fixes the problem also known as "flaky scrolling". [super setContentOffset:contentOffset animated:NO]; } +#endif + +#if TARGET_OS_OSX + +#pragma mark - Placeholder + +- (NSAttributedString*)placeholderTextAttributedString +{ + if (self.placeholder == nil) { + return nil; + } + NSMutableDictionary *placeholderAttributes = [self.typingAttributes mutableCopy]; + if (placeholderAttributes == nil) { + placeholderAttributes = [NSMutableDictionary dictionary]; + } + placeholderAttributes[NSForegroundColorAttributeName] = self.placeholderColor ?: defaultPlaceholderColor(); + placeholderAttributes[NSFontAttributeName] = self.font ?: defaultPlaceholderFont(); + return [[NSAttributedString alloc] initWithString:self.placeholder attributes:placeholderAttributes]; +} + +- (void)drawRect:(NSRect)dirtyRect +{ + [super drawRect:dirtyRect]; + + if (self.text.length == 0 && self.placeholder) { + NSAttributedString *attributedPlaceholderString = self.placeholderTextAttributedString; + + if (attributedPlaceholderString) { + NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedPlaceholderString]; + NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:self.textContainer.containerSize]; + NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; + + textContainer.lineFragmentPadding = self.textContainer.lineFragmentPadding; + [layoutManager addTextContainer:textContainer]; + [textStorage addLayoutManager:layoutManager]; + + NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; + [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:self.textContainerOrigin]; + } + } +} + +#pragma mark - Text Insets + +- (void)setTextContainerInsets:(UIEdgeInsets)textContainerInsets +{ + // NSTextView has a NSSize textContainerInset property + // UITextview has a UIEdgeInsets textContainerInset property + // RCTUITextView mac only has a UIEdgeInsets textContainerInsets property + // UI/NSTextField do NOT have textContainerInset properties + _textContainerInsets = textContainerInsets; + super.textContainerInset = NSMakeSize(MIN(textContainerInsets.left, textContainerInsets.right), MIN(textContainerInsets.top, textContainerInsets.bottom)); +} + +#endif // TARGET_OS_OSX #pragma mark - Layout @@ -158,10 +303,19 @@ - (CGFloat)preferredMaxLayoutWidth - (CGSize)placeholderSize { +#if !TARGET_OS_OSX UIEdgeInsets textContainerInset = self.textContainerInset; +#else + UIEdgeInsets textContainerInset = self.textContainerInsets; +#endif NSString *placeholder = self.placeholder ?: @""; CGSize placeholderSize = [placeholder sizeWithAttributes:@{NSFontAttributeName: self.font ?: defaultPlaceholderFont()}]; +#if !TARGET_OS_OSX placeholderSize = CGSizeMake(RCTCeilPixelValue(placeholderSize.width), RCTCeilPixelValue(placeholderSize.height)); +#else + CGFloat scale = self.window.backingScaleFactor; + placeholderSize = CGSizeMake(RCTCeilPixelValue(placeholderSize.width, scale), RCTCeilPixelValue(placeholderSize.height, scale)); +#endif placeholderSize.width += textContainerInset.left + textContainerInset.right; placeholderSize.height += textContainerInset.top + textContainerInset.bottom; // Returning size DOES contain `textContainerInset` (aka `padding`; as `sizeThatFits:` does). @@ -170,7 +324,11 @@ - (CGSize)placeholderSize - (CGSize)contentSize { +#if !TARGET_OS_OSX CGSize contentSize = super.contentSize; +#else + CGSize contentSize = super.intrinsicContentSize; +#endif CGSize placeholderSize = self.placeholderSize; // When a text input is empty, it actually displays a placehoder. // So, we have to consider `placeholderSize` as a minimum `contentSize`. @@ -180,6 +338,7 @@ - (CGSize)contentSize MAX(contentSize.height, placeholderSize.height)); } +#if !TARGET_OS_OSX - (void)layoutSubviews { [super layoutSubviews]; @@ -189,6 +348,7 @@ - (void)layoutSubviews textFrame.size.height = MIN(placeholderHeight, textFrame.size.height); _placeholderView.frame = textFrame; } +#endif - (CGSize)intrinsicContentSize { @@ -207,6 +367,7 @@ - (CGSize)sizeThatFits:(CGSize)size - (CGSize)fixedSizeThatFits:(CGSize)size { +#if !TARGET_OS_OSX // UITextView on iOS 8 has a bug that automatically scrolls to the top // when calling `sizeThatFits:`. Use a copy so that self is not screwed up. static BOOL useCustomImplementation = NO; @@ -228,14 +389,32 @@ - (CGSize)fixedSizeThatFits:(CGSize)size _detachedTextView.textContainerInset = self.textContainerInset; return [_detachedTextView sizeThatFits:size]; +#else + (void) [self.layoutManager glyphRangeForTextContainer:self.textContainer]; + NSRect rect = [self.layoutManager usedRectForTextContainer:self.textContainer]; + return CGSizeMake(MIN(rect.size.width, size.width), rect.size.height); +#endif } #pragma mark - Placeholder - (void)invalidatePlaceholderVisibility { +#if !TARGET_OS_OSX BOOL isVisible = _placeholder.length != 0 && self.text.length == 0; _placeholderView.hidden = !isVisible; +#else + [self setNeedsDisplay:YES]; +#endif +} + +#if !TARGET_OS_OSX +- (void)deleteBackward { + id textInputDelegate = [self textInputDelegate]; + if ([textInputDelegate textInputShouldHandleDeleteBackward:self]) { + [super deleteBackward]; + } } +#endif @end diff --git a/Libraries/Text/Text.js b/Libraries/Text/Text.js index 7a2e7311952cc3..350317ef24a32b 100644 --- a/Libraries/Text/Text.js +++ b/Libraries/Text/Text.js @@ -574,7 +574,7 @@ var RCTText = createReactNativeComponentClass( ); var RCTVirtualText = RCTText; -if (Platform.OS === 'android') { +if (Platform.OS === 'android' || Platform.OS === 'win32' || Platform.OS === 'windesktop' || Platform.OS === 'uwp') { RCTVirtualText = createReactNativeComponentClass('RCTVirtualText', () => ({ validAttributes: mergeFast(ReactNativeViewAttributes.UIView, { isHighlighted: true, diff --git a/Libraries/Text/TextStylePropTypes.js b/Libraries/Text/TextStylePropTypes.js index d92708683d981e..ea88dd98e2524c 100644 --- a/Libraries/Text/TextStylePropTypes.js +++ b/Libraries/Text/TextStylePropTypes.js @@ -20,6 +20,11 @@ const TextStylePropTypes = { color: ColorPropType, fontFamily: ReactPropTypes.string, + /** + * Specifies the fully qualified path of the font file. + * @platform android + */ + fontPath: ReactPropTypes.string, fontSize: ReactPropTypes.number, fontStyle: ReactPropTypes.oneOf(['normal', 'italic']), /** diff --git a/Libraries/Utilities/BackHandler.android.js b/Libraries/Utilities/BackHandler.android.js index 21e320c5e457a1..2ce66b87db266f 100644 --- a/Libraries/Utilities/BackHandler.android.js +++ b/Libraries/Utilities/BackHandler.android.js @@ -50,6 +50,8 @@ RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, function() { * * iOS: Not applicable. * + * macOS: Not applicable. + * * The event subscriptions are called in reverse order (i.e. last registered subscription first), * and if one subscription returns true then subscriptions registered earlier will not be called. * diff --git a/Libraries/Utilities/BackHandler.ios.js b/Libraries/Utilities/BackHandler.ios.js index 7c9a00ecc4e8bf..63aa78fddfb68b 100644 --- a/Libraries/Utilities/BackHandler.ios.js +++ b/Libraries/Utilities/BackHandler.ios.js @@ -35,6 +35,8 @@ function emptyFunction() {} * * iOS: Not applicable. * + * macOS: Not applicable. + * * The event subscriptions are called in reverse order (i.e. last registered subscription first), * and if one subscription returns true then subscriptions registered earlier will not be called. * diff --git a/Libraries/Utilities/BackHandler.macos.js b/Libraries/Utilities/BackHandler.macos.js new file mode 100644 index 00000000000000..88d9b7acf8b69f --- /dev/null +++ b/Libraries/Utilities/BackHandler.macos.js @@ -0,0 +1,69 @@ +/** + * 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. + * + * On Apple TV, this implements back navigation using the TV remote's menu button. + * On iOS, this just implements a stub. + * + * @providesModule BackHandler + */ + +'use strict'; + +const Platform = require('Platform'); + +type BackPressEventName = $Enum<{ + backPress: string, +}>; + +function emptyFunction() {} + +/** + * Detect hardware button presses for back navigation. + * + * Android: Detect hardware back button presses, and programmatically invoke the default back button + * functionality to exit the app if there are no listeners or if none of the listeners return true. + * + * tvOS: Detect presses of the menu button on the TV remote. (Still to be implemented: + * programmatically disable menu button handling + * functionality to exit the app if there are no listeners or if none of the listeners return true.) + * + * iOS: Not applicable. + * + * macOS: Not applicable. + * + * The event subscriptions are called in reverse order (i.e. last registered subscription first), + * and if one subscription returns true then subscriptions registered earlier will not be called. + * + * Example: + * + * ```javascript + * BackHandler.addEventListener('hardwareBackPress', function() { + * // this.onMainScreen and this.goBack are just examples, you need to use your own implementation here + * // Typically you would use the navigator here to go to the last state. + * + * if (!this.onMainScreen()) { + * this.goBack(); + * return true; + * } + * return false; + * }); + * ``` + */ +let BackHandler; + +BackHandler = { + exitApp: emptyFunction, + addEventListener() { + return { + remove: emptyFunction, + }; + }, + removeEventListener: emptyFunction, +}; + +module.exports = BackHandler; diff --git a/Libraries/Utilities/HMRLoadingView.js b/Libraries/Utilities/HMRLoadingView.js new file mode 100644 index 00000000000000..ea0559fc41d0dd --- /dev/null +++ b/Libraries/Utilities/HMRLoadingView.js @@ -0,0 +1,32 @@ +/** + * 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 HMRLoadingView + * @flow + */ + +'use strict'; + +const processColor = require('processColor'); +const { DevLoadingView } = require('NativeModules'); + +class HMRLoadingView { + static showMessage(message: string) { + DevLoadingView.showMessage( + message, + processColor('#000000'), + processColor('#aaaaaa'), + ); + } + + static hide() { + DevLoadingView.hide(); + } +} + +module.exports = HMRLoadingView; diff --git a/Libraries/Utilities/Platform.macos.js b/Libraries/Utilities/Platform.macos.js new file mode 100644 index 00000000000000..3bcbfbac470841 --- /dev/null +++ b/Libraries/Utilities/Platform.macos.js @@ -0,0 +1,23 @@ +/** + * 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 Platform + * @flow + */ + +'use strict'; + +var Platform = { + OS: 'macos', + get Version() { + return require('NativeModules').MacOSConstants.osVersion; + }, + select: (obj: Object) => obj.macos, +}; + +module.exports = Platform; diff --git a/Libraries/WebSocket/RCTSRWebSocket.m b/Libraries/WebSocket/RCTSRWebSocket.m index 7febefecfdee64..e4ca83612538c3 100644 --- a/Libraries/WebSocket/RCTSRWebSocket.m +++ b/Libraries/WebSocket/RCTSRWebSocket.m @@ -17,7 +17,9 @@ #import "RCTSRWebSocket.h" #import +#if !TARGET_OS_OSX #import +#endif #import diff --git a/Libraries/WebSocket/RCTWebSocket.xcodeproj/project.pbxproj b/Libraries/WebSocket/RCTWebSocket.xcodeproj/project.pbxproj index fbe9c9b97e7d62..56f7f33325ff6e 100644 --- a/Libraries/WebSocket/RCTWebSocket.xcodeproj/project.pbxproj +++ b/Libraries/WebSocket/RCTWebSocket.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ 1338BBE01B04ACC80064A9C9 /* RCTSRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */; }; 1338BBE11B04ACC80064A9C9 /* RCTWebSocketExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */; }; 13526A521F362F7F0008EF00 /* libfishhook.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13526A511F362F7F0008EF00 /* libfishhook.a */; }; + 180B4A3E1FEC530900879A02 /* libfishhook-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18C3A9BF1FE1D41600DEC48A /* libfishhook-macOS.a */; }; + 18C3A9B91FE1D41600DEC48A /* fishhook.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3DBE0D131F3B185A0099AA32 /* fishhook.h */; }; + 18C3A9BB1FE1D41600DEC48A /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = 3DBE0D121F3B185A0099AA32 /* fishhook.c */; }; 2D3B5F3D1D9B165B00451313 /* RCTSRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */; }; 2D3B5F3E1D9B165B00451313 /* RCTWebSocketExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */; }; 2D3B5F401D9B165B00451313 /* RCTWebSocketModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */; }; @@ -19,11 +22,22 @@ 3DBE0D151F3B185A0099AA32 /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = 3DBE0D121F3B185A0099AA32 /* fishhook.c */; }; 3DBE0D801F3B1AF00099AA32 /* fishhook.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3DBE0D131F3B185A0099AA32 /* fishhook.h */; }; 3DBE0D821F3B1B0C0099AA32 /* fishhook.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3DBE0D131F3B185A0099AA32 /* fishhook.h */; }; + 64BF1F341EF8457600B83E07 /* RCTReconnectingWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = A12E9E2D1E5DEC4E0029001B /* RCTReconnectingWebSocket.m */; }; + 64BF1F351EF8457900B83E07 /* RCTSRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDD1B04ACC80064A9C9 /* RCTSRWebSocket.m */; }; + 64BF1F361EF8457B00B83E07 /* RCTWebSocketExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */; }; + 64BF1F381EF8458500B83E07 /* RCTWebSocketModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */; }; A12E9E2E1E5DEC4E0029001B /* RCTReconnectingWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = A12E9E2D1E5DEC4E0029001B /* RCTReconnectingWebSocket.m */; }; A12E9E2F1E5DEC550029001B /* RCTReconnectingWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = A12E9E2D1E5DEC4E0029001B /* RCTReconnectingWebSocket.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 18C3A9C01FE1D44F00DEC48A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3C86DF3E1ADF2C930047B81A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 18C3A9B71FE1D41600DEC48A; + remoteInfo = "fishhook-macOS"; + }; 3DBE0D0E1F3B18490099AA32 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 3C86DF3E1ADF2C930047B81A /* Project object */; @@ -41,6 +55,16 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 18C3A9B81FE1D41600DEC48A /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/fishhook; + dstSubfolderSpec = 16; + files = ( + 18C3A9B91FE1D41600DEC48A /* fishhook.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 3DBE0D7F1F3B1AEC0099AA32 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -69,15 +93,17 @@ 1338BBDE1B04ACC80064A9C9 /* RCTWebSocketExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketExecutor.h; sourceTree = ""; }; 1338BBDF1B04ACC80064A9C9 /* RCTWebSocketExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketExecutor.m; sourceTree = ""; }; 13526A511F362F7F0008EF00 /* libfishhook.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libfishhook.a; sourceTree = ""; }; + 18C3A9BF1FE1D41600DEC48A /* libfishhook-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libfishhook-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 2D2A28881D9B049200D4039D /* libRCTWebSocket-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTWebSocket-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 2DC5E5271F3A6CFD000EE84B /* libfishhook-tvOS.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libfishhook-tvOS.a"; path = "../fishhook/build/Debug-appletvos/libfishhook-tvOS.a"; sourceTree = ""; }; 3C86DF461ADF2C930047B81A /* libRCTWebSocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTWebSocket.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3C86DF7A1ADF695F0047B81A /* RCTWebSocketModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketModule.h; sourceTree = ""; }; - 3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketModule.m; sourceTree = ""; tabWidth = 2; }; + 3C86DF7B1ADF695F0047B81A /* RCTWebSocketModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebSocketModule.m; sourceTree = ""; }; 3DBE0D001F3B181A0099AA32 /* libfishhook.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libfishhook.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3DBE0D0D1F3B181C0099AA32 /* libfishhook-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libfishhook-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3DBE0D121F3B185A0099AA32 /* fishhook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fishhook.c; path = ../fishhook/fishhook.c; sourceTree = ""; }; 3DBE0D131F3B185A0099AA32 /* fishhook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fishhook.h; path = ../fishhook/fishhook.h; sourceTree = ""; }; + 6BDE7A581ECB6B8200CC951F /* libRCTWebSocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTWebSocket.a; sourceTree = BUILT_PRODUCTS_DIR; }; A12E9E2C1E5DEC4E0029001B /* RCTReconnectingWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTReconnectingWebSocket.h; sourceTree = ""; }; A12E9E2D1E5DEC4E0029001B /* RCTReconnectingWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTReconnectingWebSocket.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -91,6 +117,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 180B4A3D1FEC52FE00879A02 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 180B4A3E1FEC530900879A02 /* libfishhook-macOS.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2DC5E5151F3A6C39000EE84B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -137,8 +171,10 @@ children = ( 3C86DF461ADF2C930047B81A /* libRCTWebSocket.a */, 2D2A28881D9B049200D4039D /* libRCTWebSocket-tvOS.a */, + 6BDE7A581ECB6B8200CC951F /* libRCTWebSocket.a */, 3DBE0D001F3B181A0099AA32 /* libfishhook.a */, 3DBE0D0D1F3B181C0099AA32 /* libfishhook-tvOS.a */, + 18C3A9BF1FE1D41600DEC48A /* libfishhook-macOS.a */, ); name = Products; sourceTree = ""; @@ -146,6 +182,22 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 18C3A9B71FE1D41600DEC48A /* fishhook-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 18C3A9BC1FE1D41600DEC48A /* Build configuration list for PBXNativeTarget "fishhook-macOS" */; + buildPhases = ( + 18C3A9B81FE1D41600DEC48A /* CopyFiles */, + 18C3A9BA1FE1D41600DEC48A /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "fishhook-macOS"; + productName = WebSocket; + productReference = 18C3A9BF1FE1D41600DEC48A /* libfishhook-macOS.a */; + productType = "com.apple.product-type.library.static"; + }; 2D2A28871D9B049200D4039D /* RCTWebSocket-tvOS */ = { isa = PBXNativeTarget; buildConfigurationList = 2D2A28901D9B049200D4039D /* Build configuration list for PBXNativeTarget "RCTWebSocket-tvOS" */; @@ -212,6 +264,23 @@ productReference = 3DBE0D0D1F3B181C0099AA32 /* libfishhook-tvOS.a */; productType = "com.apple.product-type.library.static"; }; + 6BDE7A4F1ECB6B8200CC951F /* RCTWebSocket-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6BDE7A551ECB6B8200CC951F /* Build configuration list for PBXNativeTarget "RCTWebSocket-macOS" */; + buildPhases = ( + 64BF1F331EF8457300B83E07 /* Sources */, + 180B4A3D1FEC52FE00879A02 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 18C3A9C11FE1D44F00DEC48A /* PBXTargetDependency */, + ); + name = "RCTWebSocket-macOS"; + productName = WebSocket; + productReference = 6BDE7A581ECB6B8200CC951F /* libRCTWebSocket.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -243,14 +312,24 @@ projectRoot = ""; targets = ( 3C86DF451ADF2C930047B81A /* RCTWebSocket */, + 6BDE7A4F1ECB6B8200CC951F /* RCTWebSocket-macOS */, 2D2A28871D9B049200D4039D /* RCTWebSocket-tvOS */, 3DBE0CF41F3B181A0099AA32 /* fishhook */, 3DBE0D011F3B181C0099AA32 /* fishhook-tvOS */, + 18C3A9B71FE1D41600DEC48A /* fishhook-macOS */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ + 18C3A9BA1FE1D41600DEC48A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 18C3A9BB1FE1D41600DEC48A /* fishhook.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2D2A28841D9B049200D4039D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -289,9 +368,25 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 64BF1F331EF8457300B83E07 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 64BF1F381EF8458500B83E07 /* RCTWebSocketModule.m in Sources */, + 64BF1F351EF8457900B83E07 /* RCTSRWebSocket.m in Sources */, + 64BF1F341EF8457600B83E07 /* RCTReconnectingWebSocket.m in Sources */, + 64BF1F361EF8457B00B83E07 /* RCTWebSocketExecutor.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 18C3A9C11FE1D44F00DEC48A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 18C3A9B71FE1D41600DEC48A /* fishhook-macOS */; + targetProxy = 18C3A9C01FE1D44F00DEC48A /* PBXContainerItemProxy */; + }; 3DBE0D0F1F3B18490099AA32 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 3DBE0CF41F3B181A0099AA32 /* fishhook */; @@ -305,6 +400,28 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 18C3A9BD1FE1D41600DEC48A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = lib; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + 18C3A9BE1FE1D41600DEC48A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = lib; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; 2D2A288E1D9B049200D4039D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -374,6 +491,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -418,6 +536,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -499,9 +618,40 @@ }; name = Release; }; + 6BDE7A561ECB6B8200CC951F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = lib; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTWebSocket; + SDKROOT = macosx; + }; + name = Debug; + }; + 6BDE7A571ECB6B8200CC951F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = lib; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTWebSocket; + SDKROOT = macosx; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 18C3A9BC1FE1D41600DEC48A /* Build configuration list for PBXNativeTarget "fishhook-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 18C3A9BD1FE1D41600DEC48A /* Debug */, + 18C3A9BE1FE1D41600DEC48A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 2D2A28901D9B049200D4039D /* Build configuration list for PBXNativeTarget "RCTWebSocket-tvOS" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -547,6 +697,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 6BDE7A551ECB6B8200CC951F /* Build configuration list for PBXNativeTarget "RCTWebSocket-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6BDE7A561ECB6B8200CC951F /* Debug */, + 6BDE7A571ECB6B8200CC951F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 3C86DF3E1ADF2C930047B81A /* Project object */; diff --git a/Libraries/react-native/react-native-implementation.js b/Libraries/react-native/react-native-implementation.js index 88cd864c1e8869..844844e79afb40 100644 --- a/Libraries/react-native/react-native-implementation.js +++ b/Libraries/react-native/react-native-implementation.js @@ -75,6 +75,7 @@ const ReactNative = { get BackHandler() { return require('BackHandler'); }, get CameraRoll() { return require('CameraRoll'); }, get Clipboard() { return require('Clipboard'); }, + get DatePickerMacOS() { return require('DatePickerMacOS'); }, get DatePickerAndroid() { return require('DatePickerAndroid'); }, get DeviceInfo() { return require('DeviceInfo'); }, get Dimensions() { return require('Dimensions'); }, diff --git a/Libraries/react-native/react-native-implementation.macos.js b/Libraries/react-native/react-native-implementation.macos.js new file mode 100644 index 00000000000000..8edfccc2b7cf77 --- /dev/null +++ b/Libraries/react-native/react-native-implementation.macos.js @@ -0,0 +1,131 @@ +/** + * 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 react-native-implementation + * @flow + */ +'use strict'; + +const invariant = require('fbjs/lib/invariant'); + +// Export React, plus some native additions. +const ReactNative = { + // Components + // get AccessibilityInfo() { return require('AccessibilityInfo'); }, + get ActivityIndicator() { return require('ActivityIndicator'); }, + get ART() { return require('ReactNativeART'); }, + get Button() { return require('Button'); }, + get DatePickerMacOS() { return require('DatePickerMacOS'); }, + // get DrawerLayoutAndroid() { return require('DrawerLayoutAndroid'); }, + get FlatList() { return require('FlatList'); }, + get Image() { return require('Image'); }, + get ImageBackground() { return require('ImageBackground'); }, + get ImageEditor() { return require('ImageEditor'); }, + get ImageStore() { return require('ImageStore'); }, + // get KeyboardAvoidingView() { return require('KeyboardAvoidingView'); }, + get ListView() { return require('ListView'); }, + // get Modal() { return require('Modal'); }, + // get NavigatorIOS() { return require('NavigatorIOS'); }, + get Picker() { return require('Picker'); }, + get PickerIOS() { return require('PickerIOS'); }, + // get ProgressBarAndroid() { return require('ProgressBarAndroid'); }, + get ProgressViewIOS() { return require('ProgressViewIOS'); }, + get ScrollView() { return require('ScrollView'); }, + get SectionList() { return require('SectionList'); }, + get SegmentedControlIOS() { return require('SegmentedControlIOS'); }, + get Slider() { return require('Slider'); }, + // get SnapshotViewIOS() { return require('SnapshotViewIOS'); }, + get Switch() { return require('Switch'); }, + // get RefreshControl() { return require('RefreshControl'); }, + // get StatusBar() { return require('StatusBar'); }, + get SwipeableListView() { return require('SwipeableListView'); }, + // get TabBarIOS() { return require('TabBarIOS'); }, + get Text() { return require('Text'); }, + get TextInput() { return require('TextInput'); }, + // get ToastAndroid() { return require('ToastAndroid'); }, + // get ToolbarAndroid() { return require('ToolbarAndroid'); }, + get Touchable() { return require('Touchable'); }, + get TouchableHighlight() { return require('TouchableHighlight'); }, + get TouchableNativeFeedback() { return require('TouchableNativeFeedback'); }, + get TouchableOpacity() { return require('TouchableOpacity'); }, + get TouchableWithoutFeedback() { return require('TouchableWithoutFeedback'); }, + get View() { return require('View'); }, + // get ViewPagerAndroid() { return require('ViewPagerAndroid'); }, + get VirtualizedList() { return require('VirtualizedList'); }, + get WebView() { return require('WebView'); }, + + // APIs + get ActionSheetIOS() { return require('ActionSheetIOS'); }, + // get AdSupportIOS() { return require('AdSupportIOS'); }, + get Alert() { return require('Alert'); }, + get AlertIOS() { return require('AlertIOS'); }, + get AlertMacOS() { return require('AlertMacOS'); }, + get Animated() { return require('Animated'); }, + get AppRegistry() { return require('AppRegistry'); }, + get AppState() { return require('AppState'); }, + get AsyncStorage() { return require('AsyncStorage'); }, + // get BackAndroid() { return require('BackAndroid'); }, // deprecated: use BackHandler instead + get BackHandler() { return require('BackHandler'); }, + // get CameraRoll() { return require('CameraRoll'); }, + get Clipboard() { return require('Clipboard'); }, + get DatePickerIOS() { return require('DatePickerIOS'); }, + get DatePickerAndroid() { return require('DatePickerAndroid'); }, + get DeviceInfo() { return require('DeviceInfo'); }, + get Dimensions() { return require('Dimensions'); }, + get Easing() { return require('Easing'); }, + get findNodeHandle() { return require('ReactNative').findNodeHandle; }, + get I18nManager() { return require('I18nManager'); }, + // get ImagePickerIOS() { return require('ImagePickerIOS'); }, + get InteractionManager() { return require('InteractionManager'); }, + // get Keyboard() { return require('Keyboard'); }, + get LayoutAnimation() { return require('LayoutAnimation'); }, + get Linking() { return require('Linking'); }, + get NativeEventEmitter() { return require('NativeEventEmitter'); }, + get NetInfo() { return require('NetInfo'); }, + get PanResponder() { return require('PanResponder'); }, + // get PermissionsAndroid() { return require('PermissionsAndroid'); }, + get PixelRatio() { return require('PixelRatio'); }, + get PushNotificationIOS() { return require('PushNotificationIOS'); }, + get Settings() { return require('Settings'); }, + // get Share() { return require('Share'); }, + // get StatusBarIOS() { return require('StatusBarIOS'); }, + get StyleSheet() { return require('StyleSheet'); }, + // get Systrace() { return require('Systrace'); }, + // get TimePickerAndroid() { return require('TimePickerAndroid'); }, + get TVEventHandler() { return require('TVEventHandler'); }, + get UIManager() { return require('UIManager'); }, + // get Vibration() { return require('Vibration'); }, + // get VibrationIOS() { return require('VibrationIOS'); }, + + // Plugins + get DeviceEventEmitter() { return require('RCTDeviceEventEmitter'); }, + get NativeAppEventEmitter() { return require('RCTNativeAppEventEmitter'); }, + get NativeModules() { return require('NativeModules'); }, + get Platform() { return require('Platform'); }, + get processColor() { return require('processColor'); }, + get requireNativeComponent() { return require('requireNativeComponent'); }, + // get takeSnapshot() { return require('takeSnapshot'); }, + + // Prop Types + get ColorPropType() { return require('ColorPropType'); }, + get EdgeInsetsPropType() { return require('EdgeInsetsPropType'); }, + get PointPropType() { return require('PointPropType'); }, + get ViewPropTypes() { return require('ViewPropTypes'); }, + + // Deprecated + get Navigator() { + invariant( + false, + 'Navigator is deprecated and has been removed from this package. It can now be installed ' + + 'and imported from `react-native-deprecated-custom-components` instead of `react-native`. ' + + 'Learn about alternative navigation solutions at http://facebook.github.io/react-native/docs/navigation.html' + ); + }, +}; + +module.exports = ReactNative; diff --git a/RNTester/RNTester-macOS/AppDelegate.h b/RNTester/RNTester-macOS/AppDelegate.h new file mode 100644 index 00000000000000..e2b225f23c0929 --- /dev/null +++ b/RNTester/RNTester-macOS/AppDelegate.h @@ -0,0 +1,19 @@ +// +// AppDelegate.h +// RNTester-macOS +// +// Created by Jeff Cruikshank on 6/5/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +@class RCTBridge; +extern NSString *kBundleNameJS; + +@interface AppDelegate : NSObject + +@property (nonatomic, readonly) RCTBridge *bridge; + +@end + diff --git a/RNTester/RNTester-macOS/AppDelegate.m b/RNTester/RNTester-macOS/AppDelegate.m new file mode 100644 index 00000000000000..0a2a4a6fecd429 --- /dev/null +++ b/RNTester/RNTester-macOS/AppDelegate.m @@ -0,0 +1,104 @@ +// +// AppDelegate.m +// RNTester-macOS +// +// Created by Jeff Cruikshank on 6/5/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import "AppDelegate.h" + +#import +#import +#import +#import + +const NSString *kBundleNameJS = @"RNTesterApp"; + +@interface AppDelegate () + +@end + +@implementation AppDelegate +{ + NSMutableArray *_mainWindows; +} + +- (void)awakeFromNib +{ + [super awakeFromNib]; + + _bridge = [[RCTBridge alloc] initWithDelegate:self + launchOptions:nil]; +} + +- (void)applicationWillFinishLaunching:(NSNotification *)__unused aNotification +{ + [NSUserNotificationCenter defaultUserNotificationCenter].delegate = self; + + // initialize the url event listeners for Linking module + // note that you will need to add a URL type to your app’s info.plist + // this sample registers the rntester scheme + [[NSAppleEventManager sharedAppleEventManager] setEventHandler:[RCTLinkingManager class] + andSelector:@selector(getUrlEventHandler:withReplyEvent:) + forEventClass:kInternetEventClass + andEventID:kAEGetURL]; + +} + +-(IBAction)newDocument:(id)__unused sender +{ + if (_mainWindows == nil) { + _mainWindows = [NSMutableArray new]; + } + + NSWindowController *windowController = [[NSStoryboard storyboardWithName:@"Main" bundle:nil] instantiateControllerWithIdentifier:@"MainWindow"]; + [_mainWindows addObject:windowController]; + [windowController showWindow:self]; +} + +#pragma mark - RCTBridgeDelegate Methods + +- (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge +{ + NSString *jsBundlePath = [NSString stringWithFormat:@"RNTester/js/%@.macos",kBundleNameJS]; + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:jsBundlePath + fallbackResource:nil]; +} + +# pragma mark - Push Notifications + +// Required for the remoteNotificationsRegistered event. +- (void)application:(NSApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken +{ + [RCTPushNotificationManager didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; +} + +// Required for the remoteNotificationRegistrationError event. +- (void)application:(NSApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error +{ + [RCTPushNotificationManager didFailToRegisterForRemoteNotificationsWithError:error]; +} + +// Required for the remoteNotificationReceived event. +- (void)application:(NSApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo +{ + [RCTPushNotificationManager didReceiveRemoteNotification:userInfo]; +} + +- (void)userNotificationCenter:(NSUserNotificationCenter *)center didDeliverNotification:(NSUserNotification *)notification +{ + +} + +- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification +{ + [RCTPushNotificationManager didReceiveUserNotification:notification]; +} + +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification +{ + return YES; +} + +@end diff --git a/RNTester/RNTester-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/RNTester/RNTester-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000000..2db2b1c7c6c316 --- /dev/null +++ b/RNTester/RNTester-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/RNTester/RNTester-macOS/Assets.xcassets/Contents.json b/RNTester/RNTester-macOS/Assets.xcassets/Contents.json new file mode 100644 index 00000000000000..da4a164c918651 --- /dev/null +++ b/RNTester/RNTester-macOS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/RNTester/RNTester-macOS/Assets.xcassets/story-background.imageset/Contents.json b/RNTester/RNTester-macOS/Assets.xcassets/story-background.imageset/Contents.json new file mode 100644 index 00000000000000..2b52c91722a55c --- /dev/null +++ b/RNTester/RNTester-macOS/Assets.xcassets/story-background.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "story-background@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/RNTester/RNTester-macOS/Assets.xcassets/story-background.imageset/story-background@2x.png b/RNTester/RNTester-macOS/Assets.xcassets/story-background.imageset/story-background@2x.png new file mode 100644 index 00000000000000..fc082986c4e652 Binary files /dev/null and b/RNTester/RNTester-macOS/Assets.xcassets/story-background.imageset/story-background@2x.png differ diff --git a/RNTester/RNTester-macOS/Base.lproj/Main.storyboard b/RNTester/RNTester-macOS/Base.lproj/Main.storyboard new file mode 100644 index 00000000000000..525f19b7c3b5ad --- /dev/null +++ b/RNTester/RNTester-macOS/Base.lproj/Main.storyboard @@ -0,0 +1,698 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RNTester/RNTester-macOS/Info.plist b/RNTester/RNTester-macOS/Info.plist new file mode 100644 index 00000000000000..f62f7ac6e5bf9d --- /dev/null +++ b/RNTester/RNTester-macOS/Info.plist @@ -0,0 +1,48 @@ + + + + + CFBundleURLTypes + + + CFBundleURLName + + CFBundleURLSchemes + + rntester + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2017 Facebook. All rights reserved. + NSMainStoryboardFile + Main + NSLocationWhenInUseUsageDescription + true + NSPrincipalClass + NSApplication + + diff --git a/RNTester/RNTester-macOS/ViewController.h b/RNTester/RNTester-macOS/ViewController.h new file mode 100644 index 00000000000000..7bf16f41ba8fd3 --- /dev/null +++ b/RNTester/RNTester-macOS/ViewController.h @@ -0,0 +1,15 @@ +// +// ViewController.h +// RNTester-macOS +// +// Created by Jeff Cruikshank on 6/5/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +@interface ViewController : NSViewController + + +@end + diff --git a/RNTester/RNTester-macOS/ViewController.m b/RNTester/RNTester-macOS/ViewController.m new file mode 100644 index 00000000000000..6ba764b65a5c5b --- /dev/null +++ b/RNTester/RNTester-macOS/ViewController.m @@ -0,0 +1,31 @@ +// +// ViewController.m +// RNTester-macOS +// +// Created by Jeff Cruikshank on 6/5/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import "AppDelegate.h" +#import "ViewController.h" + +#import + +@implementation ViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + RCTBridge *bridge = ((AppDelegate *)[NSApp delegate]).bridge; + RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge + moduleName:kBundleNameJS + initialProperties:nil]; + + [self.view addSubview:rootView]; + rootView.backgroundColor = [NSColor windowBackgroundColor]; + rootView.frame = self.view.bounds; + rootView.autoresizingMask = (NSViewMinXMargin | NSViewMinXMargin | NSViewMinYMargin | NSViewMaxYMargin | NSViewWidthSizable | NSViewHeightSizable); +} + +@end diff --git a/RNTester/RNTester-macOS/main.m b/RNTester/RNTester-macOS/main.m new file mode 100644 index 00000000000000..53cc6644899a09 --- /dev/null +++ b/RNTester/RNTester-macOS/main.m @@ -0,0 +1,13 @@ +// +// main.m +// RNTester-macOS +// +// Created by Jeff Cruikshank on 6/5/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +int main(int argc, const char * argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/RNTester/RNTester-macOSIntegrationTests/Info.plist b/RNTester/RNTester-macOSIntegrationTests/Info.plist new file mode 100644 index 00000000000000..6c6c23c43adc88 --- /dev/null +++ b/RNTester/RNTester-macOSIntegrationTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/RNTester/RNTester-macOSIntegrationTests/RNTesterIntegrationTests_macOS.m b/RNTester/RNTester-macOSIntegrationTests/RNTesterIntegrationTests_macOS.m new file mode 100644 index 00000000000000..6ca691c932686c --- /dev/null +++ b/RNTester/RNTester-macOSIntegrationTests/RNTesterIntegrationTests_macOS.m @@ -0,0 +1,40 @@ +// +// RNTesterIntegrationTests_macOS.m +// RNTesterIntegrationTests-macOS +// +// Created by Jeff Cruikshank on 6/5/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +@interface RNTesterIntegrationTests_macOS : XCTestCase + +@end + +@implementation RNTesterIntegrationTests_macOS + +- (void)setUp { + [super setUp]; + + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + self.continueAfterFailure = NO; + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + [[[XCUIApplication alloc] init] launch]; + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +@end diff --git a/RNTester/RNTester-macOSUnitTests/Info.plist b/RNTester/RNTester-macOSUnitTests/Info.plist new file mode 100644 index 00000000000000..6c6c23c43adc88 --- /dev/null +++ b/RNTester/RNTester-macOSUnitTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/RNTester/RNTester-macOSUnitTests/RNTesterUnitTests_macOS.m b/RNTester/RNTester-macOSUnitTests/RNTesterUnitTests_macOS.m new file mode 100644 index 00000000000000..ca27324d7d2c08 --- /dev/null +++ b/RNTester/RNTester-macOSUnitTests/RNTesterUnitTests_macOS.m @@ -0,0 +1,39 @@ +// +// RNTesterUnitTests_macOS.m +// RNTesterUnitTests-macOS +// +// Created by Jeff Cruikshank on 6/5/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +@interface RNTesterUnitTests_macOS : XCTestCase + +@end + +@implementation RNTesterUnitTests_macOS + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +//- (void)testFunctionalExample { +// // This is an example of a functional test case. +// // Use XCTAssert and related functions to verify your tests produce the correct results. +//} +// +//- (void)testPerformanceExample { +// // This is an example of a performance test case. +// [self measureBlock:^{ +// // Put the code you want to measure the time of here. +// }]; +//} + +@end diff --git a/RNTester/RNTester.xcodeproj/project.pbxproj b/RNTester/RNTester.xcodeproj/project.pbxproj index 203fe77206ce24..446ac3dd46eda4 100644 --- a/RNTester/RNTester.xcodeproj/project.pbxproj +++ b/RNTester/RNTester.xcodeproj/project.pbxproj @@ -50,6 +50,66 @@ 14D6D7281B2222EF001FB087 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; }; 14D6D7291B2222EF001FB087 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; }; 14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; }; + 180B9CC41F01B040006AF028 /* RCTModuleInitNotificationRaceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13129DD31C85F87C007D611C /* RCTModuleInitNotificationRaceTests.m */; }; + 1827ADAD20ACEA6600E266FC /* RCTLoggingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D299BAE1D33EBFA00FA1057 /* RCTLoggingTests.m */; }; + 1827ADEF20ACEB7100E266FC /* RNTesterIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DB99D0B1BA0340600302749 /* RNTesterIntegrationTests.m */; }; + 18316DFC1EF9D981003DADF3 /* RCTConvert_YGValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 192F69B61E82409A008692C7 /* RCTConvert_YGValueTests.m */; }; + 18316DFD1EF9D996003DADF3 /* RCTBundleURLProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */; }; + 18316DFE1EF9DA43003DADF3 /* RNTesterUnitTestsBundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 3DD981D51D33C6FB007DC7BE /* RNTesterUnitTestsBundle.js */; }; + 18316E011EF9DC9C003DADF3 /* RCTConvert_NSURLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA71B21F5E400C1F8F2 /* RCTConvert_NSURLTests.m */; }; + 18316E021EF9E0DE003DADF3 /* RCTFontTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA81B21F5E400C1F8F2 /* RCTFontTests.m */; }; + 18316E041EF9EAD1003DADF3 /* RCTGzipTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1300627E1B59179B0043FE5A /* RCTGzipTests.m */; }; + 18316E1B1EF9EB7D003DADF3 /* RCTAllocationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA41B21F5E400C1F8F2 /* RCTAllocationTests.m */; }; + 1834500B1EFAD54F0000CF82 /* libRCTTest-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18DF57271EFA056600BF4666 /* libRCTTest-macOS.a */; }; + 183938D01F9A55AE00930D92 /* RNTesterTestModule.m in Sources */ = {isa = PBXBuildFile; fileRef = C654F0B21EB34A73000B7A9A /* RNTesterTestModule.m */; }; + 183938D31F9A571900930D92 /* libRCTTest-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18DF57271EFA056600BF4666 /* libRCTTest-macOS.a */; }; + 185126F420ADE05000270B2D /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77E91EF4770B002B3F17 /* libReact.a */; }; + 185126F520ADE06700270B2D /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77C61EF4770B002B3F17 /* libRCTNetwork.a */; }; + 185126F620ADE07500270B2D /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77D71EF4770B002B3F17 /* libRCTWebSocket.a */; }; + 185126F720ADE09F00270B2D /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77BF1EF4770B002B3F17 /* libRCTImage.a */; }; + 1897BBD01EF47D8C003C8507 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77BF1EF4770B002B3F17 /* libRCTImage.a */; }; + 1897BBD11EF47D8C003C8507 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77C61EF4770B002B3F17 /* libRCTNetwork.a */; }; + 1897BBD21EF47D8C003C8507 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77D11EF4770B002B3F17 /* libRCTText.a */; }; + 1897BBD31EF47D8C003C8507 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77D71EF4770B002B3F17 /* libRCTWebSocket.a */; }; + 1897BBD41EF47D8C003C8507 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77E91EF4770B002B3F17 /* libReact.a */; }; + 18AA4BD41EF9C724008C7756 /* RCTURLUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B6C1A21C34225900D3FAF5 /* RCTURLUtilsTests.m */; }; + 18BA6F461EFC489700E33772 /* libRCTTest-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD323CC1DA2DD8B000FE1B8 /* libRCTTest-tvOS.a */; }; + 18BFA4C51F01C46D00969486 /* OCMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18BFA4931F01C46700969486 /* OCMock.framework */; }; + 18BFA4CB1F01C52400969486 /* RCTEventDispatcherTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */; }; + 18CF50831F9A65EE0038A244 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77E91EF4770B002B3F17 /* libReact.a */; }; + 18CF50841F9A65FC0038A244 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77D71EF4770B002B3F17 /* libRCTWebSocket.a */; }; + 18CF50851F9A66E00038A244 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77BF1EF4770B002B3F17 /* libRCTImage.a */; }; + 18CF50861F9A66EB0038A244 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77C61EF4770B002B3F17 /* libRCTNetwork.a */; }; + 18CF50871F9A67120038A244 /* libART-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 647647311F0BC04800C2D89B /* libART-macOS.a */; }; + 18CF50881F9A671B0038A244 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FC77D11EF4770B002B3F17 /* libRCTText.a */; }; + 18CF50891F9A67320038A244 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 647647C81F0BCC5500C2D89B /* libRCTAnimation.a */; }; + 18CF508A1F9A673E0038A244 /* libRCTGeolocation-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4633DB601F207C140080B326 /* libRCTGeolocation-macOS.a */; }; + 18CF508B1F9A67500038A244 /* libRCTPushNotification-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6424F7B21F669A8E0025D741 /* libRCTPushNotification-macOS.a */; }; + 18CF508C1F9A675A0038A244 /* libRCTLinking-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D429028F1F1CE21600685AE7 /* libRCTLinking-macOS.a */; }; + 18CF508D1F9A676C0038A244 /* libRCTSettings-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6448A5D21F292F16006FF1F5 /* libRCTSettings-macOS.a */; }; + 18CF508E1F9A67760038A244 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 649D87D91F69DA030005AF18 /* libRCTActionSheet.a */; }; + 18CF508F1F9A6A3B0038A244 /* RNTesterBundle-macOS.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 641DD8EE1F16AA8500B6415F /* RNTesterBundle-macOS.bundle */; }; + 18D5934D1EF9F8BC005F0CEC /* RCTImageLoaderHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 8385CF031B87479200C6273E /* RCTImageLoaderHelpers.m */; }; + 18D593781EF9F8CB005F0CEC /* RCTImageLoaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8385CEF41B873B5C00C6273E /* RCTImageLoaderTests.m */; }; + 18D593791EF9F8DA005F0CEC /* RCTImageUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 144D21231B2204C5006DB32B /* RCTImageUtilTests.m */; }; + 18D5937A1EF9F95A005F0CEC /* RCTJSONTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */; }; + 18D5937B1EF9F96A005F0CEC /* RCTMethodArgumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */; }; + 18D5937D1EF9F990005F0CEC /* RCTModuleInitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 134CB9291C85A38800265FA6 /* RCTModuleInitTests.m */; }; + 18D5937E1EF9FA22005F0CEC /* RCTModuleMethodTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.mm */; }; + 18D5937F1EF9FA39005F0CEC /* RCTMultipartStreamReaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 001BFCE31D838343008E587E /* RCTMultipartStreamReaderTests.m */; }; + 18D593801EF9FA48005F0CEC /* RCTShadowViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */; }; + 18D593811EF9FA57005F0CEC /* RCTUIManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */; }; + 18D593841EF9FAB9005F0CEC /* RCTUnicodeDecodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 39AA31A31DC1DFDC000F7EBB /* RCTUnicodeDecodeTests.m */; }; + 18D949451F9AC375007BB668 /* RCTRootViewIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B885551BED29AF00008352 /* RCTRootViewIntegrationTests.m */; }; + 18D9497D1F9AC3D0007BB668 /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; }; + 18DB36F11F10082A00A877D7 /* OCMock.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 18BFA4931F01C46700969486 /* OCMock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 18DF57111EFA047400BF4666 /* RCTComponentPropsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BCE84E1C9C209600DD7AAD /* RCTComponentPropsTests.m */; }; + 18FC778A1EF4770B002B3F17 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 18FC77891EF4770B002B3F17 /* AppDelegate.m */; }; + 18FC778D1EF4770B002B3F17 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 18FC778C1EF4770B002B3F17 /* main.m */; }; + 18FC77901EF4770B002B3F17 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18FC778F1EF4770B002B3F17 /* ViewController.m */; }; + 18FC77951EF4770B002B3F17 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 18FC77931EF4770B002B3F17 /* Main.storyboard */; }; + 18FC77A01EF4770B002B3F17 /* RNTesterUnitTests_macOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 18FC779F1EF4770B002B3F17 /* RNTesterUnitTests_macOS.m */; }; + 18FC77AB1EF4770B002B3F17 /* RNTesterIntegrationTests_macOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 18FC77AA1EF4770B002B3F17 /* RNTesterIntegrationTests_macOS.m */; }; 192F69B81E82409A008692C7 /* RCTAnimationUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 192F69B51E82409A008692C7 /* RCTAnimationUtilsTests.m */; }; 192F69B91E82409A008692C7 /* RCTConvert_YGValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 192F69B61E82409A008692C7 /* RCTConvert_YGValueTests.m */; }; 192F69BA1E82409A008692C7 /* RCTNativeAnimatedNodesManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 192F69B71E82409A008692C7 /* RCTNativeAnimatedNodesManagerTests.m */; }; @@ -112,8 +172,23 @@ 3D56F9F11D6F6E9B00F53A06 /* RNTesterBundle.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3D13F83E1D6F6AE000E69E0E /* RNTesterBundle.bundle */; }; 3DB99D0C1BA0340600302749 /* RNTesterIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DB99D0B1BA0340600302749 /* RNTesterIntegrationTests.m */; }; 3DD981D61D33C6FB007DC7BE /* RNTesterUnitTestsBundle.js in Resources */ = {isa = PBXBuildFile; fileRef = 3DD981D51D33C6FB007DC7BE /* RNTesterUnitTestsBundle.js */; }; + 4633DB9F1F212E680080B326 /* libRCTGeolocation-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4633DB601F207C140080B326 /* libRCTGeolocation-macOS.a */; }; 52C11BBB1EEACA7100C1A058 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5281CA511EEAC9A700AC40CD /* libRCTBlob.a */; }; 52C11BE11EEACA7800C1A058 /* libRCTBlob-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5281CA531EEAC9A700AC40CD /* libRCTBlob-tvOS.a */; }; + 641DD8B11F16A59800B6415F /* legacy_image@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3D2AFAF41D646CF80089D1A3 /* legacy_image@2x.png */; }; + 641DD8E91F16AA8500B6415F /* ImageInBundle.png in Resources */ = {isa = PBXBuildFile; fileRef = 3D13F8441D6F6AF200E69E0E /* ImageInBundle.png */; }; + 641DD8EA1F16AA8500B6415F /* OtherImages.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3D13F8451D6F6AF200E69E0E /* OtherImages.xcassets */; }; + 641DD8F01F16AB0600B6415F /* RNTesterBundle-macOS.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 641DD8EE1F16AA8500B6415F /* RNTesterBundle-macOS.bundle */; }; + 641DD9231F16B90100B6415F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 18FC77911EF4770B002B3F17 /* Assets.xcassets */; }; + 64481C631F6753960005D62A /* libRCTPushNotification-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6424F7B21F669A8E0025D741 /* libRCTPushNotification-macOS.a */; }; + 6448A5D31F292F16006FF1F5 /* libRCTSettings-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6448A5D21F292F16006FF1F5 /* libRCTSettings-macOS.a */; }; + 647647321F0BC04800C2D89B /* libART-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 647647311F0BC04800C2D89B /* libART-macOS.a */; }; + 647647C91F0BCC5500C2D89B /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 647647C81F0BCC5500C2D89B /* libRCTAnimation.a */; }; + 6484CE5B201A7557004275A4 /* libRCTBlob-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6484CE5A201A7557004275A4 /* libRCTBlob-macOS.a */; }; + 649BFB1F1F0D4C870078D219 /* RCTAnimationUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 192F69B51E82409A008692C7 /* RCTAnimationUtilsTests.m */; }; + 649BFB521F0D4CE50078D219 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 647647C81F0BCC5500C2D89B /* libRCTAnimation.a */; }; + 649BFB591F0D54B60078D219 /* RCTNativeAnimatedNodesManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 192F69B71E82409A008692C7 /* RCTNativeAnimatedNodesManagerTests.m */; }; + 649D880C1F69DA080005AF18 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 649D87D91F69DA030005AF18 /* libRCTActionSheet.a */; }; 68FF44381CF6111500720EFD /* RCTBundleURLProviderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */; }; 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; }; 83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; }; @@ -122,6 +197,7 @@ BC9C03401DC9F1D600B1C635 /* RCTDevMenuTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC9C033F1DC9F1D600B1C635 /* RCTDevMenuTests.m */; }; C654F0B31EB34A73000B7A9A /* RNTesterTestModule.m in Sources */ = {isa = PBXBuildFile; fileRef = C654F0B21EB34A73000B7A9A /* RNTesterTestModule.m */; }; C654F17E1EB34D24000B7A9A /* RNTesterTestModule.m in Sources */ = {isa = PBXBuildFile; fileRef = C654F0B21EB34A73000B7A9A /* RNTesterTestModule.m */; }; + D42902901F1CE21600685AE7 /* libRCTLinking-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D429028F1F1CE21600685AE7 /* libRCTLinking-macOS.a */; }; D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; }; /* End PBXBuildFile section */ @@ -203,6 +279,97 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTPushNotification; }; + 18DF57261EFA056600BF4666 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 18DF57221EFA052000BF4666; + remoteInfo = "RCTTest-macOS"; + }; + 18F73E521EF839CC00FE5F4B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 188202A21EF48CF700C9B354; + remoteInfo = "third-party-macOS"; + }; + 18F73E541EF839CC00FE5F4B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1882027C1EF48B7E00C9B354; + remoteInfo = "double-conversion-macOS"; + }; + 18FC779C1EF4770B002B3F17 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 18FC77851EF4770B002B3F17; + remoteInfo = "RNTester-macOS"; + }; + 18FC77A71EF4770B002B3F17 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 18FC77851EF4770B002B3F17; + remoteInfo = "RNTester-macOS"; + }; + 18FC77BE1EF4770B002B3F17 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 13417FE31AA91428003F314A /* RCTImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6BDE7AEB1ECB9C4400CC951F; + remoteInfo = "RCTImage-macOS"; + }; + 18FC77C51EF4770B002B3F17 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 134180261AA91779003F314A /* RCTNetwork.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6BDE7A901ECB6E8400CC951F; + remoteInfo = "RCTNetwork-macOS"; + }; + 18FC77D01EF4770B002B3F17 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6BDE7AC21ECB8D6200CC951F; + remoteInfo = "RCTText-macos"; + }; + 18FC77D61EF4770B002B3F17 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6BDE7A581ECB6B8200CC951F; + remoteInfo = "RCTWebSocket-macOS"; + }; + 18FC77E81EF4770B002B3F17 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6B857F231EC51FC600A9D063; + remoteInfo = "React-macOS"; + }; + 18FC77EA1EF4770B002B3F17 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6B857F341EC51FCC00A9D063; + remoteInfo = "yoga-macOS"; + }; + 18FC77EC1EF4770B002B3F17 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6B857F421EC51FCF00A9D063; + remoteInfo = "cxxreact-macOS"; + }; + 18FC77EE1EF4770B002B3F17 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6B857F4F1EC51FD400A9D063; + remoteInfo = "jschelpers-macOS"; + }; 2D4624C31DA2EA6900C74D09 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; @@ -399,6 +566,13 @@ remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32; remoteInfo = "fishhook-tvOS"; }; + 4633DB5F1F207C140080B326 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 4633DB5C1F2073070080B326; + remoteInfo = "RCTGeolocation-macOS"; + }; 5281CA501EEAC9A700AC40CD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 5281CA4B1EEAC9A700AC40CD /* RCTBlob.xcodeproj */; @@ -420,27 +594,97 @@ remoteGlobalIDString = 580C376F1AB104AF0015E709; remoteInfo = RCTTest; }; - 834C36D11AF8DA610019C93C /* PBXContainerItemProxy */ = { + 641DD8F11F16B77700B6415F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 641DD8E51F16AA8500B6415F; + remoteInfo = "RNTesterBundle-macOS"; + }; + 6424F7B11F669A8E0025D741 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6424F7AC1F669A3A0025D741; + remoteInfo = "RCTPushNotification-macOS"; + }; + 6448A5D11F292F16006FF1F5 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */; proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTSettings; + remoteGlobalIDString = 6448A5CD1F292E63006FF1F5; + remoteInfo = "RCTSettings-macOS"; + }; + 647647301F0BC04800C2D89B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 2D66FF5F1ECA405900F0A767 /* ART.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 647646E41F0BAC6600C2D89B; + remoteInfo = "ART-macOS"; + }; + 647647C71F0BCC5500C2D89B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 13E5019C1D07A502005F35D8 /* RCTAnimation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 647647C51F0BC7F200C2D89B; + remoteInfo = "RCTAnimation-macOS"; + }; + 6484CE59201A7557004275A4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5281CA4B1EEAC9A700AC40CD /* RCTBlob.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6484CE57201A74FA004275A4; + remoteInfo = "RCTBlob-macOS"; + }; + 649BF7182019105B0068273E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 18C3A9BF1FE1D41600DEC48A; + remoteInfo = "fishhook-macOS"; }; - AD9FBECB1FD7597F00CADA08 /* PBXContainerItemProxy */ = { + 649BF72F2019105B0068273E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */; proxyType = 2; remoteGlobalIDString = 9936F3131F5F2E4B0010BF04; remoteInfo = privatedata; }; - AD9FBECD1FD7597F00CADA08 /* PBXContainerItemProxy */ = { + 649BF7312019105B0068273E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */; proxyType = 2; remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04; remoteInfo = "privatedata-tvOS"; }; + 649BF7332019105B0068273E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 14AADEFF1AC3DB95002390C9 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 18C3A9B21FE1CDCC00DEC48A; + remoteInfo = "privatedata-macOS"; + }; + 649D87D81F69DA030005AF18 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 649D87D21F69D9BC0005AF18; + remoteInfo = "RCTActionSheet-macOS"; + }; + 834C36D11AF8DA610019C93C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTSettings; + }; + D429028E1F1CE21600685AE7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D4C812E71F1CC42300FFA059; + remoteInfo = "RCTLinking-macOS"; + }; D85B829B1AB6D5CE003F4FE2 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */; @@ -450,6 +694,20 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 18BFA4C91F01C4F900969486 /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 18DB36F11F10082A00A877D7 /* OCMock.framework in Copy Frameworks */, + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 001BFCE31D838343008E587E /* RCTMultipartStreamReaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMultipartStreamReaderTests.m; sourceTree = ""; }; 004D289E1AAF61C70097A701 /* RNTesterUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNTesterUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -500,6 +758,22 @@ 14D6D7101B220EB3001FB087 /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = ""; }; 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = ../Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj; sourceTree = ""; }; 14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = ""; }; + 18BFA4931F01C46700969486 /* OCMock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OCMock.framework; sourceTree = ""; }; + 18FC77861EF4770B002B3F17 /* RNTester-macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RNTester-macOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 18FC77881EF4770B002B3F17 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 18FC77891EF4770B002B3F17 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 18FC778C1EF4770B002B3F17 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 18FC778E1EF4770B002B3F17 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 18FC778F1EF4770B002B3F17 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 18FC77911EF4770B002B3F17 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 18FC77941EF4770B002B3F17 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 18FC77961EF4770B002B3F17 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 18FC779B1EF4770B002B3F17 /* RNTester-macOSUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RNTester-macOSUnitTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 18FC779F1EF4770B002B3F17 /* RNTesterUnitTests_macOS.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNTesterUnitTests_macOS.m; sourceTree = ""; }; + 18FC77A11EF4770B002B3F17 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 18FC77A61EF4770B002B3F17 /* RNTester-macOSIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RNTester-macOSIntegrationTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 18FC77AA1EF4770B002B3F17 /* RNTesterIntegrationTests_macOS.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNTesterIntegrationTests_macOS.m; sourceTree = ""; }; + 18FC77AC1EF4770B002B3F17 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 192F69B51E82409A008692C7 /* RCTAnimationUtilsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationUtilsTests.m; sourceTree = ""; }; 192F69B61E82409A008692C7 /* RCTConvert_YGValueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert_YGValueTests.m; sourceTree = ""; }; 192F69B71E82409A008692C7 /* RCTNativeAnimatedNodesManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTNativeAnimatedNodesManagerTests.m; sourceTree = ""; }; @@ -525,6 +799,7 @@ 3DD981D51D33C6FB007DC7BE /* RNTesterUnitTestsBundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = RNTesterUnitTestsBundle.js; sourceTree = ""; }; 5281CA4B1EEAC9A700AC40CD /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = ../Libraries/Blob/RCTBlob.xcodeproj; sourceTree = ""; }; 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = ""; }; + 641DD8EE1F16AA8500B6415F /* RNTesterBundle-macOS.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RNTesterBundle-macOS.bundle"; sourceTree = BUILT_PRODUCTS_DIR; }; 68FF44371CF6111500720EFD /* RCTBundleURLProviderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBundleURLProviderTests.m; sourceTree = ""; }; 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerScenarioTests.m; sourceTree = ""; }; 8385CEF41B873B5C00C6273E /* RCTImageLoaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoaderTests.m; sourceTree = ""; }; @@ -586,6 +861,60 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 18FC77831EF4770B002B3F17 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1897BBD41EF47D8C003C8507 /* libReact.a in Frameworks */, + 6484CE5B201A7557004275A4 /* libRCTBlob-macOS.a in Frameworks */, + 647647321F0BC04800C2D89B /* libART-macOS.a in Frameworks */, + 647647C91F0BCC5500C2D89B /* libRCTAnimation.a in Frameworks */, + 4633DB9F1F212E680080B326 /* libRCTGeolocation-macOS.a in Frameworks */, + 1897BBD01EF47D8C003C8507 /* libRCTImage.a in Frameworks */, + 64481C631F6753960005D62A /* libRCTPushNotification-macOS.a in Frameworks */, + D42902901F1CE21600685AE7 /* libRCTLinking-macOS.a in Frameworks */, + 1897BBD11EF47D8C003C8507 /* libRCTNetwork.a in Frameworks */, + 6448A5D31F292F16006FF1F5 /* libRCTSettings-macOS.a in Frameworks */, + 649D880C1F69DA080005AF18 /* libRCTActionSheet.a in Frameworks */, + 1897BBD21EF47D8C003C8507 /* libRCTText.a in Frameworks */, + 1897BBD31EF47D8C003C8507 /* libRCTWebSocket.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18FC77981EF4770B002B3F17 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 185126F720ADE09F00270B2D /* libRCTImage.a in Frameworks */, + 185126F620ADE07500270B2D /* libRCTWebSocket.a in Frameworks */, + 185126F520ADE06700270B2D /* libRCTNetwork.a in Frameworks */, + 185126F420ADE05000270B2D /* libReact.a in Frameworks */, + 18BFA4C51F01C46D00969486 /* OCMock.framework in Frameworks */, + 649BFB521F0D4CE50078D219 /* libRCTAnimation.a in Frameworks */, + 1834500B1EFAD54F0000CF82 /* libRCTTest-macOS.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18FC77A31EF4770B002B3F17 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 18CF50871F9A67120038A244 /* libART-macOS.a in Frameworks */, + 18CF50831F9A65EE0038A244 /* libReact.a in Frameworks */, + 18CF50891F9A67320038A244 /* libRCTAnimation.a in Frameworks */, + 18CF508A1F9A673E0038A244 /* libRCTGeolocation-macOS.a in Frameworks */, + 18CF50851F9A66E00038A244 /* libRCTImage.a in Frameworks */, + 18CF508B1F9A67500038A244 /* libRCTPushNotification-macOS.a in Frameworks */, + 18CF508C1F9A675A0038A244 /* libRCTLinking-macOS.a in Frameworks */, + 18CF50861F9A66EB0038A244 /* libRCTNetwork.a in Frameworks */, + 18CF508D1F9A676C0038A244 /* libRCTSettings-macOS.a in Frameworks */, + 18CF508E1F9A67760038A244 /* libRCTActionSheet.a in Frameworks */, + 18CF50881F9A671B0038A244 /* libRCTText.a in Frameworks */, + 18CF50841F9A65FC0038A244 /* libRCTWebSocket.a in Frameworks */, + 183938D31F9A571900930D92 /* libRCTTest-macOS.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2D4624D91DA2EA6900C74D09 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -616,6 +945,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 18BA6F461EFC489700E33772 /* libRCTTest-tvOS.a in Frameworks */, 2D4BD8E71DA2E20D005AC8A8 /* libOCMock.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -627,6 +957,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 641DD8E71F16AA8500B6415F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -666,6 +1003,7 @@ isa = PBXGroup; children = ( 13417FE81AA91428003F314A /* libRCTImage.a */, + 18FC77BF1EF4770B002B3F17 /* libRCTImage.a */, 2DD323BB1DA2DD8B000FE1B8 /* libRCTImage-tvOS.a */, ); name = Products; @@ -675,6 +1013,7 @@ isa = PBXGroup; children = ( 13417FEF1AA914B8003F314A /* libRCTText.a */, + 18FC77D11EF4770B002B3F17 /* libRCTText.a */, 2DD323D01DA2DD8B000FE1B8 /* libRCTText-tvOS.a */, ); name = Products; @@ -684,6 +1023,7 @@ isa = PBXGroup; children = ( 1341802B1AA91779003F314A /* libRCTNetwork.a */, + 18FC77C61EF4770B002B3F17 /* libRCTNetwork.a */, 2DD323C31DA2DD8B000FE1B8 /* libRCTNetwork-tvOS.a */, ); name = Products; @@ -693,6 +1033,7 @@ isa = PBXGroup; children = ( 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */, + 4633DB601F207C140080B326 /* libRCTGeolocation-macOS.a */, ); name = Products; sourceTree = ""; @@ -709,9 +1050,11 @@ isa = PBXGroup; children = ( 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */, + 18FC77D71EF4770B002B3F17 /* libRCTWebSocket.a */, 2DD323D51DA2DD8B000FE1B8 /* libRCTWebSocket-tvOS.a */, 3DBE0D331F3B18670099AA32 /* libfishhook.a */, 3DBE0D351F3B18670099AA32 /* libfishhook-tvOS.a */, + 649BF7192019105B0068273E /* libfishhook-macOS.a */, ); name = Products; sourceTree = ""; @@ -735,6 +1078,7 @@ children = ( 13E501A31D07A502005F35D8 /* libRCTAnimation.a */, 2DD323B51DA2DD8B000FE1B8 /* libRCTAnimation.a */, + 647647C81F0BCC5500C2D89B /* libRCTAnimation.a */, ); name = Products; sourceTree = ""; @@ -742,6 +1086,7 @@ 143BC57C1B21E18100462512 /* RNTesterUnitTests */ = { isa = PBXGroup; children = ( + 18BFA4931F01C46700969486 /* OCMock.framework */, 192F69B51E82409A008692C7 /* RCTAnimationUtilsTests.m */, 192F69B61E82409A008692C7 /* RCTConvert_YGValueTests.m */, 192F69B71E82409A008692C7 /* RCTNativeAnimatedNodesManagerTests.m */, @@ -801,6 +1146,7 @@ isa = PBXGroup; children = ( 147CED4B1AB34F8C00DA3E4C /* libRCTActionSheet.a */, + 649D87D91F69DA030005AF18 /* libRCTActionSheet.a */, ); name = Products; sourceTree = ""; @@ -809,30 +1155,30 @@ isa = PBXGroup; children = ( 14AADF041AC3DB95002390C9 /* libReact.a */, + 18FC77E91EF4770B002B3F17 /* libReact.a */, 2DD323D91DA2DD8B000FE1B8 /* libReact.a */, 3D3C08811DE3424E00C268FA /* libyoga.a */, + 18FC77EB1EF4770B002B3F17 /* libyoga.a */, 3D3C08831DE3424E00C268FA /* libyoga.a */, 3D05748C1DE6008900184BB4 /* libcxxreact.a */, + 18FC77ED1EF4770B002B3F17 /* libcxxreact.a */, 3D05748E1DE6008900184BB4 /* libcxxreact.a */, 3D0574901DE6008900184BB4 /* libjschelpers.a */, + 18FC77EF1EF4770B002B3F17 /* libjschelpers.a */, 3D0574921DE6008900184BB4 /* libjschelpers.a */, 3D507F421EBC88B700B56834 /* libthird-party.a */, + 18F73E531EF839CC00FE5F4B /* libthird-party.a */, 2D66FF8C1ECA405900F0A767 /* libthird-party.a */, 3D507F441EBC88B700B56834 /* libdouble-conversion.a */, + 18F73E551EF839CC00FE5F4B /* libdouble-conversion.a */, 2D66FF8E1ECA405900F0A767 /* libdouble-conversion.a */, - AD9FBECC1FD7597F00CADA08 /* libprivatedata.a */, - AD9FBECE1FD7597F00CADA08 /* libprivatedata-tvOS.a */, + 649BF7302019105B0068273E /* libprivatedata.a */, + 649BF7322019105B0068273E /* libprivatedata-tvOS.a */, + 649BF7342019105B0068273E /* libprivatedata-macOS.a */, ); name = Products; sourceTree = ""; }; - 14D6D6EA1B2205C0001FB087 /* OCMock */ = { - isa = PBXGroup; - children = ( - ); - path = OCMock; - sourceTree = ""; - }; 14D6D7011B220AE3001FB087 /* OCMock */ = { isa = PBXGroup; children = ( @@ -854,10 +1200,52 @@ children = ( 14DC67F11AB71876001358AB /* libRCTPushNotification.a */, 3D05746C1DE6008900184BB4 /* libRCTPushNotification-tvOS.a */, + 6424F7B21F669A8E0025D741 /* libRCTPushNotification-macOS.a */, ); name = Products; sourceTree = ""; }; + 18FC77871EF4770B002B3F17 /* RNTester-macOS */ = { + isa = PBXGroup; + children = ( + 18FC77881EF4770B002B3F17 /* AppDelegate.h */, + 18FC77891EF4770B002B3F17 /* AppDelegate.m */, + 18FC778E1EF4770B002B3F17 /* ViewController.h */, + 18FC778F1EF4770B002B3F17 /* ViewController.m */, + 18FC77911EF4770B002B3F17 /* Assets.xcassets */, + 18FC77931EF4770B002B3F17 /* Main.storyboard */, + 18FC77961EF4770B002B3F17 /* Info.plist */, + 18FC778B1EF4770B002B3F17 /* Supporting Files */, + ); + path = "RNTester-macOS"; + sourceTree = ""; + }; + 18FC778B1EF4770B002B3F17 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 18FC778C1EF4770B002B3F17 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 18FC779E1EF4770B002B3F17 /* RNTester-macOSUnitTests */ = { + isa = PBXGroup; + children = ( + 18FC779F1EF4770B002B3F17 /* RNTesterUnitTests_macOS.m */, + 18FC77A11EF4770B002B3F17 /* Info.plist */, + ); + path = "RNTester-macOSUnitTests"; + sourceTree = ""; + }; + 18FC77A91EF4770B002B3F17 /* RNTester-macOSIntegrationTests */ = { + isa = PBXGroup; + children = ( + 18FC77AA1EF4770B002B3F17 /* RNTesterIntegrationTests_macOS.m */, + 18FC77AC1EF4770B002B3F17 /* Info.plist */, + ); + path = "RNTester-macOSIntegrationTests"; + sourceTree = ""; + }; 272E6B3A1BEA846C001FCF37 /* NativeExampleViews */ = { isa = PBXGroup; children = ( @@ -874,6 +1262,7 @@ children = ( 2D66FF651ECA405900F0A767 /* libART.a */, 2D66FF671ECA405900F0A767 /* libART-tvOS.a */, + 647647311F0BC04800C2D89B /* libART-macOS.a */, ); name = Products; sourceTree = ""; @@ -891,6 +1280,7 @@ children = ( 357859011B28D2C500341EDB /* libRCTLinking.a */, 2DD323BF1DA2DD8B000FE1B8 /* libRCTLinking-tvOS.a */, + D429028F1F1CE21600685AE7 /* libRCTLinking-macOS.a */, ); name = Products; sourceTree = ""; @@ -911,6 +1301,7 @@ children = ( 5281CA511EEAC9A700AC40CD /* libRCTBlob.a */, 5281CA531EEAC9A700AC40CD /* libRCTBlob-tvOS.a */, + 6484CE5A201A7557004275A4 /* libRCTBlob-macOS.a */, ); name = Products; sourceTree = ""; @@ -920,15 +1311,24 @@ children = ( 58005BEE1ABA80530062E044 /* libRCTTest.a */, 2DD323CC1DA2DD8B000FE1B8 /* libRCTTest-tvOS.a */, + 18DF57271EFA056600BF4666 /* libRCTTest-macOS.a */, ); name = Products; sourceTree = ""; }; + 6424F7AD1F669A8E0025D741 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; 834C36CE1AF8DA610019C93C /* Products */ = { isa = PBXGroup; children = ( 834C36D21AF8DA610019C93C /* libRCTSettings.a */, 2DD323C81DA2DD8B000FE1B8 /* libRCTSettings-tvOS.a */, + 6448A5D21F292F16006FF1F5 /* libRCTSettings-macOS.a */, ); name = Products; sourceTree = ""; @@ -941,9 +1341,12 @@ 143BC57C1B21E18100462512 /* RNTesterUnitTests */, 143BC5961B21E3E100462512 /* RNTesterIntegrationTests */, 3D13F83F1D6F6AE000E69E0E /* RNTesterBundle */, - 14D6D6EA1B2205C0001FB087 /* OCMock */, 2DD323911DA2DD8B000FE1B8 /* RNTester-tvOS */, + 18FC77871EF4770B002B3F17 /* RNTester-macOS */, + 18FC779E1EF4770B002B3F17 /* RNTester-macOSUnitTests */, + 18FC77A91EF4770B002B3F17 /* RNTester-macOSIntegrationTests */, 83CBBA001A601CBA00E9B192 /* Products */, + 6424F7AD1F669A8E0025D741 /* Frameworks */, ); indentWidth = 2; sourceTree = ""; @@ -960,6 +1363,10 @@ 2DD323901DA2DD8A000FE1B8 /* RNTester-tvOS.app */, 2DD323A51DA2DD8B000FE1B8 /* RNTester-tvOSUnitTests.xctest */, 2D4624E01DA2EA6900C74D09 /* RNTester-tvOSIntegrationTests.xctest */, + 18FC77861EF4770B002B3F17 /* RNTester-macOS.app */, + 18FC779B1EF4770B002B3F17 /* RNTester-macOSUnitTests.xctest */, + 18FC77A61EF4770B002B3F17 /* RNTester-macOSIntegrationTests.xctest */, + 641DD8EE1F16AA8500B6415F /* RNTesterBundle-macOS.bundle */, ); name = Products; sourceTree = ""; @@ -1029,6 +1436,63 @@ productReference = 143BC5951B21E3E100462512 /* RNTesterIntegrationTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 18FC77851EF4770B002B3F17 /* RNTester-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 18FC77F01EF4770B002B3F17 /* Build configuration list for PBXNativeTarget "RNTester-macOS" */; + buildPhases = ( + 18FC77821EF4770B002B3F17 /* Sources */, + 18FC77831EF4770B002B3F17 /* Frameworks */, + 18FC77841EF4770B002B3F17 /* Resources */, + 64BF1F6D1EF848FB00B83E07 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 641DD8F21F16B77700B6415F /* PBXTargetDependency */, + ); + name = "RNTester-macOS"; + productName = "RNTester-macOS"; + productReference = 18FC77861EF4770B002B3F17 /* RNTester-macOS.app */; + productType = "com.apple.product-type.application"; + }; + 18FC779A1EF4770B002B3F17 /* RNTester-macOSUnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 18FC77F11EF4770B002B3F17 /* Build configuration list for PBXNativeTarget "RNTester-macOSUnitTests" */; + buildPhases = ( + 18FC77971EF4770B002B3F17 /* Sources */, + 18FC77981EF4770B002B3F17 /* Frameworks */, + 18FC77991EF4770B002B3F17 /* Resources */, + 18BFA4C91F01C4F900969486 /* Copy Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 18FC779D1EF4770B002B3F17 /* PBXTargetDependency */, + ); + name = "RNTester-macOSUnitTests"; + productName = "RNTester-macOSTests"; + productReference = 18FC779B1EF4770B002B3F17 /* RNTester-macOSUnitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 18FC77A51EF4770B002B3F17 /* RNTester-macOSIntegrationTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 18FC77F21EF4770B002B3F17 /* Build configuration list for PBXNativeTarget "RNTester-macOSIntegrationTests" */; + buildPhases = ( + 18FC77A21EF4770B002B3F17 /* Sources */, + 18FC77A31EF4770B002B3F17 /* Frameworks */, + 18FC77A41EF4770B002B3F17 /* Resources */, + 185127A120ADEF4800270B2D /* Modify xctest.xcent file */, + ); + buildRules = ( + ); + dependencies = ( + 18FC77A81EF4770B002B3F17 /* PBXTargetDependency */, + ); + name = "RNTester-macOSIntegrationTests"; + productName = "RNTester-macOSUITests"; + productReference = 18FC77A61EF4770B002B3F17 /* RNTester-macOSIntegrationTests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; 2D4624C11DA2EA6900C74D09 /* RNTester-tvOSIntegrationTests */ = { isa = PBXNativeTarget; buildConfigurationList = 2D4624DD1DA2EA6900C74D09 /* Build configuration list for PBXNativeTarget "RNTester-tvOSIntegrationTests" */; @@ -1100,6 +1564,23 @@ productReference = 3D13F83E1D6F6AE000E69E0E /* RNTesterBundle.bundle */; productType = "com.apple.product-type.bundle"; }; + 641DD8E51F16AA8500B6415F /* RNTesterBundle-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 641DD8EB1F16AA8500B6415F /* Build configuration list for PBXNativeTarget "RNTesterBundle-macOS" */; + buildPhases = ( + 641DD8E61F16AA8500B6415F /* Sources */, + 641DD8E71F16AA8500B6415F /* Frameworks */, + 641DD8E81F16AA8500B6415F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RNTesterBundle-macOS"; + productName = RNTesterBundle; + productReference = 641DD8EE1F16AA8500B6415F /* RNTesterBundle-macOS.bundle */; + productType = "com.apple.product-type.bundle"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1116,6 +1597,20 @@ CreatedOnToolsVersion = 6.3.2; TestTargetID = 13B07F861A680F5B00A75B9A; }; + 18FC77851EF4770B002B3F17 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + }; + 18FC779A1EF4770B002B3F17 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + TestTargetID = 18FC77851EF4770B002B3F17; + }; + 18FC77A51EF4770B002B3F17 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + TestTargetID = 18FC77851EF4770B002B3F17; + }; 2DD3238F1DA2DD8A000FE1B8 = { CreatedOnToolsVersion = 8.0; ProvisioningStyle = Automatic; @@ -1216,6 +1711,10 @@ 2DD3238F1DA2DD8A000FE1B8 /* RNTester-tvOS */, 2DD323A41DA2DD8B000FE1B8 /* RNTester-tvOSUnitTests */, 2D4624C11DA2EA6900C74D09 /* RNTester-tvOSIntegrationTests */, + 18FC77851EF4770B002B3F17 /* RNTester-macOS */, + 18FC779A1EF4770B002B3F17 /* RNTester-macOSUnitTests */, + 18FC77A51EF4770B002B3F17 /* RNTester-macOSIntegrationTests */, + 641DD8E51F16AA8500B6415F /* RNTesterBundle-macOS */, ); }; /* End PBXProject section */ @@ -1291,6 +1790,83 @@ remoteRef = 14DC67F01AB71876001358AB /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 18DF57271EFA056600BF4666 /* libRCTTest-macOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTTest-macOS.a"; + remoteRef = 18DF57261EFA056600BF4666 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 18F73E531EF839CC00FE5F4B /* libthird-party.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libthird-party.a"; + remoteRef = 18F73E521EF839CC00FE5F4B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 18F73E551EF839CC00FE5F4B /* libdouble-conversion.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libdouble-conversion.a"; + remoteRef = 18F73E541EF839CC00FE5F4B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 18FC77BF1EF4770B002B3F17 /* libRCTImage.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTImage.a; + remoteRef = 18FC77BE1EF4770B002B3F17 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 18FC77C61EF4770B002B3F17 /* libRCTNetwork.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTNetwork.a; + remoteRef = 18FC77C51EF4770B002B3F17 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 18FC77D11EF4770B002B3F17 /* libRCTText.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTText.a; + remoteRef = 18FC77D01EF4770B002B3F17 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 18FC77D71EF4770B002B3F17 /* libRCTWebSocket.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTWebSocket.a; + remoteRef = 18FC77D61EF4770B002B3F17 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 18FC77E91EF4770B002B3F17 /* libReact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libReact.a; + remoteRef = 18FC77E81EF4770B002B3F17 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 18FC77EB1EF4770B002B3F17 /* libyoga.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libyoga.a; + remoteRef = 18FC77EA1EF4770B002B3F17 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 18FC77ED1EF4770B002B3F17 /* libcxxreact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libcxxreact.a; + remoteRef = 18FC77EC1EF4770B002B3F17 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 18FC77EF1EF4770B002B3F17 /* libjschelpers.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libjschelpers.a; + remoteRef = 18FC77EE1EF4770B002B3F17 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 2D66FF651ECA405900F0A767 /* libART.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -1466,6 +2042,13 @@ remoteRef = 3DBE0D341F3B18670099AA32 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 4633DB601F207C140080B326 /* libRCTGeolocation-macOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTGeolocation-macOS.a"; + remoteRef = 4633DB5F1F207C140080B326 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 5281CA511EEAC9A700AC40CD /* libRCTBlob.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -1487,25 +2070,88 @@ remoteRef = 58005BED1ABA80530062E044 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 834C36D21AF8DA610019C93C /* libRCTSettings.a */ = { + 6424F7B21F669A8E0025D741 /* libRCTPushNotification-macOS.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = libRCTSettings.a; - remoteRef = 834C36D11AF8DA610019C93C /* PBXContainerItemProxy */; + path = "libRCTPushNotification-macOS.a"; + remoteRef = 6424F7B11F669A8E0025D741 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 6448A5D21F292F16006FF1F5 /* libRCTSettings-macOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTSettings-macOS.a"; + remoteRef = 6448A5D11F292F16006FF1F5 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 647647311F0BC04800C2D89B /* libART-macOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libART-macOS.a"; + remoteRef = 647647301F0BC04800C2D89B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 647647C81F0BCC5500C2D89B /* libRCTAnimation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTAnimation.a; + remoteRef = 647647C71F0BCC5500C2D89B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 6484CE5A201A7557004275A4 /* libRCTBlob-macOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTBlob-macOS.a"; + remoteRef = 6484CE59201A7557004275A4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 649BF7192019105B0068273E /* libfishhook-macOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libfishhook-macOS.a"; + remoteRef = 649BF7182019105B0068273E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - AD9FBECC1FD7597F00CADA08 /* libprivatedata.a */ = { + 649BF7302019105B0068273E /* libprivatedata.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libprivatedata.a; - remoteRef = AD9FBECB1FD7597F00CADA08 /* PBXContainerItemProxy */; + remoteRef = 649BF72F2019105B0068273E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - AD9FBECE1FD7597F00CADA08 /* libprivatedata-tvOS.a */ = { + 649BF7322019105B0068273E /* libprivatedata-tvOS.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = "libprivatedata-tvOS.a"; - remoteRef = AD9FBECD1FD7597F00CADA08 /* PBXContainerItemProxy */; + remoteRef = 649BF7312019105B0068273E /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 649BF7342019105B0068273E /* libprivatedata-macOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libprivatedata-macOS.a"; + remoteRef = 649BF7332019105B0068273E /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 649D87D91F69DA030005AF18 /* libRCTActionSheet.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTActionSheet.a; + remoteRef = 649D87D81F69DA030005AF18 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 834C36D21AF8DA610019C93C /* libRCTSettings.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTSettings.a; + remoteRef = 834C36D11AF8DA610019C93C /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + D429028F1F1CE21600685AE7 /* libRCTLinking-macOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTLinking-macOS.a"; + remoteRef = D429028E1F1CE21600685AE7 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */ = { @@ -1544,6 +2190,33 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 18FC77841EF4770B002B3F17 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 641DD9231F16B90100B6415F /* Assets.xcassets in Resources */, + 641DD8F01F16AB0600B6415F /* RNTesterBundle-macOS.bundle in Resources */, + 18FC77951EF4770B002B3F17 /* Main.storyboard in Resources */, + 641DD8B11F16A59800B6415F /* legacy_image@2x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18FC77991EF4770B002B3F17 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 18316DFE1EF9DA43003DADF3 /* RNTesterUnitTestsBundle.js in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18FC77A41EF4770B002B3F17 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 18CF508F1F9A6A3B0038A244 /* RNTesterBundle-macOS.bundle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2D4624DB1DA2EA6900C74D09 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1578,9 +2251,33 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 641DD8E81F16AA8500B6415F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 641DD8E91F16AA8500B6415F /* ImageInBundle.png in Resources */, + 641DD8EA1F16AA8500B6415F /* OtherImages.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 185127A120ADEF4800270B2D /* Modify xctest.xcent file */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(PROJECT_DIR)/scripts/ModifyXcuitestEntitlements.py", + ); + name = "Modify xctest.xcent file"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/bin/env python \"${SCRIPT_INPUT_FILE_0}\""; + }; 2DD323EB1DA2DEC1000FE1B8 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1594,6 +2291,19 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n$SRCROOT/../scripts/react-native-xcode.sh RNTester/js/RNTesterApp.ios.js"; }; + 64BF1F6D1EF848FB00B83E07 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export NODE_BINARY=node\n$SRCROOT/../scripts/react-native-xcode.sh RNTester/js/RNTesterApp.macos.js"; + }; 68CD48B71D2BCB2C007E06A9 /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1666,6 +2376,60 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 18FC77821EF4770B002B3F17 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 18FC77901EF4770B002B3F17 /* ViewController.m in Sources */, + 18FC778D1EF4770B002B3F17 /* main.m in Sources */, + 18FC778A1EF4770B002B3F17 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18FC77971EF4770B002B3F17 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 18316E011EF9DC9C003DADF3 /* RCTConvert_NSURLTests.m in Sources */, + 649BFB1F1F0D4C870078D219 /* RCTAnimationUtilsTests.m in Sources */, + 18BFA4CB1F01C52400969486 /* RCTEventDispatcherTests.m in Sources */, + 18D593801EF9FA48005F0CEC /* RCTShadowViewTests.m in Sources */, + 18316E1B1EF9EB7D003DADF3 /* RCTAllocationTests.m in Sources */, + 18D5937E1EF9FA22005F0CEC /* RCTModuleMethodTests.mm in Sources */, + 18FC77A01EF4770B002B3F17 /* RNTesterUnitTests_macOS.m in Sources */, + 18D5937A1EF9F95A005F0CEC /* RCTJSONTests.m in Sources */, + 18DF57111EFA047400BF4666 /* RCTComponentPropsTests.m in Sources */, + 18AA4BD41EF9C724008C7756 /* RCTURLUtilsTests.m in Sources */, + 18D593811EF9FA57005F0CEC /* RCTUIManagerTests.m in Sources */, + 18D593841EF9FAB9005F0CEC /* RCTUnicodeDecodeTests.m in Sources */, + 18D593781EF9F8CB005F0CEC /* RCTImageLoaderTests.m in Sources */, + 18D5937F1EF9FA39005F0CEC /* RCTMultipartStreamReaderTests.m in Sources */, + 18D5937B1EF9F96A005F0CEC /* RCTMethodArgumentTests.m in Sources */, + 18316DFD1EF9D996003DADF3 /* RCTBundleURLProviderTests.m in Sources */, + 18D593791EF9F8DA005F0CEC /* RCTImageUtilTests.m in Sources */, + 649BFB591F0D54B60078D219 /* RCTNativeAnimatedNodesManagerTests.m in Sources */, + 180B9CC41F01B040006AF028 /* RCTModuleInitNotificationRaceTests.m in Sources */, + 18316E041EF9EAD1003DADF3 /* RCTGzipTests.m in Sources */, + 18316E021EF9E0DE003DADF3 /* RCTFontTests.m in Sources */, + 18D5934D1EF9F8BC005F0CEC /* RCTImageLoaderHelpers.m in Sources */, + 18316DFC1EF9D981003DADF3 /* RCTConvert_YGValueTests.m in Sources */, + 18D5937D1EF9F990005F0CEC /* RCTModuleInitTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 18FC77A21EF4770B002B3F17 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 18FC77AB1EF4770B002B3F17 /* RNTesterIntegrationTests_macOS.m in Sources */, + 1827ADEF20ACEB7100E266FC /* RNTesterIntegrationTests.m in Sources */, + 18D9497D1F9AC3D0007BB668 /* RCTUIManagerScenarioTests.m in Sources */, + 1827ADAD20ACEA6600E266FC /* RCTLoggingTests.m in Sources */, + 183938D01F9A55AE00930D92 /* RNTesterTestModule.m in Sources */, + 18D949451F9AC375007BB668 /* RCTRootViewIntegrationTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2D4624C41DA2EA6900C74D09 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1723,6 +2487,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 641DD8E61F16AA8500B6415F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1731,6 +2502,16 @@ target = 13B07F861A680F5B00A75B9A /* RNTester */; targetProxy = 143BC59B1B21E3E100462512 /* PBXContainerItemProxy */; }; + 18FC779D1EF4770B002B3F17 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 18FC77851EF4770B002B3F17 /* RNTester-macOS */; + targetProxy = 18FC779C1EF4770B002B3F17 /* PBXContainerItemProxy */; + }; + 18FC77A81EF4770B002B3F17 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 18FC77851EF4770B002B3F17 /* RNTester-macOS */; + targetProxy = 18FC77A71EF4770B002B3F17 /* PBXContainerItemProxy */; + }; 2D4624C21DA2EA6900C74D09 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 2DD3238F1DA2DD8A000FE1B8 /* RNTester-tvOS */; @@ -1746,6 +2527,11 @@ target = 3D13F83D1D6F6AE000E69E0E /* RNTesterBundle */; targetProxy = 3D13F84B1D6F6B5F00E69E0E /* PBXContainerItemProxy */; }; + 641DD8F21F16B77700B6415F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 641DD8E51F16AA8500B6415F /* RNTesterBundle-macOS */; + targetProxy = 641DD8F11F16B77700B6415F /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -1758,12 +2544,24 @@ path = RNTester; sourceTree = ""; }; + 18FC77931EF4770B002B3F17 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 18FC77941EF4770B002B3F17 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 004D28A61AAF61C70097A701 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/RNTesterUnitTests", + ); GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1784,6 +2582,10 @@ 004D28A71AAF61C70097A701 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/RNTesterUnitTests", + ); HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/RNTesterUnitTests", @@ -1860,6 +2662,123 @@ }; name = Release; }; + 18FC77AD1EF4770B002B3F17 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + INFOPLIST_FILE = "RNTester-macOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "RCT.RNTester-macOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + 18FC77AE1EF4770B002B3F17 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + INFOPLIST_FILE = "RNTester-macOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "RCT.RNTester-macOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; + 18FC77AF1EF4770B002B3F17 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/RNTesterUnitTests", + ); + INFOPLIST_FILE = "RNTester-macOSUnitTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_BUNDLE_IDENTIFIER = "RCT.RNTester-macOSUnitTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + 18FC77B01EF4770B002B3F17 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/RNTesterUnitTests", + ); + INFOPLIST_FILE = "RNTester-macOSUnitTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_BUNDLE_IDENTIFIER = "RCT.RNTester-macOSUnitTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; + 18FC77B11EF4770B002B3F17 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + INFOPLIST_FILE = "RNTester-macOSIntegrationTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_BUNDLE_IDENTIFIER = "RCT.RNTester-macOSIntegrationTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + SDKROOT = macosx; + TEST_TARGET_NAME = "RNTester-macOS"; + }; + name = Debug; + }; + 18FC77B21EF4770B002B3F17 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + INFOPLIST_FILE = "RNTester-macOSIntegrationTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_BUNDLE_IDENTIFIER = "RCT.RNTester-macOSIntegrationTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + SDKROOT = macosx; + TEST_TARGET_NAME = "RNTester-macOS"; + }; + name = Release; + }; 2D4624DE1DA2EA6900C74D09 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2033,6 +2952,36 @@ }; name = Release; }; + 641DD8EC1F16AA8500B6415F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + COMBINE_HIDPI_IMAGES = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = RNTester/RNTesterBundle/Info.plist; + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.RNTesterBundle; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 641DD8ED1F16AA8500B6415F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + COMBINE_HIDPI_IMAGES = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = RNTester/RNTesterBundle/Info.plist; + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.RNTesterBundle; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; 83CBBA201A601CBA00E9B192 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2067,7 +3016,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", - "$(inherited)", + "FB_REFERENCE_IMAGE_DIR=\"\\\"$(SOURCE_ROOT)/$(PROJECT_NAME)IntegrationTests/ReferenceImages\\\"\"", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; @@ -2086,6 +3035,7 @@ GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = ( @@ -2147,6 +3097,7 @@ GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = ( "-ObjC", @@ -2192,6 +3143,33 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 18FC77F01EF4770B002B3F17 /* Build configuration list for PBXNativeTarget "RNTester-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 18FC77AD1EF4770B002B3F17 /* Debug */, + 18FC77AE1EF4770B002B3F17 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 18FC77F11EF4770B002B3F17 /* Build configuration list for PBXNativeTarget "RNTester-macOSUnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 18FC77AF1EF4770B002B3F17 /* Debug */, + 18FC77B01EF4770B002B3F17 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 18FC77F21EF4770B002B3F17 /* Build configuration list for PBXNativeTarget "RNTester-macOSIntegrationTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 18FC77B11EF4770B002B3F17 /* Debug */, + 18FC77B21EF4770B002B3F17 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 2D4624DD1DA2EA6900C74D09 /* Build configuration list for PBXNativeTarget "RNTester-tvOSIntegrationTests" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2228,6 +3206,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 641DD8EB1F16AA8500B6415F /* Build configuration list for PBXNativeTarget "RNTesterBundle-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 641DD8EC1F16AA8500B6415F /* Debug */, + 641DD8ED1F16AA8500B6415F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "RNTester" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester Release.xcscheme b/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester Release.xcscheme new file mode 100644 index 00000000000000..a6b8c7e7d11334 --- /dev/null +++ b/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester Release.xcscheme @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester-macOS Release.xcscheme b/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester-macOS Release.xcscheme new file mode 100644 index 00000000000000..77510b3e3cae54 --- /dev/null +++ b/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester-macOS Release.xcscheme @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester-macOS.xcscheme b/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester-macOS.xcscheme new file mode 100644 index 00000000000000..efd1a43c7b176c --- /dev/null +++ b/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester-macOS.xcscheme @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester.xcscheme b/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester.xcscheme index 93f81fdc17a4f5..aadcd090037546 100644 --- a/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester.xcscheme +++ b/RNTester/RNTester.xcodeproj/xcshareddata/xcschemes/RNTester.xcscheme @@ -82,6 +82,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + @@ -121,6 +127,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/RNTester/RNTesterIntegrationTests/RCTLoggingTests.m b/RNTester/RNTesterIntegrationTests/RCTLoggingTests.m index 4f23baf0470dab..9467c7c017e0d8 100644 --- a/RNTester/RNTesterIntegrationTests/RCTLoggingTests.m +++ b/RNTester/RNTesterIntegrationTests/RCTLoggingTests.m @@ -13,6 +13,7 @@ #import #import #import +#import @interface RCTLoggingTests : XCTestCase @@ -33,7 +34,7 @@ - (void)setUp NSURL *scriptURL; if (getenv("CI_USE_PACKAGER")) { NSString *app = @"IntegrationTests/IntegrationTestsApp"; - scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]]; + scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=%@&dev=true", app, kRCTPlatformName]]; } else { scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"]; } diff --git a/RNTester/RNTesterIntegrationTests/RCTRootViewIntegrationTests.m b/RNTester/RNTesterIntegrationTests/RCTRootViewIntegrationTests.m index 364a2b956202d4..150ffef8759faf 100644 --- a/RNTester/RNTesterIntegrationTests/RCTRootViewIntegrationTests.m +++ b/RNTester/RNTesterIntegrationTests/RCTRootViewIntegrationTests.m @@ -8,7 +8,7 @@ * */ -#import +#import #import #import diff --git a/RNTester/RNTesterIntegrationTests/RCTUIManagerScenarioTests.m b/RNTester/RNTesterIntegrationTests/RCTUIManagerScenarioTests.m index 4e23f431ff8cc7..fb23b721a8f826 100644 --- a/RNTester/RNTesterIntegrationTests/RCTUIManagerScenarioTests.m +++ b/RNTester/RNTesterIntegrationTests/RCTUIManagerScenarioTests.m @@ -8,7 +8,7 @@ * */ -#import +#import #import #import diff --git a/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m b/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m index 6a09c5e1ee5930..626a23aa952a3d 100644 --- a/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m +++ b/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import +#import #import #import @@ -74,10 +74,14 @@ - (void)testTheTester_ExpectError RCT_TEST(SyncMethodTest) RCT_TEST(PromiseTest) RCT_TEST_ONLY_WITH_PACKAGER(WebSocketTest) +#if !TARGET_OS_OSX // ios specific RCT_TEST(AccessibilityManagerTest) +#endif #if !TARGET_OS_TV // tvOS does not fully support WebView +#if !TARGET_OS_OSX // VSO#2332957: macOS WebViewTest fails RCT_TEST(WebViewTest) #endif +#endif @end diff --git a/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m b/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m index 9aad75e46f2df2..8e83c477fcbbfc 100644 --- a/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m +++ b/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m @@ -8,7 +8,7 @@ * */ -#import +#import #import #import @@ -24,12 +24,16 @@ @implementation RNTesterSnapshotTests - (void)setUp { +#if !TARGET_OS_OSX _runner = RCTInitRunnerForApp(@"RNTester/js/RNTesterApp.ios", nil); if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 11) { _runner.testSuffix = @"-iOS11"; } else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10) { _runner.testSuffix = @"-iOS10"; } +#else // TARGET_OS_OSX + _runner = RCTInitRunnerForApp(@"RNTester/js/RNTesterApp.macos", nil); +#endif _runner.recordMode = NO; } @@ -44,6 +48,13 @@ - (void)test##name \ RCT_TEST(ARTExample) RCT_TEST(ScrollViewExample) RCT_TEST(TextExample) +#if !TARGET_OS_TV +// No switch or slider available on tvOS +RCT_TEST(SwitchExample) +RCT_TEST(SliderExample) +// TabBarExample on tvOS passes locally but not on Travis +RCT_TEST(TabBarExample) +#endif - (void)testZZZNotInRecordMode { diff --git a/RNTester/RNTesterIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testImageSnapshotTest_1@2x.png b/RNTester/RNTesterIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testImageSnapshotTest_1@2x.png index 41492574c58749..adf8f217a5dd75 100644 Binary files a/RNTester/RNTesterIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testImageSnapshotTest_1@2x.png and b/RNTester/RNTesterIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testImageSnapshotTest_1@2x.png differ diff --git a/RNTester/RNTesterIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testImageSnapshotTest_1_macOS.png b/RNTester/RNTesterIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testImageSnapshotTest_1_macOS.png new file mode 100644 index 00000000000000..d4803c93429c63 Binary files /dev/null and b/RNTester/RNTesterIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testImageSnapshotTest_1_macOS.png differ diff --git a/RNTester/RNTesterIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testSimpleSnapshotTest_1_macOS.png b/RNTester/RNTesterIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testSimpleSnapshotTest_1_macOS.png new file mode 100644 index 00000000000000..ead694d19a12f2 Binary files /dev/null and b/RNTester/RNTesterIntegrationTests/ReferenceImages/IntegrationTests-IntegrationTestsApp/testSimpleSnapshotTest_1_macOS.png differ diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Headers b/RNTester/RNTesterUnitTests/OCMock.framework/Headers new file mode 120000 index 00000000000000..a177d2a6b92600 --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Modules b/RNTester/RNTesterUnitTests/OCMock.framework/Modules new file mode 120000 index 00000000000000..5736f3186e797b --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/OCMock b/RNTester/RNTesterUnitTests/OCMock.framework/OCMock new file mode 120000 index 00000000000000..c388ea8df9e614 --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/OCMock @@ -0,0 +1 @@ +Versions/Current/OCMock \ No newline at end of file diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Resources b/RNTester/RNTesterUnitTests/OCMock.framework/Resources new file mode 120000 index 00000000000000..953ee36f3bb709 --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/NSNotificationCenter+OCMAdditions.h b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/NSNotificationCenter+OCMAdditions.h new file mode 100644 index 00000000000000..7d58aabea84f2e --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/NSNotificationCenter+OCMAdditions.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2009-2016 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import + +@class OCObserverMockObject; + + +@interface NSNotificationCenter(OCMAdditions) + +- (void)addMockObserver:(OCObserverMockObject *)notificationObserver name:(NSString *)notificationName object:(id)notificationSender; + +@end diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMArg.h b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMArg.h new file mode 100644 index 00000000000000..6df735e99ef9ad --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMArg.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2009-2016 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import + +@interface OCMArg : NSObject + +// constraining arguments + ++ (id)any; ++ (SEL)anySelector; ++ (void *)anyPointer; ++ (id __autoreleasing *)anyObjectRef; ++ (id)isNil; ++ (id)isNotNil; ++ (id)isEqual:(id)value; ++ (id)isNotEqual:(id)value; ++ (id)isKindOfClass:(Class)cls; ++ (id)checkWithSelector:(SEL)selector onObject:(id)anObject; ++ (id)checkWithBlock:(BOOL (^)(id obj))block; + +// manipulating arguments + ++ (id *)setTo:(id)value; ++ (void *)setToValue:(NSValue *)value; ++ (id)invokeBlock; ++ (id)invokeBlockWithArgs:(id)first,... NS_REQUIRES_NIL_TERMINATION; + ++ (id)defaultValue; + +// internal use only + ++ (id)resolveSpecialValues:(NSValue *)value; + +@end + +#define OCMOCK_ANY [OCMArg any] + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) + #define OCMOCK_VALUE(variable) \ + ({ __typeof__(variable) __v = (variable); [NSValue value:&__v withObjCType:@encode(__typeof__(__v))]; }) +#else + #define OCMOCK_VALUE(variable) [NSValue value:&variable withObjCType:@encode(__typeof__(variable))] +#endif + diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMConstraint.h b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMConstraint.h new file mode 100644 index 00000000000000..19fc1a713cdb4c --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMConstraint.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2007-2016 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import + + +@interface OCMConstraint : NSObject + ++ (instancetype)constraint; +- (BOOL)evaluate:(id)value; + +// if you are looking for any, isNil, etc, they have moved to OCMArg + +// try to use [OCMArg checkWith...] instead of the constraintWith... methods below + ++ (instancetype)constraintWithSelector:(SEL)aSelector onObject:(id)anObject; ++ (instancetype)constraintWithSelector:(SEL)aSelector onObject:(id)anObject withValue:(id)aValue; + + +@end + +@interface OCMAnyConstraint : OCMConstraint +@end + +@interface OCMIsNilConstraint : OCMConstraint +@end + +@interface OCMIsNotNilConstraint : OCMConstraint +@end + +@interface OCMIsNotEqualConstraint : OCMConstraint +{ + @public + id testValue; +} + +@end + +@interface OCMInvocationConstraint : OCMConstraint +{ + @public + NSInvocation *invocation; +} + +@end + +@interface OCMBlockConstraint : OCMConstraint +{ + BOOL (^block)(id); +} + +- (instancetype)initWithConstraintBlock:(BOOL (^)(id))block; + +@end + + +#define CONSTRAINT(aSelector) [OCMConstraint constraintWithSelector:aSelector onObject:self] +#define CONSTRAINTV(aSelector, aValue) [OCMConstraint constraintWithSelector:aSelector onObject:self withValue:(aValue)] diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMFunctions.h b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMFunctions.h new file mode 100644 index 00000000000000..b0c2df353c583b --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMFunctions.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014-2016 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import + + +#if defined(__cplusplus) +#define OCMOCK_EXTERN extern "C" +#else +#define OCMOCK_EXTERN extern +#endif + + +OCMOCK_EXTERN BOOL OCMIsObjectType(const char *objCType); diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMLocation.h b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMLocation.h new file mode 100644 index 00000000000000..7870c5297838bd --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMLocation.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014-2016 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import +#import "OCMFunctions.h" + + +@interface OCMLocation : NSObject +{ + id testCase; + NSString *file; + NSUInteger line; +} + ++ (instancetype)locationWithTestCase:(id)aTestCase file:(NSString *)aFile line:(NSUInteger)aLine; + +- (instancetype)initWithTestCase:(id)aTestCase file:(NSString *)aFile line:(NSUInteger)aLine; + +- (id)testCase; +- (NSString *)file; +- (NSUInteger)line; + +@end + +OCMOCK_EXTERN OCMLocation *OCMMakeLocation(id testCase, const char *file, int line); diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMMacroState.h b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMMacroState.h new file mode 100644 index 00000000000000..dba41bebdc0ada --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMMacroState.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014-2016 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import + +@class OCMLocation; +@class OCMRecorder; +@class OCMStubRecorder; +@class OCMockObject; + + +@interface OCMMacroState : NSObject +{ + OCMRecorder *recorder; +} + ++ (void)beginStubMacro; ++ (OCMStubRecorder *)endStubMacro; + ++ (void)beginExpectMacro; ++ (OCMStubRecorder *)endExpectMacro; + ++ (void)beginRejectMacro; ++ (OCMStubRecorder *)endRejectMacro; + ++ (void)beginVerifyMacroAtLocation:(OCMLocation *)aLocation; ++ (void)endVerifyMacro; + ++ (OCMMacroState *)globalState; + +- (OCMRecorder *)recorder; + +- (void)switchToClassMethod; + +@end diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMRecorder.h b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMRecorder.h new file mode 100644 index 00000000000000..9670d085f4bd07 --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMRecorder.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014-2016 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import + +@class OCMockObject; +@class OCMInvocationMatcher; + + +@interface OCMRecorder : NSProxy +{ + OCMockObject *mockObject; + OCMInvocationMatcher *invocationMatcher; +} + +- (instancetype)init; +- (instancetype)initWithMockObject:(OCMockObject *)aMockObject; + +- (void)setMockObject:(OCMockObject *)aMockObject; + +- (OCMInvocationMatcher *)invocationMatcher; + +- (id)classMethod; +- (id)ignoringNonObjectArgs; + +@end diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMStubRecorder.h b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMStubRecorder.h new file mode 100644 index 00000000000000..e32029fc222a78 --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMStubRecorder.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2004-2016 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import +#import +#import + +@interface OCMStubRecorder : OCMRecorder + +- (id)andReturn:(id)anObject; +- (id)andReturnValue:(NSValue *)aValue; +- (id)andThrow:(NSException *)anException; +- (id)andPost:(NSNotification *)aNotification; +- (id)andCall:(SEL)selector onObject:(id)anObject; +- (id)andDo:(void (^)(NSInvocation *invocation))block; +- (id)andForwardToRealObject; + +@end + + +@interface OCMStubRecorder (Properties) + +#define andReturn(aValue) _andReturn(({ \ + __typeof__(aValue) _val = (aValue); \ + NSValue *_nsval = [NSValue value:&_val withObjCType:@encode(__typeof__(_val))]; \ + if (OCMIsObjectType(@encode(__typeof(_val)))) { \ + objc_setAssociatedObject(_nsval, "OCMAssociatedBoxedValue", *(__unsafe_unretained id *) (void *) &_val, OBJC_ASSOCIATION_RETAIN); \ + } \ + _nsval; \ +})) +@property (nonatomic, readonly) OCMStubRecorder *(^ _andReturn)(NSValue *); + +#define andThrow(anException) _andThrow(anException) +@property (nonatomic, readonly) OCMStubRecorder *(^ _andThrow)(NSException *); + +#define andPost(aNotification) _andPost(aNotification) +@property (nonatomic, readonly) OCMStubRecorder *(^ _andPost)(NSNotification *); + +#define andCall(anObject, aSelector) _andCall(anObject, aSelector) +@property (nonatomic, readonly) OCMStubRecorder *(^ _andCall)(id, SEL); + +#define andDo(aBlock) _andDo(aBlock) +@property (nonatomic, readonly) OCMStubRecorder *(^ _andDo)(void (^)(NSInvocation *)); + +#define andForwardToRealObject() _andForwardToRealObject() +@property (nonatomic, readonly) OCMStubRecorder *(^ _andForwardToRealObject)(void); + +@end + + + diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMock.h b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMock.h new file mode 100644 index 00000000000000..9d558135bff234 --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMock.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2004-2016 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import + + +#define OCMClassMock(cls) [OCMockObject niceMockForClass:cls] + +#define OCMStrictClassMock(cls) [OCMockObject mockForClass:cls] + +#define OCMProtocolMock(protocol) [OCMockObject niceMockForProtocol:protocol] + +#define OCMStrictProtocolMock(protocol) [OCMockObject mockForProtocol:protocol] + +#define OCMPartialMock(obj) [OCMockObject partialMockForObject:obj] + +#define OCMObserverMock() [OCMockObject observerMock] + + +#define OCMStub(invocation) \ +({ \ + _OCMSilenceWarnings( \ + [OCMMacroState beginStubMacro]; \ + OCMStubRecorder *recorder = nil; \ + @try{ \ + invocation; \ + }@finally{ \ + recorder = [OCMMacroState endStubMacro]; \ + } \ + recorder; \ + ); \ +}) + +#define OCMExpect(invocation) \ +({ \ + _OCMSilenceWarnings( \ + [OCMMacroState beginExpectMacro]; \ + OCMStubRecorder *recorder = nil; \ + @try{ \ + invocation; \ + }@finally{ \ + recorder = [OCMMacroState endExpectMacro]; \ + } \ + recorder; \ + ); \ +}) + +#define OCMReject(invocation) \ +({ \ + _OCMSilenceWarnings( \ + [OCMMacroState beginRejectMacro]; \ + OCMStubRecorder *recorder = nil; \ + @try{ \ + invocation; \ + }@finally{ \ + recorder = [OCMMacroState endRejectMacro]; \ + } \ + recorder; \ + ); \ +}) + +#define ClassMethod(invocation) \ + _OCMSilenceWarnings( \ + [[OCMMacroState globalState] switchToClassMethod]; \ + invocation; \ + ); + + +#define OCMVerifyAll(mock) [mock verifyAtLocation:OCMMakeLocation(self, __FILE__, __LINE__)] + +#define OCMVerifyAllWithDelay(mock, delay) [mock verifyWithDelay:delay atLocation:OCMMakeLocation(self, __FILE__, __LINE__)] + +#define OCMVerify(invocation) \ +({ \ + _OCMSilenceWarnings( \ + [OCMMacroState beginVerifyMacroAtLocation:OCMMakeLocation(self, __FILE__, __LINE__)]; \ + @try{ \ + invocation; \ + }@finally{ \ + [OCMMacroState endVerifyMacro]; \ + } \ + ); \ +}) + +#define _OCMSilenceWarnings(macro) \ +({ \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunused-value\"") \ + _Pragma("clang diagnostic ignored \"-Wunused-getter-return-value\"") \ + macro \ + _Pragma("clang diagnostic pop") \ +}) diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMockObject.h b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMockObject.h new file mode 100644 index 00000000000000..31f7ac41d9e3d8 --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Headers/OCMockObject.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2004-2016 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import + +@class OCMLocation; +@class OCMInvocationStub; +@class OCMStubRecorder; +@class OCMInvocationMatcher; +@class OCMInvocationExpectation; + + +@interface OCMockObject : NSProxy +{ + BOOL isNice; + BOOL expectationOrderMatters; + NSMutableArray *stubs; + NSMutableArray *expectations; + NSMutableArray *exceptions; + NSMutableArray *invocations; +} + ++ (id)mockForClass:(Class)aClass; ++ (id)mockForProtocol:(Protocol *)aProtocol; ++ (id)partialMockForObject:(NSObject *)anObject; + ++ (id)niceMockForClass:(Class)aClass; ++ (id)niceMockForProtocol:(Protocol *)aProtocol; + ++ (id)observerMock; + +- (instancetype)init; + +- (void)setExpectationOrderMatters:(BOOL)flag; + +- (id)stub; +- (id)expect; +- (id)reject; + +- (id)verify; +- (id)verifyAtLocation:(OCMLocation *)location; + +- (void)verifyWithDelay:(NSTimeInterval)delay; +- (void)verifyWithDelay:(NSTimeInterval)delay atLocation:(OCMLocation *)location; + +- (void)stopMocking; + +// internal use only + +- (void)addStub:(OCMInvocationStub *)aStub; +- (void)addExpectation:(OCMInvocationExpectation *)anExpectation; + +- (BOOL)handleInvocation:(NSInvocation *)anInvocation; +- (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation; +- (BOOL)handleSelector:(SEL)sel; + +- (void)verifyInvocation:(OCMInvocationMatcher *)matcher; +- (void)verifyInvocation:(OCMInvocationMatcher *)matcher atLocation:(OCMLocation *)location; + +@end + diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Modules/module.modulemap b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 00000000000000..6e98890917bc77 --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module OCMock { + umbrella header "OCMock.h" + + export * + module * { export * } +} diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/OCMock b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/OCMock new file mode 100755 index 00000000000000..59a2bb21a23a94 Binary files /dev/null and b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/OCMock differ diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Resources/Info.plist b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Resources/Info.plist new file mode 100644 index 00000000000000..e2e434a080a503 --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,46 @@ + + + + + BuildMachineOSBuild + 15G1212 + CFBundleDevelopmentRegion + English + CFBundleExecutable + OCMock + CFBundleIdentifier + com.mulle-kybernetik.OCMock + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + OCMock + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 8C1002 + DTPlatformVersion + GM + DTSDKBuild + 16C58 + DTSDKName + macosx10.12 + DTXcode + 0821 + DTXcodeBuild + 8C1002 + NSHumanReadableCopyright + Copyright © 2004-2013 Mulle Kybernetik. + + diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Resources/en.lproj/InfoPlist.strings b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Resources/en.lproj/InfoPlist.strings new file mode 100644 index 00000000000000..5e45963c382ba6 Binary files /dev/null and b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/Resources/en.lproj/InfoPlist.strings differ diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/_CodeSignature/CodeResources b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/_CodeSignature/CodeResources new file mode 100644 index 00000000000000..2655b32794b716 --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/A/_CodeSignature/CodeResources @@ -0,0 +1,265 @@ + + + + + files + + Resources/Info.plist + + ItWMZFQXFjExa++WCUdKXb18yHM= + + Resources/en.lproj/InfoPlist.strings + + hash + + MiLKDDnrUKr4EmuvhS5VQwxHGK8= + + optional + + + + files2 + + Headers/NSNotificationCenter+OCMAdditions.h + + hash + + pFhPKZzX9HM07wFLAmeFF43SG6Q= + + hash2 + + NnTKrGNP4Njk3yKcEvglOeMqccpGA9jHyC1RQu2S0bQ= + + + Headers/OCMArg.h + + hash + + eXH6306vchCAoMs2SOo05ZgYfyE= + + hash2 + + FxqsQiA69iuoHCr4qLNLeUjs0O3nrlM1/y8HFUrRbz0= + + + Headers/OCMConstraint.h + + hash + + FRbc5GvcXMiiCFl1PgW3OvepNYA= + + hash2 + + 0qapY/YQKe0npD4guJXAxrg3PnEIeEK3R+PqW7jEc+A= + + + Headers/OCMFunctions.h + + hash + + CyzP7WkZ+OOYELFA5WsZaWO5kxg= + + hash2 + + hBleQ1IlJVlSU7ObC/o8aWPiF4aS3tbjdPPSQX0oFaI= + + + Headers/OCMLocation.h + + hash + + qjhhruQ18SiTXjsRjKGUD1eCM/g= + + hash2 + + D0ep+94IDMx4jNTEObjRjXjOa9o5HWtU3kALeNvMrMk= + + + Headers/OCMMacroState.h + + hash + + qJJTF+b6OeQggRm1gDg8v6UIzpM= + + hash2 + + rO0jxkQY4xa4ynAHSysMSsK1THpZIjLVGO6JAfyb0p8= + + + Headers/OCMRecorder.h + + hash + + diyYzxk+iLvIqBa+mGMSxgNx5Hs= + + hash2 + + Dl5hWJ6yAPLAbm0qYTbZ1Q8WT49qyklAUoxBRUazevU= + + + Headers/OCMStubRecorder.h + + hash + + jk2b9AjWJ1SRj0ju7Uu+U3C3uT8= + + hash2 + + tZQFBeJpaclUrRhLP7S8Tkw6To79q4BFCgy956ocR8U= + + + Headers/OCMock.h + + hash + + 93kK1Oj0uYy5eT+GMc5l1IOm+E0= + + hash2 + + 4QEeRNlGEjQ8CTfq7Bl5lcdkpjb35WQvT9VvDQott8Q= + + + Headers/OCMockObject.h + + hash + + oZEWGlkN3u1F+7KPHoLi3MYbU0k= + + hash2 + + JIb6/mFaQhSB0mM9ofM5Stqa5aJe7pwiDsuBf9FZV00= + + + Modules/module.modulemap + + hash + + C/zv4wGd+b+kg7T2q31cWmGbPX0= + + hash2 + + B5k13RUp+z7jVfSrgRPFafuYH6CF8A6V1qosS2Lm+1k= + + + Resources/Info.plist + + hash + + ItWMZFQXFjExa++WCUdKXb18yHM= + + hash2 + + 9pX+IC62PDe7XfTWAWGU6N2saRZFPEIyI0nMHyOV19Q= + + + Resources/en.lproj/InfoPlist.strings + + hash + + MiLKDDnrUKr4EmuvhS5VQwxHGK8= + + hash2 + + Oc8u4Ht7Mz58F50L9NeYpbcq9qTlhPUeZCcDu/pPyCg= + + optional + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/RNTester/RNTesterUnitTests/OCMock.framework/Versions/Current b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/Current new file mode 120000 index 00000000000000..8c7e5a667f1b77 --- /dev/null +++ b/RNTester/RNTesterUnitTests/OCMock.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/RNTester/RNTesterUnitTests/RCTBundleURLProviderTests.m b/RNTester/RNTesterUnitTests/RCTBundleURLProviderTests.m index e25a9a83f64c69..384de6bc4ed7e9 100644 --- a/RNTester/RNTesterUnitTests/RCTBundleURLProviderTests.m +++ b/RNTester/RNTesterUnitTests/RCTBundleURLProviderTests.m @@ -22,12 +22,12 @@ static NSURL *localhostBundleURL() { - return [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true&minify=false", testFile]]; + return [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=%@&dev=true&minify=false", testFile, kRCTPlatformName]]; } static NSURL *ipBundleURL() { - return [NSURL URLWithString:[NSString stringWithFormat:@"http://192.168.1.1:8081/%@.bundle?platform=ios&dev=true&minify=false", testFile]]; + return [NSURL URLWithString:[NSString stringWithFormat:@"http://192.168.1.1:8081/%@.bundle?platform=%@&dev=true&minify=false", testFile, kRCTPlatformName]]; } @implementation NSBundle (RCTBundleURLProviderTests) diff --git a/RNTester/RNTesterUnitTests/RCTConvert_NSURLTests.m b/RNTester/RNTesterUnitTests/RCTConvert_NSURLTests.m index 8c6a1ec170b875..7dc6601f39a11e 100644 --- a/RNTester/RNTesterUnitTests/RCTConvert_NSURLTests.m +++ b/RNTester/RNTesterUnitTests/RCTConvert_NSURLTests.m @@ -32,7 +32,7 @@ - (void)test_##name { \ } \ #define TEST_BUNDLE_PATH(name, _input, _expectedPath) \ -TEST_PATH(name, _input, [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:_expectedPath]) +TEST_PATH(name, _input, [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:_expectedPath]) // Basic tests TEST_URL(basic, @"http://example.com", @"http://example.com") diff --git a/RNTester/RNTesterUnitTests/RCTEventDispatcherTests.m b/RNTester/RNTesterUnitTests/RCTEventDispatcherTests.m index f58fd45ac0e55a..a5e09ba4747b74 100644 --- a/RNTester/RNTesterUnitTests/RCTEventDispatcherTests.m +++ b/RNTester/RNTesterUnitTests/RCTEventDispatcherTests.m @@ -8,7 +8,7 @@ * */ -#import +#import #import #import diff --git a/RNTester/RNTesterUnitTests/RCTFontTests.m b/RNTester/RNTesterUnitTests/RCTFontTests.m index 148a0bcf7a1536..8ecd1be8687dbc 100644 --- a/RNTester/RNTesterUnitTests/RCTFontTests.m +++ b/RNTester/RNTesterUnitTests/RCTFontTests.m @@ -29,13 +29,26 @@ @implementation RCTFontTests - (void)testWeight { +#if !TARGET_OS_OSX + // VSO#1878630: macOS: some RCTFontTests failing + + // macOS: expected = .AppleSystemUIFontBold 14.00 pt., result = .AppleSystemUIFontEmphasized 14.00 pt. { +#if TARGET_OS_TV UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightBold]; +#else + UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]; +#endif UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"bold"}]; RCTAssertEqualFonts(expected, result); } +#endif { +#if TARGET_OS_TV || TARGET_OS_OSX UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; +#else + UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightRegular]; +#endif UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"500"}]; RCTAssertEqualFonts(expected, result); } @@ -83,6 +96,8 @@ - (void)testFamily - (void)testStyle { +#if !TARGET_OS_OSX + // macOS: expected = .SFNSText-Italic 14.00 pt., result = .AppleSystemUIFontItalic 14.00 pt. { UIFont *font = [UIFont systemFontOfSize:14]; UIFontDescriptor *fontDescriptor = [font fontDescriptor]; @@ -93,6 +108,7 @@ - (void)testStyle UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"italic"}]; RCTAssertEqualFonts(expected, result); } +#endif { UIFont *expected = [UIFont systemFontOfSize:14]; UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"normal"}]; @@ -102,6 +118,8 @@ - (void)testStyle - (void)testStyleAndWeight { +#if !TARGET_OS_OSX + // macOS: expected = .SFNSText-LightItalic 14.00 pt., result = .AppleSystemUIFontUltraLightItalic 14.00 pt. { UIFont *font = [UIFont systemFontOfSize:14 weight:UIFontWeightUltraLight]; UIFontDescriptor *fontDescriptor = [font fontDescriptor]; @@ -112,8 +130,13 @@ - (void)testStyleAndWeight UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"italic", @"fontWeight": @"100"}]; RCTAssertEqualFonts(expected, result); } + // macOS: expected = .SFNSText-BoldItalic 14.00 pt., result = .AppleSystemUIFontEmphasizedItalic 14.00 pt. { +#if TARGET_OS_TV UIFont *font = [UIFont systemFontOfSize:14 weight:UIFontWeightBold]; +#else + UIFont *font = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]; +#endif UIFontDescriptor *fontDescriptor = [font fontDescriptor]; UIFontDescriptorSymbolicTraits symbolicTraits = fontDescriptor.symbolicTraits; symbolicTraits |= UIFontDescriptorTraitItalic; @@ -122,6 +145,7 @@ - (void)testStyleAndWeight UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"italic", @"fontWeight": @"bold"}]; RCTAssertEqualFonts(expected, result); } +#endif } - (void)testFamilyAndWeight @@ -185,11 +209,14 @@ - (void)testFamilyStyleAndWeight - (void)testVariant { +#if !TARGET_OS_OSX + // expected = .AppleSystemUIFont 14.00 pt., result = .SFNSText 14.00 pt. { UIFont *expected = [UIFont monospacedDigitSystemFontOfSize:14 weight:UIFontWeightRegular]; UIFont *result = [RCTConvert UIFont:@{@"fontVariant": @[@"tabular-nums"]}]; RCTAssertEqualFonts(expected, result); } + // expected = .AppleSystemUIFont 14.00 pt., result = .SFNSText 14.00 pt. { UIFont *monospaceFont = [UIFont monospacedDigitSystemFontOfSize:14 weight:UIFontWeightRegular]; UIFontDescriptor *fontDescriptor = [monospaceFont.fontDescriptor fontDescriptorByAddingAttributes:@{ @@ -202,6 +229,7 @@ - (void)testVariant UIFont *result = [RCTConvert UIFont:@{@"fontVariant": @[@"tabular-nums", @"small-caps"]}]; RCTAssertEqualFonts(expected, result); } +#endif } - (void)testInvalidFont @@ -211,11 +239,18 @@ - (void)testInvalidFont UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"foobar"}]; RCTAssertEqualFonts(expected, result); } +#if !TARGET_OS_OSX + // expected = .AppleSystemUIFontBold 14.00 pt., result = .AppleSystemUIFontDemi 14.00 pt. { +#if TARGET_OS_TV UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightBold]; +#else + UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]; +#endif UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"foobar", @"fontWeight": @"bold"}]; RCTAssertEqualFonts(expected, result); } +#endif } @end diff --git a/RNTester/RNTesterUnitTests/RCTImageUtilTests.m b/RNTester/RNTesterUnitTests/RCTImageUtilTests.m index e5aa49f43634a2..7a8b7c4b854d4a 100644 --- a/RNTester/RNTesterUnitTests/RCTImageUtilTests.m +++ b/RNTester/RNTesterUnitTests/RCTImageUtilTests.m @@ -10,7 +10,6 @@ #import #import -#import #import #import diff --git a/RNTester/RNTesterUnitTests/RCTMethodArgumentTests.m b/RNTester/RNTesterUnitTests/RCTMethodArgumentTests.m index 8c266cdce9a06d..ead68ab501aea7 100644 --- a/RNTester/RNTesterUnitTests/RCTMethodArgumentTests.m +++ b/RNTester/RNTesterUnitTests/RCTMethodArgumentTests.m @@ -8,7 +8,7 @@ * */ -#import +#import #import #import diff --git a/RNTester/RNTesterUnitTests/RCTModuleMethodTests.mm b/RNTester/RNTesterUnitTests/RCTModuleMethodTests.mm index 7abcd37aaf170f..54f2cb40a9fced 100644 --- a/RNTester/RNTesterUnitTests/RCTModuleMethodTests.mm +++ b/RNTester/RNTesterUnitTests/RCTModuleMethodTests.mm @@ -8,7 +8,7 @@ * */ -#import +#import #import #import diff --git a/RNTester/RNTesterUnitTests/RCTNativeAnimatedNodesManagerTests.m b/RNTester/RNTesterUnitTests/RCTNativeAnimatedNodesManagerTests.m index 70678ac2854269..cbb93177a07d33 100644 --- a/RNTester/RNTesterUnitTests/RCTNativeAnimatedNodesManagerTests.m +++ b/RNTester/RNTesterUnitTests/RCTNativeAnimatedNodesManagerTests.m @@ -15,6 +15,7 @@ #import #import #import +#import static const NSTimeInterval FRAME_LENGTH = 1.0 / 60.0; @@ -123,6 +124,7 @@ @implementation RCTNativeAnimatedNodesManagerTests id _uiManager; RCTNativeAnimatedNodesManager *_nodesManager; RCTFakeDisplayLink *_displayLink; + } - (void)setUp @@ -168,6 +170,7 @@ - (void)testFramesAnimation [[_uiManager expect] synchronouslyUpdateViewOnUIThread:@1000 viewName:@"UIView" props:RCTPropChecker(@"opacity", frame)]; + [_nodesManager stepAnimations:_displayLink]; [_uiManager verify]; } diff --git a/RNTester/js/AccessibilityIOSExample.js b/RNTester/js/AccessibilityIOSExample.js index 29c8b16f062184..a005b59f367c39 100644 --- a/RNTester/js/AccessibilityIOSExample.js +++ b/RNTester/js/AccessibilityIOSExample.js @@ -55,6 +55,11 @@ class AccessibilityIOSExample extends React.Component<{}> { accessible={true}> This text component's accessibilityLabel is set explicitly. + + + This view's children are hidden from the accessibility tree + + ); } diff --git a/RNTester/js/ActionSheetMacOSExample.js b/RNTester/js/ActionSheetMacOSExample.js new file mode 100644 index 00000000000000..d1aa7510466f19 --- /dev/null +++ b/RNTester/js/ActionSheetMacOSExample.js @@ -0,0 +1,130 @@ +/** + * 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. + * + * @flow + * @providesModule ActionSheetMacOSExample + */ +'use strict'; + +var React = require('react'); +var ReactNative = require('react-native'); +var { + ActionSheetIOS, + StyleSheet, + takeSnapshot, + Text, + View, +} = ReactNative; + +var BUTTONS = [ + 'Option 0', + 'Option 1', + 'Option 2', + 'Delete', + 'Cancel', +]; +var DESTRUCTIVE_INDEX = 3; +var CANCEL_INDEX = 4; + +class ActionSheetExample extends React.Component { + state = { + clicked: 'none', + }; + + render() { + return ( + + + Click to show the ActionSheet + + + Clicked button: {this.state.clicked} + + + ); + } + + showActionSheet = () => { + ActionSheetIOS.showActionSheetWithOptions({ + options: BUTTONS, + cancelButtonIndex: CANCEL_INDEX, + destructiveButtonIndex: DESTRUCTIVE_INDEX, + }, + (buttonIndex) => { + this.setState({ clicked: BUTTONS[buttonIndex] }); + }); + }; +} + +class ShareActionSheetExample extends React.Component { + state = { + text: '' + }; + + render() { + return ( + + + Click to show the Share ActionSheet + + + {this.state.text} + + + ); + } + + showShareActionSheet = () => { + ActionSheetIOS.showShareActionSheetWithOptions({ + url: this.props.url, + message: 'message to go with the shared url', + subject: 'a subject to go in the email heading', + excludedActivityTypes: [ + 'com.apple.share.Twitter.post' + ] + }, + (error) => alert(error), + (completed, method) => { + var text; + if (completed) { + text = `Shared via ${method}`; + } else { + text = 'You didn\'t share'; + } + this.setState({text}); + }); + }; +} + +var style = StyleSheet.create({ + button: { + marginBottom: 10, + fontWeight: '500', + } +}); + +exports.title = 'ActionSheetIOS'; +exports.description = 'Interface to show iOS\' action sheets'; +exports.examples = [ + { + title: 'Show Action Sheet', + render(): React.Element { return ; } + }, + { + title: 'Show Share Action Sheet', + render(): React.Element { + return ; + } + }, + { + title: 'Share Local Image', + render(): React.Element { + return ; + } + } +]; diff --git a/RNTester/js/ActivityIndicatorExample.js b/RNTester/js/ActivityIndicatorExample.js index e185041b7a9aa8..0990a106c67e19 100644 --- a/RNTester/js/ActivityIndicatorExample.js +++ b/RNTester/js/ActivityIndicatorExample.js @@ -147,6 +147,19 @@ exports.examples = [ } }, { + platform: 'ios', + title: 'Custom size', + render() { + return ( + + ); + } + }, + { + platform: 'android', title: 'Custom size', render() { return ( diff --git a/RNTester/js/AlertMacOSExample.js b/RNTester/js/AlertMacOSExample.js new file mode 100644 index 00000000000000..8895132c8f79e3 --- /dev/null +++ b/RNTester/js/AlertMacOSExample.js @@ -0,0 +1,297 @@ +/** + * 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. + * + * @flow + * @providesModule AlertMacOSExample + */ +'use strict'; + +var React = require('react'); +var ReactNative = require('react-native'); +var { + StyleSheet, + View, + Text, + TouchableHighlight, + AlertMacOS, +} = ReactNative; + +var { SimpleAlertExampleBlock } = require('./AlertExample'); + +exports.framework = 'React'; +exports.title = 'AlertMacOS'; +exports.description = 'macOS alerts'; +exports.examples = [{ + title: 'Alerts', + render() { + return ; + } +}, +{ + title: 'Prompt Options', + render(): React.Element { + return ; + } +}, +{ + title: 'Prompt Types', + render() { + return ( + + AlertMacOS.prompt('Plain Text Entry')}> + + + + plain-text + + + + + AlertMacOS.prompt('Secure Text', null, null, 'secure-text')}> + + + + secure-text + + + + + AlertMacOS.prompt( + 'Login & Password', + null, + null, + 'login-password', + [ + {default: '', placeholder: 'login'}, + {default: '', placeholder: 'Password'}, + ], + )}> + + + + login-password + + + + + + ); + } +}, +{ + title: 'Prompt Presentation', + render() { + return ( + + AlertMacOS.prompt( + 'Default sheet', + null, + null, + 'default', + [ + {default: '', placeholder: ''}, + ], + false + )}> + + + + Default sheet + + + + + AlertMacOS.prompt( + 'Modal', + null, + null, + 'default', + [ + {default: '', placeholder: ''}, + ], + true + )}> + + + + Modal + + + + + + ); + } +}, +{ + title: 'Prompt Style', + render() { + return ( + + AlertMacOS.prompt( + 'Default warning style', + null, + null, + 'default' + )}> + + + + Default warning style + + + + + AlertMacOS.prompt( + 'Critical', + null, + null, + 'default', + [ + {default: '', placeholder: ''}, + ], + false, + true) + }> + + + + Critical + + + + + + ); + } +}, +]; + +class PromptOptions extends React.Component { + state: any; + customButtons: Array; + + constructor(props) { + super(props); + + // $FlowFixMe this seems to be a Flow bug, `saveResponse` is defined below + this.saveResponse = this.saveResponse.bind(this); + + this.customButtons = [{ + text: 'Custom OK', + onPress: this.saveResponse + }, { + text: 'Custom Cancel', + style: 'cancel', + }]; + + this.state = { + promptValue: undefined, + }; + } + + render() { + return ( + + + Prompt value: {this.state.promptValue} + + + AlertMacOS.prompt('Type a value', null, this.saveResponse)}> + + + + prompt with title & callback + + + + + AlertMacOS.prompt('Type a value', null, this.customButtons)}> + + + + prompt with title & custom buttons + + + + + AlertMacOS.prompt( + 'Type a value', + null, + this.saveResponse, + undefined, + [ + {default: 'Default value', placeholder: ''}, + ], + )}> + + + + prompt with title, callback & default inputs + + + + + AlertMacOS.prompt( + 'Type a value', + null, + this.customButtons, + 'login-password', + [ + {default: 'admin@site.com', placeholder: 'login'}, + {default: '', placeholder: 'password'}, + ], + )}> + + + + prompt with title, custom buttons, login/password & default inputs + + + + + ); + } + + saveResponse(promptValue) { + this.setState({ promptValue: JSON.stringify(promptValue) }); + } +} + +var styles = StyleSheet.create({ + wrapper: { + borderRadius: 5, + marginBottom: 5, + }, + button: { + backgroundColor: '#eeeeee', + padding: 10, + }, +}); diff --git a/RNTester/js/AnimatedExampleMacOS.js b/RNTester/js/AnimatedExampleMacOS.js new file mode 100644 index 00000000000000..711afc461e0e08 --- /dev/null +++ b/RNTester/js/AnimatedExampleMacOS.js @@ -0,0 +1,187 @@ +/** + * 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. + * + * @flow + * @providesModule AnimatedExampleMacOS + */ +'use strict'; + +var React = require('react'); +var ReactNative = require('react-native'); +var { + Animated, + Easing, + StyleSheet, + Text, + View, +} = ReactNative; +var RNTesterButton = require('./RNTesterButton'); + +exports.framework = 'React'; +exports.title = 'Animated - Examples'; +exports.description = 'Animated provides a powerful ' + + 'and easy-to-use API for building modern, ' + + 'interactive user experiences.'; + +exports.examples = [ + { + title: 'FadeInView', + description: 'Uses a simple timing animation to ' + + 'bring opacity from 0 to 1 when the component ' + + 'mounts.', + render: function() { + class FadeInView extends React.Component { + state: any; + + constructor(props) { + super(props); + this.state = { + fadeAnim: new Animated.Value(0), // opacity 0 + }; + } + componentDidMount() { + Animated.timing( // Uses easing functions + this.state.fadeAnim, // The value to drive + { + toValue: 1, // Target + duration: 2000, // Configuration + }, + ).start(); // Don't forget start! + } + render() { + return ( + + {this.props.children} + + ); + } + } + class FadeInExample extends React.Component { + state: any; + + constructor(props) { + super(props); + this.state = { + show: true, + }; + } + render() { + return ( + + { + this.setState((state) => ( + {show: !state.show} + )); + }}> + Press to {this.state.show ? + 'Hide' : 'Show'} + + {this.state.show && + + FadeInView + + } + + ); + } + } + return ; + }, + }, + { + title: 'Composite Animations with Easing', + description: 'Sequence, parallel, delay, and ' + + 'stagger with different easing functions.', + render: function() { + this.anims = this.anims || [1,2,3].map( + () => new Animated.Value(0) + ); + return ( + + { + var timing = Animated.timing; + Animated.sequence([ // One after the other + timing(this.anims[0], { + toValue: 200, + easing: Easing.linear, + }), + Animated.delay(400), // Use with sequence + timing(this.anims[0], { + toValue: 0, + easing: Easing.elastic(2), // Springy + }), + Animated.delay(400), + Animated.stagger(200, + this.anims.map((anim) => timing( + anim, {toValue: 200} + )).concat( + this.anims.map((anim) => timing( + anim, {toValue: 0} + ))), + ), + Animated.delay(400), + Animated.parallel([ + Easing.inOut(Easing.quad), // Symmetric + Easing.back(1.5), // Goes backwards first + Easing.ease // Default bezier + ].map((easing, ii) => ( + timing(this.anims[ii], { + toValue: 320, easing, duration: 3000, + }) + ))), + Animated.delay(400), + Animated.stagger(200, + this.anims.map((anim) => timing(anim, { + toValue: 0, + easing: Easing.bounce, // Like a ball + duration: 2000, + })), + ), + ]).start(); }}> + Press to Animate + + {['Composite', 'Easing', 'Animations!'].map( + (text, ii) => ( + + {text} + + ) + )} + + ); + }, + }, + { + title: 'Continuous Interactions', + description: 'Gesture events, chaining, 2D ' + + 'values, interrupting and transitioning ' + + 'animations, etc.', + render: () => ( + Checkout the Gratuitous Animation App! + ), + } +]; + +var styles = StyleSheet.create({ + content: { + backgroundColor: 'deepskyblue', + borderWidth: 1, + borderColor: 'dodgerblue', + padding: 20, + margin: 20, + borderRadius: 10, + alignItems: 'center', + }, +}); diff --git a/RNTester/js/BorderExample.js b/RNTester/js/BorderExample.js index 3bb5de8fce78ca..79b4674bf6b472 100644 --- a/RNTester/js/BorderExample.js +++ b/RNTester/js/BorderExample.js @@ -205,7 +205,7 @@ exports.examples = [ { title: 'Custom Borders', description: 'border*Width & border*Color', - platform: 'ios', + platform: ['ios', 'macos'], render() { return ; } @@ -213,7 +213,7 @@ exports.examples = [ { title: 'Custom Borders', description: 'border*Width & border*Color', - platform: 'ios', + platform: ['ios', 'macos'], render() { return ; } @@ -221,7 +221,7 @@ exports.examples = [ { title: 'Custom Borders', description: 'borderRadius & clipping', - platform: 'ios', + platform: ['ios', 'macos'], render() { return ( diff --git a/RNTester/js/DatePickerIOSExample.js b/RNTester/js/DatePickerIOSExample.js index d03c19faefc726..74a8ab136b4da2 100644 --- a/RNTester/js/DatePickerIOSExample.js +++ b/RNTester/js/DatePickerIOSExample.js @@ -48,7 +48,13 @@ class DatePickerExample extends React.Component<$FlowFixMeProps, $FlowFixMeState // Ideally, the timezone input would be a picker rather than a // text input, but we don't have any pickers yet :( return ( - + { this.state.date.toLocaleDateString() + diff --git a/RNTester/js/DatePickerMacOSExample.js b/RNTester/js/DatePickerMacOSExample.js new file mode 100644 index 00000000000000..716c647db2ee58 --- /dev/null +++ b/RNTester/js/DatePickerMacOSExample.js @@ -0,0 +1,173 @@ +/** + * 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. + * + * @flow + * @providesModule DatePickerMacOSExample + */ +'use strict'; + +var React = require('react'); +var ReactNative = require('react-native'); +var { + DatePickerMacOS, + StyleSheet, + Text, + TextInput, + View, +} = ReactNative; + +class DatePickerExample extends React.Component { + static defaultProps = { + date: new Date(), + timeZoneOffsetInHours: (-1) * (new Date()).getTimezoneOffset() / 60, + }; + + state = { + date: this.props.date, + timeZoneOffsetInHours: this.props.timeZoneOffsetInHours, + }; + + onDateChange = (date) => { + this.setState({date: date}); + }; + + onTimezoneChange = (event) => { + var offset = parseInt(event.nativeEvent.text, 10); + if (isNaN(offset)) { + return; + } + this.setState({timeZoneOffsetInHours: offset}); + }; + + render() { + // Ideally, the timezone input would be a picker rather than a + // text input, but we don't have any pickers yet :( + return ( + + + { + this.state.date.toLocaleDateString() + + ' ' + + this.state.date.toLocaleTimeString() + } + + + + hours from UTC + + + + + + + + + ); + } +} + +class WithLabel extends React.Component { + render() { + return ( + + + + {this.props.label} + + + {this.props.children} + + ); + } +} + +class Heading extends React.Component { + render() { + return ( + + + {this.props.label} + + + ); + } +} + +exports.displayName = (undefined: ?string); +exports.title = ''; +exports.description = 'Select dates and times using the native UIDatePicker.'; +exports.examples = [ +{ + title: '', + render: function(): React.Element { + return ; + }, +}]; + +var styles = StyleSheet.create({ + pickerTextField: { + height: 30, + width: 170, + }, + pickerClock: { + height: 150, + width: 150, + }, + textinput: { + height: 26, + width: 50, + borderWidth: 0.5, + borderColor: '#0f0f0f', + padding: 4, + fontSize: 13, + }, + labelContainer: { + flexDirection: 'row', + alignItems: 'center', + marginVertical: 2, + }, + labelView: { + marginRight: 10, + paddingVertical: 2, + }, + label: { + fontWeight: '500', + }, + headingContainer: { + padding: 4, + backgroundColor: '#f6f7f8', + }, + heading: { + fontWeight: '500', + fontSize: 14, + }, +}); diff --git a/RNTester/js/FlatListExample.js b/RNTester/js/FlatListExample.js index aa2f805b73563b..2571845adf1532 100644 --- a/RNTester/js/FlatListExample.js +++ b/RNTester/js/FlatListExample.js @@ -125,6 +125,8 @@ class FlatListExample extends React.PureComponent<{}, $FlowFixMeState> { data={filteredData} debug={this.state.debug} disableVirtualization={!this.state.virtualized} + enableSelectionOnKeyPress={true} + onSelectionEntered={this._handleSelectionEntered} getItemLayout={this.state.fixedHeight ? this._getItemLayout : undefined @@ -165,10 +167,11 @@ class FlatListExample extends React.PureComponent<{}, $FlowFixMeState> { })); }; _onRefresh = () => Alert.alert('onRefresh: nothing to refresh :P'); - _renderItemComponent = ({item, separators}) => { + _renderItemComponent = ({item, isSelected, separators}) => { return ( { this._listRef.getNode().recordInteraction(); pressItem(this, key); }; + _handleSelectionEntered = (item) => { + const {key} = item; + this._listRef.getNode().recordInteraction(); + pressItem(this, key); + } _listRef: AnimatedFlatList; } diff --git a/RNTester/js/ImageCapInsetsExample.js b/RNTester/js/ImageCapInsetsExample.js index 88830779e1d424..c8315bd340a6dc 100644 --- a/RNTester/js/ImageCapInsetsExample.js +++ b/RNTester/js/ImageCapInsetsExample.js @@ -13,6 +13,7 @@ var React = require('react'); var ReactNative = require('react-native'); +var Platform = require('Platform'); var nativeImageSource = require('nativeImageSource'); var { @@ -24,6 +25,22 @@ var { class ImageCapInsetsExample extends React.Component<{}> { render() { + + let nativeImage; + if (Platform.OS === 'macos') { + nativeImage = nativeImageSource({ + macos: 'story-background', + width: 60, + height: 60 + }); + } else { + nativeImage = nativeImageSource({ + ios: 'story-background', + width: 60, + height: 60 + }) + } + return ( @@ -31,11 +48,7 @@ class ImageCapInsetsExample extends React.Component<{}> { capInsets: none { capInsets: 15 this._loadEventFired(`✔ onLoadStart (+${new Date() - mountTime}ms)`)} onLoad={(event) => { - // Currently this image source feature is only available on iOS. + // Currently this image source feature is only available on iOS & macOS. if (event.nativeEvent.source) { const url = event.nativeEvent.source.url; this._loadEventFired(`✔ onLoad (+${new Date() - mountTime}ms) for URL ${url}`); @@ -274,7 +274,7 @@ exports.examples = [ ); }, - platform: 'ios', + platform: ['ios', 'macos'], }, { title: 'Image Download Progress', @@ -283,7 +283,7 @@ exports.examples = [ ); }, - platform: 'ios', + platform: ['ios', 'macos'], }, { title: 'defaultSource', @@ -297,7 +297,7 @@ exports.examples = [ /> ); }, - platform: 'ios', + platform: ['ios', 'macos'], }, { title: 'Cache Policy', @@ -326,8 +326,9 @@ exports.examples = [ ); }, - platform: 'ios', + platform: ['ios', 'macos'], }, + { title: 'Border Color', render: function() { @@ -560,7 +561,7 @@ exports.examples = [ source={image} /> - { Platform.OS === 'ios' ? + { Platform.OS === 'ios' || Platform.OS === 'macos' ? Repeat @@ -602,7 +603,7 @@ exports.examples = [ /> ); }, - platform: 'ios', + platform: ['ios', 'macos'], }, { title: 'Base64 image', @@ -614,7 +615,7 @@ exports.examples = [ /> ); }, - platform: 'ios', + platform: ['ios', 'macos'], }, { title: 'Cap Insets', @@ -626,7 +627,7 @@ exports.examples = [ render: function() { return ; }, - platform: 'ios', + platform: ['ios', 'macos'], }, { title: 'Image Size', @@ -686,6 +687,36 @@ exports.examples = [ }, platform: 'ios', }, + { + title: 'Bundled images', + description: + 'Images shipped in a separate native bundle', + render: function() { + return ( + + + + + ); + }, + platform: 'macos', + }, { title: 'Blur Radius', render: function() { diff --git a/RNTester/js/LayoutEventsExample.js b/RNTester/js/LayoutEventsExample.js index c1e99d4964ebca..4f17bdcf6a2e5e 100644 --- a/RNTester/js/LayoutEventsExample.js +++ b/RNTester/js/LayoutEventsExample.js @@ -13,6 +13,7 @@ var React = require('react'); var ReactNative = require('react-native'); +var Platform = require('Platform'); var { Image, LayoutAnimation, @@ -40,13 +41,23 @@ class LayoutEventExample extends React.Component<{}, State> { }; animateViewLayout = () => { - LayoutAnimation.configureNext( + if (Platform.OS === 'macos') { + LayoutAnimation.configureNext( + LayoutAnimation.Presets.easeInEaseOut, + () => { + console.log('layout animation done.'); + this.addWrapText(); + } + ); + } else { + LayoutAnimation.configureNext( LayoutAnimation.Presets.spring, () => { console.log('layout animation done.'); this.addWrapText(); - } - ); + } + ); + } this.setState({ viewStyle: { margin: this.state.viewStyle.margin > 20 ? 20 : 60, diff --git a/RNTester/js/LinkingExample.js b/RNTester/js/LinkingExample.js index 7da8f99fca9e7c..d43af3735f67ae 100644 --- a/RNTester/js/LinkingExample.js +++ b/RNTester/js/LinkingExample.js @@ -13,6 +13,7 @@ var React = require('react'); var PropTypes = require('prop-types'); var ReactNative = require('react-native'); +var Platform = require('Platform'); var { Linking, StyleSheet, @@ -59,8 +60,8 @@ class IntentAndroidExample extends React.Component { - - + + ); diff --git a/RNTester/js/ListExampleShared.js b/RNTester/js/ListExampleShared.js index cae8519f0aca74..1db0afbd2011c4 100644 --- a/RNTester/js/ListExampleShared.js +++ b/RNTester/js/ListExampleShared.js @@ -47,6 +47,7 @@ const ITEM_HEIGHT = 72; class ItemComponent extends React.PureComponent<{ fixedHeight?: ?boolean, horizontal?: ?boolean, + isSelected?: ?boolean, item: Item, onPress: (key: string) => void, onShowUnderlay?: () => void, @@ -59,6 +60,7 @@ class ItemComponent extends React.PureComponent<{ const {fixedHeight, horizontal, item} = this.props; const itemHash = Math.abs(hashCode(item.title)); const imgSource = THUMB_URLS[itemHash % THUMB_URLS.length]; + const rowStyle = this.props.isSelected ? styles.selectedRow : styles.row; return ( + rowStyle, horizontal && {width: HORIZ_WIDTH}, fixedHeight && {height: ITEM_HEIGHT}]}> {!item.noImage && } { render() { return ( this.setState({carMake, modelIndex: 0})}> {Object.keys(CAR_MAKES_AND_MODELS).map((carMake) => ( diff --git a/RNTester/js/RNTesterApp.ios.js b/RNTester/js/RNTesterApp.ios.js index b12946890344ec..94f47145a01ede 100644 --- a/RNTester/js/RNTesterApp.ios.js +++ b/RNTester/js/RNTesterApp.ios.js @@ -11,8 +11,6 @@ */ 'use strict'; -require('MessageQueue').spy(true); - const AsyncStorage = require('AsyncStorage'); const BackHandler = require('BackHandler'); const Linking = require('Linking'); diff --git a/RNTester/js/RNTesterApp.macos.js b/RNTester/js/RNTesterApp.macos.js new file mode 100644 index 00000000000000..d79b7080c965f1 --- /dev/null +++ b/RNTester/js/RNTesterApp.macos.js @@ -0,0 +1,193 @@ +/** + * 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 RNTesterApp + * @flow + */ +'use strict'; + +const AsyncStorage = require('AsyncStorage'); +// const BackHandler = require('BackHandler'); +const Linking = require('Linking'); +const React = require('react'); +const ReactNative = require('react-native'); +const RNTesterActions = require('./RNTesterActions'); +const RNTesterExampleContainer = require('./RNTesterExampleContainer'); +const RNTesterExampleList = require('./RNTesterExampleList'); +const RNTesterList = require('./RNTesterList.macos'); +const RNTesterNavigationReducer = require('./RNTesterNavigationReducer'); +const URIActionMap = require('./URIActionMap'); + +const { + Button, + AppRegistry, + // SnapshotViewIOS, + StyleSheet, + Text, + View, + } = ReactNative; + +import type { RNTesterExample } from './RNTesterList.macos'; +import type { RNTesterAction } from './RNTesterActions'; +import type { RNTesterNavigationState } from './RNTesterNavigationReducer'; + +type Props = { + exampleFromAppetizeParams: string, +}; + +const APP_STATE_KEY = 'RNTesterAppState.v2'; + +const Header = ({ onBack, title }: { onBack?: () => mixed, title: string }) => ( + + + {title} + + {onBack && +