Repository: microsoft/reactxp Branch: master Commit: 9faae700c7fd Files: 790 Total size: 2.5 MB Directory structure: gitextract_wyk0on54/ ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── .npmrc ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── README_Hindi.md ├── SECURITY.md ├── azure-pipelines.yml ├── docs/ │ ├── Gemfile │ ├── _config.yml │ ├── _data/ │ │ ├── authors.yml │ │ └── nav_docs.yml │ ├── _includes/ │ │ ├── blog_post.html │ │ ├── footer.html │ │ ├── hero.html │ │ ├── nav_blog.html │ │ ├── nav_docs.html │ │ └── navigation.html │ ├── _layouts/ │ │ ├── default.html │ │ ├── docs.html │ │ ├── hero.html │ │ └── post.html │ ├── _plugins/ │ │ └── sass.rb │ ├── _posts/ │ │ ├── 2017-04-06-introducing-reactxp.md │ │ ├── 2017-04-27-building-skype-on-reactxp.md │ │ ├── 2017-05-24-performance-tuning.md │ │ └── 2017-06-29-asset-loading.md │ ├── _sass/ │ │ ├── _typography.scss │ │ └── _variables.scss │ ├── blog/ │ │ ├── all.html │ │ └── index.html │ ├── community/ │ │ └── support.md │ ├── css/ │ │ ├── reactxp.scss │ │ └── syntax.css │ ├── docs/ │ │ ├── accessibility.md │ │ ├── animations.md │ │ ├── apis/ │ │ │ ├── accessibility.md │ │ │ ├── alert.md │ │ │ ├── app.md │ │ │ ├── clipboard.md │ │ │ ├── input.md │ │ │ ├── international.md │ │ │ ├── linking.md │ │ │ ├── location.md │ │ │ ├── modal.md │ │ │ ├── network.md │ │ │ ├── platform.md │ │ │ ├── popup.md │ │ │ ├── statusbar.md │ │ │ ├── storage.md │ │ │ ├── userinterface.md │ │ │ └── userpresence.md │ │ ├── components/ │ │ │ ├── activityindicator.md │ │ │ ├── button.md │ │ │ ├── gestureview.md │ │ │ ├── image.md │ │ │ ├── link.md │ │ │ ├── picker.md │ │ │ ├── scrollview.md │ │ │ ├── text.md │ │ │ ├── textinput.md │ │ │ ├── view.md │ │ │ └── webview.md │ │ ├── extensions/ │ │ │ ├── database.md │ │ │ ├── imagesvg.md │ │ │ ├── navigator.md │ │ │ ├── netinfo.md │ │ │ ├── restclient.md │ │ │ ├── video.md │ │ │ ├── virtuallistview.md │ │ │ └── webview.md │ │ ├── extensions.md │ │ ├── faq.md │ │ ├── getting-started.md │ │ ├── react_concepts.md │ │ ├── react_lifecycle.md │ │ ├── react_stores.md │ │ ├── styles.md │ │ └── using-reactxp.md │ ├── index.md │ └── versions/ │ └── version_history.md ├── extensions/ │ ├── README.md │ ├── imagesvg/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── README.md │ │ ├── index.android.js │ │ ├── index.ios.js │ │ ├── index.js │ │ ├── index.macos.js │ │ ├── index.windows.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── android/ │ │ │ │ └── PluginBase.tsx │ │ │ ├── common/ │ │ │ │ ├── Interfaces.ts │ │ │ │ ├── PluginBaseChecker.ts │ │ │ │ ├── Types.ts │ │ │ │ └── assert.ts │ │ │ ├── ios/ │ │ │ │ └── PluginBase.tsx │ │ │ ├── macos/ │ │ │ │ └── PluginBase.tsx │ │ │ ├── native-common/ │ │ │ │ ├── ImageSvg.tsx │ │ │ │ ├── SvgPath.tsx │ │ │ │ └── SvgRect.tsx │ │ │ ├── typings/ │ │ │ │ └── react-native-svg.d.ts │ │ │ ├── web/ │ │ │ │ ├── ImageSvg.tsx │ │ │ │ ├── PluginBase.ts │ │ │ │ ├── SvgPath.tsx │ │ │ │ └── SvgRect.tsx │ │ │ └── windows/ │ │ │ ├── ImageSvg.tsx │ │ │ ├── PluginBase.ts │ │ │ ├── SvgPath.tsx │ │ │ └── SvgRect.tsx │ │ └── tsconfig.json │ ├── navigation/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── README.md │ │ ├── index.android.js │ │ ├── index.ios.js │ │ ├── index.js │ │ ├── index.macos.js │ │ ├── index.windows.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── common/ │ │ │ │ ├── Types.ts │ │ │ │ ├── assert.ts │ │ │ │ └── lodashMini.ts │ │ │ ├── native-common/ │ │ │ │ ├── Navigator.tsx │ │ │ │ └── NavigatorExperimentalDelegate.tsx │ │ │ ├── typings/ │ │ │ │ ├── react-native-deprecated-custom-components.d.ts │ │ │ │ └── rebound.d.ts │ │ │ └── web/ │ │ │ ├── Navigator.tsx │ │ │ └── NavigatorSceneConfigFactory.tsx │ │ └── tsconfig.json │ ├── netinfo/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── README.md │ │ ├── index.android.js │ │ ├── index.ios.js │ │ ├── index.js │ │ ├── index.macos.js │ │ ├── index.windows.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── android/ │ │ │ │ └── PluginBase.tsx │ │ │ ├── common/ │ │ │ │ ├── Interfaces.ts │ │ │ │ ├── PluginBaseChecker.ts │ │ │ │ └── Types.ts │ │ │ ├── ios/ │ │ │ │ └── PluginBase.tsx │ │ │ ├── macos/ │ │ │ │ └── PluginBase.tsx │ │ │ ├── native-common/ │ │ │ │ └── NetInfo.tsx │ │ │ ├── web/ │ │ │ │ ├── NetInfo.tsx │ │ │ │ └── PluginBase.ts │ │ │ └── windows/ │ │ │ └── PluginBase.ts │ │ └── tsconfig.json │ ├── video/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── README.md │ │ ├── index.android.js │ │ ├── index.ios.js │ │ ├── index.js │ │ ├── index.macos.js │ │ ├── index.windows.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── android/ │ │ │ │ └── PluginBase.tsx │ │ │ ├── common/ │ │ │ │ ├── Interfaces.ts │ │ │ │ ├── PluginBaseChecker.ts │ │ │ │ └── Types.ts │ │ │ ├── ios/ │ │ │ │ └── PluginBase.tsx │ │ │ ├── macos/ │ │ │ │ └── PluginBase.tsx │ │ │ ├── native-common/ │ │ │ │ └── Video.tsx │ │ │ ├── typings/ │ │ │ │ └── react-native-video.d.ts │ │ │ ├── web/ │ │ │ │ ├── PluginBase.ts │ │ │ │ └── Video.tsx │ │ │ └── windows/ │ │ │ ├── PluginBase.ts │ │ │ └── Video.tsx │ │ └── tsconfig.json │ ├── virtuallistview/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── index.android.js │ │ ├── index.ios.js │ │ ├── index.js │ │ ├── index.macos.js │ │ ├── index.windows.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── VirtualListCell.tsx │ │ │ ├── VirtualListView.tsx │ │ │ └── assert.ts │ │ └── tsconfig.json │ └── webview/ │ ├── .eslintrc.json │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── index.android.js │ ├── index.ios.js │ ├── index.js │ ├── index.macos.js │ ├── index.windows.js │ ├── package.json │ ├── src/ │ │ ├── android/ │ │ │ └── PluginBase.tsx │ │ ├── common/ │ │ │ ├── Interfaces.ts │ │ │ ├── PluginBaseChecker.ts │ │ │ └── Types.ts │ │ ├── ios/ │ │ │ └── PluginBase.tsx │ │ ├── macos/ │ │ │ └── PluginBase.tsx │ │ ├── native-common/ │ │ │ └── WebView.tsx │ │ ├── web/ │ │ │ ├── PluginBase.ts │ │ │ └── WebView.tsx │ │ └── windows/ │ │ └── PluginBase.ts │ └── tsconfig.json ├── index.android.js ├── index.ios.js ├── index.js ├── index.macos.js ├── index.windows.js ├── package.json ├── samples/ │ ├── ImageList/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── README.md │ │ ├── android/ │ │ │ ├── app/ │ │ │ │ ├── BUCK │ │ │ │ ├── build.gradle │ │ │ │ ├── build_defs.bzl │ │ │ │ ├── proguard-rules.pro │ │ │ │ └── src/ │ │ │ │ ├── debug/ │ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ │ └── res/ │ │ │ │ │ └── xml/ │ │ │ │ │ └── react_native_config.xml │ │ │ │ └── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── assets/ │ │ │ │ │ └── .gitignore │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── rxpimagelist/ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ │ └── res/ │ │ │ │ └── values/ │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── build.gradle │ │ │ ├── gradle/ │ │ │ │ └── wrapper/ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradle.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── keystores/ │ │ │ │ ├── BUCK │ │ │ │ └── debug.keystore.properties │ │ │ └── settings.gradle │ │ ├── babel.config.js │ │ ├── index.js │ │ ├── ios/ │ │ │ ├── RXPImageList/ │ │ │ │ ├── AppDelegate.h │ │ │ │ ├── AppDelegate.m │ │ │ │ ├── Base.lproj/ │ │ │ │ │ └── LaunchScreen.xib │ │ │ │ ├── Images.xcassets/ │ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ └── Contents.json │ │ │ │ ├── Info.plist │ │ │ │ └── main.m │ │ │ ├── RXPImageList-tvOS/ │ │ │ │ └── Info.plist │ │ │ ├── RXPImageList-tvOSTests/ │ │ │ │ └── Info.plist │ │ │ ├── RXPImageList.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ └── xcshareddata/ │ │ │ │ └── xcschemes/ │ │ │ │ ├── RXPImageList-tvOS.xcscheme │ │ │ │ └── RXPImageList.xcscheme │ │ │ └── RXPImageListTests/ │ │ │ ├── Info.plist │ │ │ └── RXPImageListTests.m │ │ ├── jest/ │ │ │ ├── enzyme.config.js │ │ │ └── jest.config.js │ │ ├── metro.config.js │ │ ├── package.json │ │ ├── scripts/ │ │ │ └── react-native.js │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── config.ts │ │ │ ├── controls/ │ │ │ │ └── SearchField.tsx │ │ │ ├── index.tsx │ │ │ ├── services/ │ │ │ │ └── GiphyClient.ts │ │ │ ├── stores/ │ │ │ │ └── ImageStore.ts │ │ │ └── views/ │ │ │ ├── ImageList.tsx │ │ │ └── RootView.tsx │ │ ├── tsconfig.json │ │ ├── web/ │ │ │ ├── index.hmr.js │ │ │ ├── template.html │ │ │ └── webpack/ │ │ │ ├── common.js │ │ │ ├── dev.js │ │ │ └── prod.js │ │ └── windows/ │ │ ├── .gitignore │ │ ├── RXPImageList/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── MainReactNativeHost.cs │ │ │ ├── Package.appxmanifest │ │ │ ├── Properties/ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ └── Default.rd.xml │ │ │ ├── RXPImageList.csproj │ │ │ ├── RXPImageList_TemporaryKey.pfx │ │ │ └── ReactAssets/ │ │ │ └── .gitignore │ │ └── RXPImageList.sln │ ├── README.md │ ├── RXPTest/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── android/ │ │ │ ├── app/ │ │ │ │ ├── BUCK │ │ │ │ ├── build.gradle │ │ │ │ ├── build_defs.bzl │ │ │ │ ├── proguard-rules.pro │ │ │ │ └── src/ │ │ │ │ ├── debug/ │ │ │ │ │ └── AndroidManifest.xml │ │ │ │ └── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── rxphelloworld/ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ │ └── res/ │ │ │ │ └── values/ │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── build.gradle │ │ │ ├── gradle/ │ │ │ │ └── wrapper/ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradle.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── keystores/ │ │ │ │ ├── BUCK │ │ │ │ └── debug.keystore.properties │ │ │ └── settings.gradle │ │ ├── index.android.js │ │ ├── index.html │ │ ├── index.ios.js │ │ ├── index.windows.js │ │ ├── ios/ │ │ │ ├── RXPTest/ │ │ │ │ ├── AppDelegate.h │ │ │ │ ├── AppDelegate.m │ │ │ │ ├── Base.lproj/ │ │ │ │ │ └── LaunchScreen.xib │ │ │ │ ├── Images.xcassets/ │ │ │ │ │ └── AppIcon.appiconset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Info.plist │ │ │ │ └── main.m │ │ │ └── RXPTest.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── xcshareddata/ │ │ │ └── xcschemes/ │ │ │ ├── RXPTest-tvOS.xcscheme │ │ │ └── RXPTest.xcscheme │ │ ├── metro.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── CommonStyles.tsx │ │ │ ├── Test.tsx │ │ │ ├── TestContainer.tsx │ │ │ ├── TestListView.tsx │ │ │ ├── TestRegistry.ts │ │ │ ├── Tests/ │ │ │ │ ├── AccessibilityTest.tsx │ │ │ │ ├── ActivityIndicatorTest.tsx │ │ │ │ ├── AlertTest.tsx │ │ │ │ ├── AnimationTest.tsx │ │ │ │ ├── AppTest.tsx │ │ │ │ ├── ButtonTest.tsx │ │ │ │ ├── ClipboardTest.tsx │ │ │ │ ├── DragAndDropTest.tsx │ │ │ │ ├── GestureViewTest.tsx │ │ │ │ ├── ImageApiTest.tsx │ │ │ │ ├── ImageInteractiveTest.tsx │ │ │ │ ├── InputTest.tsx │ │ │ │ ├── InternationalTest.tsx │ │ │ │ ├── LinkTest.tsx │ │ │ │ ├── LinkingTest.tsx │ │ │ │ ├── LocationTest.tsx │ │ │ │ ├── ModalTest.tsx │ │ │ │ ├── NetworkTest.tsx │ │ │ │ ├── PickerTest.tsx │ │ │ │ ├── PlatformTest.tsx │ │ │ │ ├── PopupTest.tsx │ │ │ │ ├── ScrollViewBasicTest.tsx │ │ │ │ ├── ScrollViewEventTest.tsx │ │ │ │ ├── StatusBarTest.tsx │ │ │ │ ├── StorageTest.tsx │ │ │ │ ├── TextInputApiTest.tsx │ │ │ │ ├── TextInputInteractiveTest.tsx │ │ │ │ ├── TextTest.tsx │ │ │ │ ├── UserInterfaceTest.tsx │ │ │ │ ├── UserPresenceTest.tsx │ │ │ │ ├── ViewBasicTest.tsx │ │ │ │ ├── ViewMouseTest.tsx │ │ │ │ ├── ViewTouchTest.tsx │ │ │ │ ├── WebViewBasicTest.tsx │ │ │ │ └── WebViewDynamicTest.tsx │ │ │ ├── Utilities.ts │ │ │ └── index.tsx │ │ ├── tsconfig.json │ │ ├── tslint.json │ │ ├── webpack.config.ts │ │ └── windows/ │ │ ├── .gitignore │ │ ├── rxptest/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── MainReactNativeHost.cs │ │ │ ├── Package.appxmanifest │ │ │ ├── Properties/ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ └── Default.rd.xml │ │ │ ├── ReactAssets/ │ │ │ │ └── .gitignore │ │ │ ├── rxptest.csproj │ │ │ └── rxptest_TemporaryKey.pfx │ │ └── rxptest.sln │ ├── TodoList/ │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── README.md │ │ ├── android/ │ │ │ ├── app/ │ │ │ │ ├── BUCK │ │ │ │ ├── build.gradle │ │ │ │ ├── build_defs.bzl │ │ │ │ ├── debug.keystore │ │ │ │ ├── proguard-rules.pro │ │ │ │ └── src/ │ │ │ │ ├── debug/ │ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ │ └── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── rxptodolist/ │ │ │ │ │ └── ReactNativeFlipper.java │ │ │ │ └── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── rxptodolist/ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ │ └── res/ │ │ │ │ └── values/ │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── build.gradle │ │ │ ├── gradle/ │ │ │ │ └── wrapper/ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradle.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ └── settings.gradle │ │ ├── app.json │ │ ├── babel.config.js │ │ ├── buildconfig.js │ │ ├── buildtools/ │ │ │ └── inline-require.js │ │ ├── gulpfile.js │ │ ├── index.android.js │ │ ├── index.ios.js │ │ ├── index.windows.js │ │ ├── ios/ │ │ │ ├── Podfile │ │ │ ├── rxptodolist/ │ │ │ │ ├── AppDelegate.h │ │ │ │ ├── AppDelegate.m │ │ │ │ ├── Base.lproj/ │ │ │ │ │ └── LaunchScreen.xib │ │ │ │ ├── Images.xcassets/ │ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ └── Contents.json │ │ │ │ ├── Info.plist │ │ │ │ └── main.m │ │ │ ├── rxptodolist-tvOS/ │ │ │ │ └── Info.plist │ │ │ ├── rxptodolist-tvOSTests/ │ │ │ │ └── Info.plist │ │ │ ├── rxptodolist.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ └── xcshareddata/ │ │ │ │ └── xcschemes/ │ │ │ │ ├── rxptodolist-tvOS.xcscheme │ │ │ │ └── rxptodolist.xcscheme │ │ │ └── rxptodolistTests/ │ │ │ ├── Info.plist │ │ │ └── rxptodolistTests.m │ │ ├── metro.config.js │ │ ├── nodeserver.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── ts/ │ │ │ ├── app/ │ │ │ │ ├── AppBootstrapper.tsx │ │ │ │ ├── AppBootstrapperNative.tsx │ │ │ │ ├── AppBootstrapperWeb.tsx │ │ │ │ ├── AppConfig.ts │ │ │ │ ├── DeepLinkConverter.ts │ │ │ │ ├── LocalDb.ts │ │ │ │ ├── NavActions.ts │ │ │ │ ├── README.md │ │ │ │ └── Styles.tsx │ │ │ ├── controls/ │ │ │ │ ├── HoverButton.tsx │ │ │ │ ├── Modal.tsx │ │ │ │ ├── README.md │ │ │ │ ├── SimpleButton.tsx │ │ │ │ ├── SimpleDialog.tsx │ │ │ │ ├── SimpleMenu.tsx │ │ │ │ └── VerticalSeparator.tsx │ │ │ ├── index.web.tsx │ │ │ ├── models/ │ │ │ │ ├── IdentityModels.ts │ │ │ │ ├── NavModels.ts │ │ │ │ ├── README.md │ │ │ │ ├── ResponsiveWidthModels.ts │ │ │ │ └── TodoModels.ts │ │ │ ├── modules/ │ │ │ │ ├── README.md │ │ │ │ ├── fonts/ │ │ │ │ │ ├── Fonts.ts │ │ │ │ │ ├── index.native.ts │ │ │ │ │ ├── index.web.ts │ │ │ │ │ └── index.windows.ts │ │ │ │ └── images/ │ │ │ │ ├── Images.ts │ │ │ │ ├── index.native.ts │ │ │ │ └── index.web.ts │ │ │ ├── services/ │ │ │ │ ├── PageUrlService.ts │ │ │ │ ├── README.md │ │ │ │ ├── ServiceManager.ts │ │ │ │ └── ServiceRegistrar.ts │ │ │ ├── stores/ │ │ │ │ ├── CurrentUserStore.ts │ │ │ │ ├── NavContextStore.ts │ │ │ │ ├── README.md │ │ │ │ ├── ResponsiveWidthStore.ts │ │ │ │ └── TodosStore.tsx │ │ │ ├── typings/ │ │ │ │ └── defines.d.ts │ │ │ ├── utilities/ │ │ │ │ ├── ExceptionReporter.ts │ │ │ │ ├── KeyCodes.ts │ │ │ │ ├── README.md │ │ │ │ └── ShimHelpers.ts │ │ │ └── views/ │ │ │ ├── AccountMenuButton.tsx │ │ │ ├── CreateTodoPanel.tsx │ │ │ ├── README.md │ │ │ ├── RootView.tsx │ │ │ ├── TodoCompositeView.tsx │ │ │ ├── TodoListItem.tsx │ │ │ ├── TodoListPanel.tsx │ │ │ ├── TopBarComposite.tsx │ │ │ ├── TopBarStack.tsx │ │ │ └── ViewTodoPanel.tsx │ │ ├── tsconfig.json │ │ ├── web/ │ │ │ ├── css/ │ │ │ │ └── app.css │ │ │ └── index.html │ │ ├── webpack.config.ts │ │ └── windows/ │ │ ├── .gitignore │ │ ├── TodoList/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── MainPage.cs │ │ │ ├── Package.appxmanifest │ │ │ ├── Properties/ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ └── Default.rd.xml │ │ │ ├── TodoList.csproj │ │ │ └── TodoList_TemporaryKey.pfx │ │ └── TodoList.sln │ ├── hello-world/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── README.md │ │ ├── android/ │ │ │ ├── app/ │ │ │ │ ├── BUCK │ │ │ │ ├── build.gradle │ │ │ │ ├── build_defs.bzl │ │ │ │ ├── proguard-rules.pro │ │ │ │ └── src/ │ │ │ │ ├── debug/ │ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ │ └── res/ │ │ │ │ │ └── xml/ │ │ │ │ │ └── react_native_config.xml │ │ │ │ └── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── rxphelloworld/ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ │ └── res/ │ │ │ │ └── values/ │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── build.gradle │ │ │ ├── gradle/ │ │ │ │ └── wrapper/ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradle.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── keystores/ │ │ │ │ ├── BUCK │ │ │ │ └── debug.keystore.properties │ │ │ └── settings.gradle │ │ ├── babel.config.js │ │ ├── index.js │ │ ├── ios/ │ │ │ ├── RXPHelloWorld/ │ │ │ │ ├── AppDelegate.h │ │ │ │ ├── AppDelegate.m │ │ │ │ ├── Base.lproj/ │ │ │ │ │ └── LaunchScreen.xib │ │ │ │ ├── Images.xcassets/ │ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ └── Contents.json │ │ │ │ ├── Info.plist │ │ │ │ └── main.m │ │ │ ├── RXPHelloWorld-tvOS/ │ │ │ │ └── Info.plist │ │ │ ├── RXPHelloWorld-tvOSTests/ │ │ │ │ └── Info.plist │ │ │ ├── RXPHelloWorld.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ └── xcshareddata/ │ │ │ │ └── xcschemes/ │ │ │ │ ├── RXPHelloWorld-tvOS.xcscheme │ │ │ │ └── RXPHelloWorld.xcscheme │ │ │ └── RXPHelloWorldTests/ │ │ │ ├── Info.plist │ │ │ └── RXPHelloWorldTests.m │ │ ├── jest/ │ │ │ ├── enzyme.config.js │ │ │ └── jest.config.js │ │ ├── metro.config.js │ │ ├── package.json │ │ ├── scripts/ │ │ │ └── react-native.js │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── config.ts │ │ │ ├── controls/ │ │ │ │ ├── ProgressIndicator.tsx │ │ │ │ └── ToggleSwitch.tsx │ │ │ ├── index.tsx │ │ │ └── views/ │ │ │ ├── MainPanel.tsx │ │ │ ├── RootView.tsx │ │ │ └── SecondPanel.tsx │ │ ├── tsconfig.json │ │ ├── web/ │ │ │ ├── index.hmr.js │ │ │ ├── template.html │ │ │ └── webpack/ │ │ │ ├── common.js │ │ │ ├── dev.js │ │ │ └── prod.js │ │ └── windows/ │ │ ├── .gitignore │ │ ├── RXPHelloWorld/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── MainReactNativeHost.cs │ │ │ ├── Package.appxmanifest │ │ │ ├── Properties/ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ └── Default.rd.xml │ │ │ ├── RXPHelloWorld.csproj │ │ │ ├── RXPHelloWorld_TemporaryKey.pfx │ │ │ └── ReactAssets/ │ │ │ └── .gitignore │ │ └── RXPHelloWorld.sln │ └── hello-world-js/ │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── android/ │ │ ├── app/ │ │ │ ├── BUCK │ │ │ ├── build.gradle │ │ │ ├── build_defs.bzl │ │ │ ├── proguard-rules.pro │ │ │ └── src/ │ │ │ ├── debug/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── res/ │ │ │ │ └── xml/ │ │ │ │ └── react_native_config.xml │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── rxphelloworld/ │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res/ │ │ │ └── values/ │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── build.gradle │ │ ├── gradle/ │ │ │ └── wrapper/ │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradle.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── keystores/ │ │ │ ├── BUCK │ │ │ └── debug.keystore.properties │ │ └── settings.gradle │ ├── babel.config.js │ ├── index.js │ ├── ios/ │ │ ├── RXPHelloWorld/ │ │ │ ├── AppDelegate.h │ │ │ ├── AppDelegate.m │ │ │ ├── Base.lproj/ │ │ │ │ └── LaunchScreen.xib │ │ │ ├── Images.xcassets/ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Info.plist │ │ │ └── main.m │ │ ├── RXPHelloWorld-tvOS/ │ │ │ └── Info.plist │ │ ├── RXPHelloWorld-tvOSTests/ │ │ │ └── Info.plist │ │ ├── RXPHelloWorld.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── xcshareddata/ │ │ │ └── xcschemes/ │ │ │ ├── RXPHelloWorld-tvOS.xcscheme │ │ │ └── RXPHelloWorld.xcscheme │ │ └── RXPHelloWorldTests/ │ │ ├── Info.plist │ │ └── RXPHelloWorldTests.m │ ├── jest/ │ │ ├── enzyme.config.js │ │ └── jest.config.js │ ├── metro.config.js │ ├── package.json │ ├── scripts/ │ │ └── react-native.js │ ├── src/ │ │ ├── App.js │ │ ├── config.js │ │ ├── controls/ │ │ │ ├── ProgressIndicator.js │ │ │ └── ToggleSwitch.js │ │ ├── index.js │ │ └── views/ │ │ ├── MainPanel.js │ │ ├── RootView.js │ │ └── SecondPanel.js │ ├── web/ │ │ ├── index.hmr.js │ │ ├── template.html │ │ └── webpack/ │ │ ├── common.js │ │ ├── dev.js │ │ └── prod.js │ └── windows/ │ ├── .gitignore │ ├── RXPHelloWorld/ │ │ ├── App.xaml │ │ ├── App.xaml.cs │ │ ├── MainReactNativeHost.cs │ │ ├── Package.appxmanifest │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ └── Default.rd.xml │ │ ├── RXPHelloWorld.csproj │ │ ├── RXPHelloWorld_TemporaryKey.pfx │ │ └── ReactAssets/ │ │ └── .gitignore │ └── RXPHelloWorld.sln ├── src/ │ ├── ReactXP.ts │ ├── android/ │ │ ├── Accessibility.ts │ │ ├── AccessibilityUtil.ts │ │ ├── GestureView.tsx │ │ ├── Image.tsx │ │ ├── ReactXP.ts │ │ ├── StatusBar.ts │ │ └── Text.tsx │ ├── common/ │ │ ├── Accessibility.ts │ │ ├── AccessibilityUtil.ts │ │ ├── AppConfig.ts │ │ ├── Bezier.ts │ │ ├── Easing.ts │ │ ├── GestureView.tsx │ │ ├── Image.ts │ │ ├── Interfaces.ts │ │ ├── Linking.ts │ │ ├── Location.ts │ │ ├── ModuleInterface.ts │ │ ├── PopupContainerViewBase.tsx │ │ ├── StyleLeakDetector.ts │ │ ├── Types.ts │ │ ├── assert.ts │ │ ├── lodashMini.ts │ │ └── utils/ │ │ ├── AutoFocusHelper.ts │ │ ├── EventHelpers.ts │ │ ├── FocusManager.ts │ │ ├── PromiseDefer.ts │ │ └── Timers.ts │ ├── ios/ │ │ ├── Accessibility.ts │ │ ├── AccessibilityUtil.ts │ │ ├── GestureView.tsx │ │ ├── Linking.ts │ │ ├── ReactXP.ts │ │ └── StatusBar.ts │ ├── macos/ │ │ ├── Accessibility.ts │ │ ├── AccessibilityUtil.ts │ │ ├── GestureView.tsx │ │ ├── Input.ts │ │ ├── Linking.ts │ │ ├── ReactXP.ts │ │ ├── StatusBar.ts │ │ └── View.tsx │ ├── native-common/ │ │ ├── Accessibility.ts │ │ ├── AccessibilityUtil.ts │ │ ├── ActivityIndicator.tsx │ │ ├── Alert.ts │ │ ├── Animated.tsx │ │ ├── App.ts │ │ ├── Button.tsx │ │ ├── Clipboard.ts │ │ ├── FrontLayerViewManager.tsx │ │ ├── GestureView.tsx │ │ ├── Image.tsx │ │ ├── Input.ts │ │ ├── International.ts │ │ ├── Link.tsx │ │ ├── Linking.ts │ │ ├── MainViewStore.ts │ │ ├── Modal.ts │ │ ├── ModalContainer.tsx │ │ ├── Picker.tsx │ │ ├── Platform.ts │ │ ├── Popup.ts │ │ ├── PopupContainerView.tsx │ │ ├── RootView.tsx │ │ ├── ScrollView.tsx │ │ ├── Storage.ts │ │ ├── StyleLeakDetector.ts │ │ ├── Styles.ts │ │ ├── Text.tsx │ │ ├── TextInput.tsx │ │ ├── UserInterface.tsx │ │ ├── UserPresence.ts │ │ ├── View.tsx │ │ ├── ViewBase.tsx │ │ └── utils/ │ │ ├── EventHelpers.ts │ │ └── lodashMini.ts │ ├── native-desktop/ │ │ ├── App.ts │ │ ├── Input.ts │ │ ├── RootView.tsx │ │ ├── ScrollView.tsx │ │ └── utils/ │ │ └── FocusManager.ts │ ├── tslint/ │ │ ├── groupedImportRule.ts │ │ ├── incorrectThisPropsRule.ts │ │ └── noUnreferencedStylesRule.ts │ ├── typings/ │ │ ├── prop-types.d.ts │ │ ├── react-native-extensions.d.ts │ │ ├── react-native-windows.d.ts │ │ ├── rebound.d.ts │ │ └── window.d.ts │ ├── web/ │ │ ├── Accessibility.ts │ │ ├── AccessibilityAnnouncer.tsx │ │ ├── AccessibilityUtil.ts │ │ ├── ActivityIndicator.tsx │ │ ├── Alert.tsx │ │ ├── AlertModalContent.tsx │ │ ├── Animated.tsx │ │ ├── App.ts │ │ ├── Button.tsx │ │ ├── Clipboard.ts │ │ ├── CustomScrollbar.ts │ │ ├── FrontLayerViewManager.tsx │ │ ├── GestureView.tsx │ │ ├── Image.tsx │ │ ├── Input.ts │ │ ├── International.ts │ │ ├── Link.tsx │ │ ├── Linking.ts │ │ ├── Modal.ts │ │ ├── ModalContainer.tsx │ │ ├── Picker.tsx │ │ ├── Platform.ts │ │ ├── Popup.ts │ │ ├── PopupContainerView.tsx │ │ ├── ReactXP.ts │ │ ├── RootView.tsx │ │ ├── ScrollView.tsx │ │ ├── ScrollViewConfig.ts │ │ ├── StatusBar.tsx │ │ ├── Storage.ts │ │ ├── Styles.ts │ │ ├── Text.tsx │ │ ├── TextInput.tsx │ │ ├── UserInterface.ts │ │ ├── UserPresence.ts │ │ ├── View.tsx │ │ ├── ViewBase.tsx │ │ ├── animated/ │ │ │ └── executeTransition.ts │ │ ├── listAnimations/ │ │ │ ├── AnimateListEdits.tsx │ │ │ └── MonitorListEdits.tsx │ │ ├── utils/ │ │ │ ├── AppVisibilityUtils.ts │ │ │ ├── FocusManager.ts │ │ │ ├── MouseResponder.ts │ │ │ ├── lodashMini.ts │ │ │ └── restyleForInlineText.tsx │ │ └── window.ts │ └── windows/ │ ├── Accessibility.ts │ ├── AccessibilityAnnouncer.tsx │ ├── AccessibilityUtil.ts │ ├── App.ts │ ├── Button.tsx │ ├── GestureView.tsx │ ├── Link.tsx │ ├── ReactXP.ts │ ├── RootView.tsx │ ├── StatusBar.ts │ ├── Text.tsx │ ├── TextInput.tsx │ └── View.tsx └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc.json ================================================ { "parserOptions": { "project": "./tsconfig.json" }, "extends": ["skype", "skype/react"], "rules": { "no-console": [ "error", { "allow": [ "error", "warn", "log" ] } ], "camelcase": "off", "@typescript-eslint/camelcase": [ "error", { "allow": [ "^reactxp_", "^mixin_", "^UNSAFE_", "^Radio_button_" /* Android properties only */ ] } ], "@typescript-eslint/no-parameter-properties": "off" }, "overrides": [ { "files": [ "*.tsx" ], "rules": { "@typescript-eslint/explicit-function-return-type": "off" } }, { "files": [ "ReactXP.ts", "ModuleInterface.ts" ], "rules": { "no-self-assign": "off", "prefer-const": "off", "@typescript-eslint/no-namespace": "off", "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/prefer-namespace-keyword": "off" } }, { "files": [ "Easing.ts" ], "rules": { "@typescript-eslint/member-naming": [ "error", { "public": "^[A-Z]\\w+$" } ] } }, { "files": [ "*.d.ts" ], "rules": { "@typescript-eslint/prefer-namespace-keyword": "off", "@typescript-eslint/no-unused-vars": "off" } }, { "files": [ "*/web/Animated.tsx", "*/native-desktop/RootView.tsx" ], "rules": { "@typescript-eslint/member-naming": "off" } } ] } ================================================ FILE: .gitignore ================================================ # Logs logs *.log npm-debug.log* # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules /samples/*/node_modules # Optional npm cache directory .npm # Optional REPL history .node_repl_history # Build artifacts /src/.vs /src/bin /src/obj /dist # Miscellaneous user files *.user .vscode .DS_STORE .idea/* .vs ================================================ FILE: .npmignore ================================================ /node_modules /src/.vs /src/bin /src/obj /samples /extensions /docs *.user .eslintrc ================================================ FILE: .npmrc ================================================ ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "node" dist: trusty sudo: false addons: chrome: stable install: - node --version - npm --version - npm i script: - npm run lint - npm run build cache: directories: - node_modules ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to ReactXP We welcome contributions to ReactXP! This gude provides some tips for a successful contributions. For complex changes, we recommend filing a GitHub issue to start a discussion with project maintainers before implementing the change. ## Pull Requests Please make sure the following is done when submitting a pull request: 1. Fork the repo and create your branch from `master`. 2. If you've added code that should be tested, add test code to the RXPTest sample app. 3. If you've changed APIs, update the documentation files. 4. Ensure the test suite passes. 5. If you haven't already, complete the Contributor License Agreement ("CLA"). ## Testing Your Change To test your change: 1. Rebuild reactxp: `npm i` and `npm run build` 2. Switch to the RXPTest sample directory: `cd ./samples/RXPTest` 3. Update dependencies: `npm i` 4. Copy the locally-built reactxp library: `cp -r ../../dist/* ./node_modules/reactxp/dist` 5. Rebuild the test app: `npm run web-watch` or `npm run rn-watch` 6. If testing the web version: - for desktop: open the test in the browser: `open ./index.html` and run the test - for mobile: run `npm run web -- -host YOUR_LOCAL_IP` then on your mobile enter `http://YOUR_LOCAL_IP:8080` 7. If testing one or more RN versions, open the corresponding native project and build and run the test ## Contributor License Agreement ("CLA") This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. You must sign a Contribution License Agreement (CLA) before your PR will be merged. This is a one-time requirement for Microsoft projects in GitHub. You can read more about [Contribution License Agreements (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) on Wikipedia. You can sign the Microsoft Contribution License Agreement by visiting https://cla.microsoft.com/. Use your GitHub account to login. ## Issues We use GitHub issues to track bugs. ## Coding Style ReactXP is written in TypeScript and uses tslint to help enforce an internally-consistent coding style. Contributions should be consistent with this style. ================================================ FILE: LICENSE ================================================ ReactXP Copyright (c) Microsoft Corporation All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # ReactXP [](https://github.com/Microsoft/reactxp/blob/master/LICENSE) [](https://www.npmjs.com/package/reactxp) [](https://dev.azure.com/ms/reactxp/_build/latest?definitionId=16) [](https://travis-ci.org/Microsoft/reactxp) [](https://www.npmjs.com/package/reactxp) [](https://github.com/Microsoft/reactxp#contributing) [](https://gitter.im/msreactxp/Lobby) ReactXP is a library for cross-platform app development using React and React Native. ## ReactXP End of Life The ReactXP library is no longer being maintained and is is considered “end of life”. We recommend alternatives such as [React Native for Web](https://necolas.github.io/react-native-web/docs/). The ReactXP github project will be put into “archive” mode and will remain available in read-only form for the benefit of those who are still using it within older projects, but no new versions will be published. ## Why ReactXP With React and React Native, your web app can share most of its logic with your iOS and Android apps, but the view layer needs to be implemented separately for each platform. We have taken this a step further and developed a thin cross-platform layer we call ReactXP. If you write your app to this abstraction, you can share your view definitions, styles and animations across multiple target platforms. Of course, you can still provide platform-specific UI variants, but this can be done selectively where desired. ## Getting Started The [samples](/samples) directory contains a minimal “Hello World” app that demonstrates some basic ReactXP functionality. You can use this as a starting point. Just follow the build instructions in the README file. Also included in the samples directory is the [RXPTest app](/samples/RXPTest) which attempts to exercise all of the functionality of ReactXP. It is a good source to consult for sample usage of APIs, components, and props. You can read more about ReactXP and its APIs from the [ReactXP official Documentation](https://microsoft.github.io/reactxp/docs/getting-started.html). Use the command-line tool called [create-rx-app](https://github.com/a-tarasyuk/create-rx-app) to create a starter project. ```sh npm install create-rx-app -g create-rx-app AppName ``` or ```sh npx create-rx-app AppName ``` By default the project will be created in TypeScript. However if you prefer JavaScript instead, add `--javascript` when creating the project. This will create a directory called **AppName** inside the current working directory. Inside **AppName**, this will generate the initial project structure and install all of its dependencies. Once this installation is done, there are some commands you can run in the project directory: - `npm run start:web` - runs the Web version of the app in the development mode - `npm run build:web` - builds the Web version of the app for production to the **dist-web** folder - `npm run start:ios` - runs the iOS version of the app and attempts to open in the iOS Simulator if you're on a Mac and have it installed - `npm run start:android` - runs the Android version of the app and attempts to open your app on a connected Android device or emulator - `npm run start:windows` - runs the Windows version of the app - `npm start:rn-dev-server` - runs react native (RN) development server ### Prerequisites * [Node.Js](https://nodejs.org/) ([Setup Instructions](https://nodejs.org/en/download/package-manager/)) * [React Native](https://facebook.github.io/react-native/) ([Setup Instructions](https://facebook.github.io/react-native/docs/getting-started)) ## ESLint rules > [TSLint will be deprecated some time in 2019](https://github.com/palantir/tslint) If you plan to migrate your projects from TSLint to ESlint and want to continue using the [_rules_](https://github.com/microsoft/reactxp/tree/master/src/tslint) to automate search common problems in *ReactXP* usage, you can use [eslint-plugin-reactxp](https://github.com/a-tarasyuk/eslint-plugin-reactxp). ## Contributing We welcome contributions to ReactXP. See the [CONTRIBUTING](./CONTRIBUTING.md) file for how to help out. ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details ================================================ FILE: README_Hindi.md ================================================ # ReactXP [](https://github.com/Microsoft/reactxp/blob/master/LICENSE) [](https://www.npmjs.com/package/reactxp) [](https://travis-ci.org/Microsoft/reactxp) [](https://www.npmjs.com/package/reactxp) [](https://github.com/Microsoft/reactxp#contributing) [](https://gitter.im/msreactxp/Lobby) ReactXP एक क्रॉस-प्लॅटफॉर्म ऍप डेवलपमेंट लाइब्रेरी है जिसमे React और React Native का उपयोग किया जाता है. ##ReactXP क्यों ? React और React Native के साथ आपका वेब ऍप, iOS और Android ऍप के साथ ज्यादातर तार्किक शेयर कर सकता है, लेकिन view layer आपको अलग से इम्प्लीमेंट करना होगा हर एक प्लॅटफॉर्म के लिए. हमने एक कदम आगे लेकर एक हल्का क्रॉस-प्लॅटफॉर्म लेयर विकसित किया है जिसे हम ReactXP बुलाते है. अगर आप आपके ऍप को इस abstraction के हिसाब से लिखते है फिर आप आपके view definitions, styles और animations को शेयर कर सकते है मल्टिपल टारगेट प्लेटफॉर्म्स पर. आप प्लेटफार्म स्पेसिफिक UI variants भी प्रदान कर सकते है जैसे आप चुनेंगे जब आपकी मर्ज़ी हो| ## शुरुआत दिए हुए [samples](/samples) directory में आपको एक आसान सा “Hello World” ऍप मिलेगा जिसमे ReactXP के बुनियादी कार्यक्षमता दिखाई गई है. आप उससे स्टार्टिंग पॉइंट की तरह प्रयोग कर सकते है. दिए हुए README के सूचनाओं का पालन कर सकते है. samples directory[RXPTest app](/samples/RXPTest) में RXPTest ऍप भी दिया गया है जो ReactXP के सारे कार्यक्षमताओं का उपयोग करने का प्रयास करता है. ये एक अछि सोर्स है APIs, components और props का उपयोग जान्ने के लिए. आप ReactXP और उसके APIs के बारे में और पड़ सकते है इस वेबसाइट पर [ReactXP official Documentation](https://microsoft.github.io/reactxp/docs/getting-started.html). [create-rx-app](https://github.com/a-tarasyuk/create-rx-app) नाम के कमांड-लाइन टूल का उपयोग करके स्टार्टर प्रोजेक्ट बनाइये. ```sh npm install create-rx-app -g create-rx-app AppName ``` अथवा ```sh npx create-rx-app AppName ``` शुरुवात में प्रोजेक्ट TypeScript में उपलब्ध किया जाता hai. लेकिन अगर आपको JavaScript में करना हो तोह प्रोजेक्ट बनाते समय फिर `--javascript` का उपयोग करें. ये **AppName** नाम का डायरेक्टरी बनाएगा वर्किंग डायरेक्टरी में. **AppName** के अंदर शुरुवाती प्रोजेक्ट का आकार बना हुआ रहेगा और सारे dependencies इनस्टॉल किये हुए रहेंगे. इंस्टालेशन ख़तम होने के बाद निचे दिए हुए कुछ commands आप रन कर सकते है प्रोजेक्ट डायरेक्टरी में : - `npm run start:web` - ऍप के Web version को development mode में run करने के लिए - `npm run build:web` - ऍप के Web version को बनता है production के लिए **dist-web** फोल्डर में - `npm run start:ios` - ऍप के iOS version को run करके iOS Simulator में खोलके की कोशिश करता है अगर आप Mac इस्तेमाल कर रहे हो और उसमे इनस्टॉल किया हुआ हो - `npm run start:android` - ऍप के Android version को run करके आपके ऍप को connected Android device या फिर emulator पर खोलने के कोशिश करता है - `npm run start:windows` - ऍप के Windows version को run करता है - `npm start:rn-dev-server` - React native (RN) development server run करता है ### Prerequisites * [Node.Js](https://nodejs.org/) ([Setup Instructions](https://nodejs.org/en/download/package-manager/)) * [React Native](https://facebook.github.io/react-native/) ([Setup Instructions](https://facebook.github.io/react-native/docs/getting-started)) ## Contributing This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. You must sign a Contribution License Agreement (CLA) before your PR will be merged. This is a one-time requirement for Microsoft projects in GitHub. You can read more about [Contribution License Agreements (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) on Wikipedia. You can sign the Microsoft Contribution License Agreement by visiting https://cla.microsoft.com/. Use your GitHub account to login. ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details ================================================ FILE: SECURITY.md ================================================ ## Security Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. ## Preferred Languages We prefer all communications to be in English. ## Policy Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). ================================================ FILE: azure-pipelines.yml ================================================ # Node.js # Build a general Node.js project with npm. # Add steps that analyze code, save build artifacts, deploy, and more: # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript pool: vmImage: 'Ubuntu 16.04' steps: - task: NodeTool@0 inputs: versionSpec: '8.x' displayName: 'Install Node.js' - script: | npm ci npm run lint npm run build displayName: 'npm install and build' ================================================ FILE: docs/Gemfile ================================================ source 'https://rubygems.org' # jekyll, which builds it all # 3.0 includes sass processing gem 'jekyll', '~>3.1' # Jekyll extensions gem 'jekyll-redirect-from' gem 'jekyll-paginate' ================================================ FILE: docs/_config.yml ================================================ theme: jekyll-theme-slate name: ReactXP description: A library for building cross-platform apps url: https://microsoft.github.io baseurl: /reactxp permalink: /blog/:year/:month/:day/:title.html paginate_path: /blog/page:num/ paginate: 5 timezone: America/Los_Angeles defaults: - scope: path: blog type: pages values: layout: post sectionid: blog - scope: path: docs type: pages values: layout: docs sectionid: docs - scope: path: community type: pages values: layout: default sectionid: community - scope: path: versions type: pages values: layout: default sectionid: versions exclude: - Gemfile - README.md sass: sass_dir: ./_sass gems: - jekyll-redirect-from - jekyll-paginate ================================================ FILE: docs/_data/authors.yml ================================================ # Map of short name to more information. `name` will be used but if you don't # want to use your real name, just use whatever. If url is included, your name # will be a link to the provided url. erictraut: name: Eric Traut url: https://github.com/erictraut ================================================ FILE: docs/_data/nav_docs.yml ================================================ - title: Quick Start items: - id: getting-started title: Getting Started - id: using-reactxp title: Using ReactXP - id: faq title: FAQ - id: react_concepts title: React Concepts - id: react_lifecycle title: Component Lifecycle - id: react_stores title: Stores & Services - id: styles title: Styles - id: animations title: Animations - id: accessibility title: Accessibility - id: extensions title: Extensions - title: Components items: - id: components/activityindicator title: ActivityIndicator - id: components/button title: Button - id: components/gestureview title: GestureView - id: components/image title: Image - id: components/link title: Link - id: components/picker title: Picker - id: components/scrollview title: ScrollView - id: components/text title: Text - id: components/textinput title: TextInput - id: components/view title: View - id: components/webview title: WebView - title: APIs items: - id: apis/accessibility title: Accessibility - id: apis/alert title: Alert - id: apis/app title: App - id: apis/clipboard title: Clipboard - id: apis/input title: Input - id: apis/international title: International - id: apis/linking title: Linking - id: apis/location title: Location - id: apis/modal title: Modal - id: apis/network title: Network - id: apis/platform title: Platform - id: apis/popup title: Popup - id: apis/statusbar title: StatusBar - id: apis/storage title: Storage - id: apis/userinterface title: UserInterface - id: apis/userpresence title: UserPresence - title: Extensions items: - id: extensions/database title: Database - id: extensions/imagesvg title: ImageSvg - id: extensions/navigator title: Navigator - id: extensions/netinfo title: NetInfo - id: extensions/restclient title: REST Client - id: extensions/video title: Video - id: extensions/virtuallistview title: VirtualListView - id: extensions/webview title: WebView ================================================ FILE: docs/_includes/blog_post.html ================================================ {% assign page = include.page %}


styles above */
display: block;
/* Cancel out styles for `li code` in case we have a within an
. */
background: none;
padding: 0;
}
.highlight pre .lineno {
display: inline-block;
width: 22px;
padding-right: 5px;
margin-right: 10px;
color: #bebec5;
text-align: right;
}
/* Echo out a label for the example */
div.highlight:after {
position: absolute;
top: 0;
right: 0;
left: 0;
padding: 3px 7px;
font-size: 12px;
font-weight: bold;
color: #c2c0bc;
background-color: #f1ede4;
content: "Code";
}
.downloadCenter {
text-align: center;
margin-top: 20px;
margin-bottom: 25px;
}
.downloadSection:hover {
text-decoration: none !important;
}
@include bp-large {
.nav-main {
position: relative;
}
.container {
padding-top: 0;
}
}
.post {
margin-bottom: 30px;
}
.post img {
max-width: 100%;
height: auto;
}
.pagination {
margin-bottom: 30px;
/* Trick to get the wrapper to expand to fit floating elements */
width: 100%;
overflow: hidden;
.next {
float: right;
}
}
// Twitter embeds. Need to !important because they inline margin on the iframe.
div[data-twttr-id] iframe {
margin: 10px auto !important;
width: 100% !important;
}
.fb_iframe_widget {
max-width: 100%;
* {
max-width: 100%;
}
}
#twitter-widget-0 {
@include bp-large {
display: none !important; // Need !important because they inline display on the iframe.
}
}
/* Acknowledgements */
.three-column {
@include clearfix;
}
.three-column > ul {
float: left;
margin-left: 30px;
width: 190px;
}
.three-column > ul:first-child {
margin-left: 20px;
}
/* Algolia Doc Search */
input#algolia-doc-search {
background: transparent url('/react/img/search.png') no-repeat 10px center;
background-size: 16px 16px;
position: relative;
vertical-align: top;
margin-left: 10px;
padding: 0 10px;
padding-left: 35px;
height: 30px;
margin-top: 10px;
font-size: 16px;
line-height: 20px;
background-color: #333;
border-radius: 4px;
color: white;
outline: none;
width: 170px;
transition: width .2s ease;
&:focus {
width: 240px;
}
@include bp-large {
background-color: transparent;
width: 0;
cursor: pointer;
&:focus {
width: 200px;
background-color: #333;
}
}
}
.algolia-autocomplete .aa-dropdown-menu {
margin-left: -110px;
margin-top: -4px;
}
.algolia-autocomplete {
vertical-align: top;
height: 53px;
}
.algolia-docsearch-suggestion {
border-bottom-color: #c05b4d;
}
.algolia-docsearch-suggestion--category-header {
background-color: #cc7a6f;
}
.algolia-docsearch-suggestion--highlight {
color: #c05b4d;
}
.algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--highlight {
background-color: #c05b4d;
}
.aa-cursor .algolia-docsearch-suggestion--content {
color: #c05b4d;
}
.aa-cursor .algolia-docsearch-suggestion {
background: #f1f3f5;
}
/**
* Footer
*/
footer.nav-footer {
box-sizing: border-box;
border: none;
font-weight: 300;
color: #202020;
font-size: 15px;
line-height: 24px;
background: #2D2D2D;
box-shadow: inset 0 10px 10px -5px #0d1116;
padding-top: 2em;
padding-bottom: 2em;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
footer .sitemap {
display: flex;
display: -ms-flexbox;
justify-content: space-between;
max-width: 1080px;
margin: 0 auto 3em;
}
footer .sitemap div {
flex: 1;
-ms-flex: 1;
}
footer .sitemap .nav-home {
display: table;
margin: -12px 20px 0 0;
padding: 10px;
width: 60px;
height: 60px;
transition: opacity 0.15s ease-in-out;
background: url("../img/logo_small_gray.png") no-repeat content-box;
}
footer .sitemap .nav-home:hover,
footer .sitemap .nav-home:focus {
background-image: url("../img/logo_small.png");
}
@media screen and (max-width: 740px) {
footer .sitemap {
display: none;
}
}
footer .sitemap a {
color: white;
display: table;
margin: 2px -10px;
padding: 3px 10px;
}
footer .sitemap a:hover,
footer .sitemap a:focus {
color: #61dafb;
text-decoration: none;
}
footer .sitemap h6 > a:hover,
footer .sitemap h6 > a:focus {
color: #61dafb;
text-decoration: none;
}
footer .sitemap h5,
footer .sitemap h6 {
margin: 0 0 10px;
}
footer .sitemap h5,
footer .sitemap h6,
footer .sitemap h5 > a,
footer .sitemap h6 > a {
color: white;
}
footer .sitemap h5 > a,
footer .sitemap h6 > a {
margin: 0 -10px;
}
footer .fbOpenSource {
display: block;
margin: 1em auto;
opacity: 0.4;
transition: opacity 0.15s ease-in-out;
width: 170px;
}
footer .fbOpenSource:hover {
opacity: 1.0;
}
footer .copyright {
color: rgba(255, 255, 255, 0.4);
text-align: center;
}
================================================
FILE: docs/css/syntax.css
================================================
.highlight pre code {
color: #637c84;
}
.highlight {
color: #333333;
background: #f8f5ec;
}
.highlight .c {
color: #93a1a1;
}
.highlight .g {
color: #637c84;
}
/* Generic */
.highlight .k {
color: #859900;
}
/* Keyword */
.highlight .l {
color: #637c84;
}
/* Literal */
.highlight .n {
color: #637c84;
}
/* Name */
.highlight .o {
color: #859900;
}
/* Operator */
.highlight .x {
color: #cc7a6f;
}
/* Other */
.highlight .p {
color: #637c84;
}
/* Punctuation */
.highlight .cm {
color: #93a1a1;
}
/* Comment.Multiline */
.highlight .cp {
color: #859900;
}
/* Comment.Preproc */
.highlight .c1 {
color: #93a1a1;
}
/* Comment.Single */
.highlight .cs {
color: #859900;
}
/* Comment.Special */
.highlight .gd {
color: #36958e;
}
/* Generic.Deleted */
.highlight .ge {
font-style: italic;
color: #637c84;
}
/* Generic.Emph */
.highlight .gr {
color: #dc322f;
}
/* Generic.Error */
.highlight .gh {
color: #cc7a6f;
}
/* Generic.Heading */
.highlight .gi {
color: #859900;
}
/* Generic.Inserted */
.highlight .go {
color: #637c84;
}
/* Generic.Output */
.highlight .gp {
color: #637c84;
}
/* Generic.Prompt */
.highlight .gs {
font-weight: bold;
color: #637c84;
}
/* Generic.Strong */
.highlight .gu {
color: #cc7a6f;
}
/* Generic.Subheading */
.highlight .gt {
color: #637c84;
}
/* Generic.Traceback */
.highlight .kc {
color: #cc7a6f;
}
/* Keyword.Constant */
.highlight .kd {
color: #268bd2;
}
/* Keyword.Declaration */
.highlight .kn {
color: #859900;
}
/* Keyword.Namespace */
.highlight .kp {
color: #859900;
}
/* Keyword.Pseudo */
.highlight .kr {
color: #268bd2;
}
/* Keyword.Reserved */
.highlight .kt {
color: #dc322f;
}
/* Keyword.Type */
.highlight .ld {
color: #637c84;
}
/* Literal.Date */
.highlight .m {
color: #36958e;
}
/* Literal.Number */
.highlight .s {
color: #36958e;
}
/* Literal.String */
.highlight .na {
color: #637c84;
}
/* Name.Attribute */
.highlight .nb {
color: #b58900;
}
/* Name.Builtin */
.highlight .nc {
color: #268bd2;
}
/* Name.Class */
.highlight .no {
color: #cc7a6f;
}
/* Name.Constant */
.highlight .nd {
color: #268bd2;
}
/* Name.Decorator */
.highlight .ni {
color: #cc7a6f;
}
/* Name.Entity */
.highlight .ne {
color: #cc7a6f;
}
/* Name.Exception */
.highlight .nf {
color: #268bd2;
}
/* Name.Function */
.highlight .nl {
color: #637c84;
}
/* Name.Label */
.highlight .nn {
color: #637c84;
}
/* Name.Namespace */
.highlight .nx {
color: #637c84;
}
/* Name.Other */
.highlight .py {
color: #637c84;
}
/* Name.Property */
.highlight .nt {
color: #268bd2;
}
/* Name.Tag */
.highlight .nv {
color: #268bd2;
}
/* Name.Variable */
.highlight .ow {
color: #859900;
}
/* Operator.Word */
.highlight .w {
color: #637c84;
}
/* Text.Whitespace */
.highlight .mf {
color: #36958e;
}
/* Literal.Number.Float */
.highlight .mh {
color: #36958e;
}
/* Literal.Number.Hex */
.highlight .mi {
color: #36958e;
}
/* Literal.Number.Integer */
.highlight .mo {
color: #36958e;
}
/* Literal.Number.Oct */
.highlight .sb {
color: #93a1a1;
}
/* Literal.String.Backtick */
.highlight .sc {
color: #36958e;
}
/* Literal.String.Char */
.highlight .sd {
color: #637c84;
}
/* Literal.String.Doc */
.highlight .s2 {
color: #36958e;
}
/* Literal.String.Double */
.highlight .se {
color: #cc7a6f;
}
/* Literal.String.Escape */
.highlight .sh {
color: #637c84;
}
/* Literal.String.Heredoc */
.highlight .si {
color: #36958e;
}
/* Literal.String.Interpol */
.highlight .sx {
color: #36958e;
}
/* Literal.String.Other */
.highlight .sr {
color: #dc322f;
}
/* Literal.String.Regex */
.highlight .s1 {
color: #36958e;
}
/* Literal.String.Single */
.highlight .ss {
color: #36958e;
}
/* Literal.String.Symbol */
.highlight .bp {
color: #268bd2;
}
/* Name.Builtin.Pseudo */
.highlight .vc {
color: #268bd2;
}
/* Name.Variable.Class */
.highlight .vg {
color: #268bd2;
}
/* Name.Variable.Global */
.highlight .vi {
color: #268bd2;
}
/* Name.Variable.Instance */
.highlight .il {
color: #36958e;
}
================================================
FILE: docs/docs/accessibility.md
================================================
---
id: accessibility
title: Accessibility
layout: docs
category: Overview
permalink: docs/accessibility.html
next: extensions
---
ReactXP exposes a common way to implement accessibility features across platforms.
A screen reader is an assistive technology available for visually-impaired users. It allows users to navigate through an application by focusing actionable components and announcing the purpose of those components.
ReactXP components [View](components/view), [Button](components/button), [GestureView](components/gestureview) and [TextInput](components/textinput) implement a common set of accessibility-related props described below.
Additional [Accessibility APIs](apis/accessibility) are provided for programmatically invoking the screen reader to announce events.
Accessibility traits override the default screen reader behaviors where appropriate. For example, the default behavior for [Button](components/button) is to append the text "Button" at the end of the label. In most cases, the default traits provide the desired behavior, but it is sometimes useful to provide additional information to the screen reader.
## Types
``` javascript
export enum ImportantForAccessibility {
// Platform decides which views are important for accessibility and brings
// the screen reader focus on those views
Auto,
// Groups all subviews under the view, allowing the screen reader to focus
// just this view; if the accessibilityLabel is specified, it is announced;
// otherwise, the labels of its children are used
Yes,
// Tells the screen reader that it can focus the subviews of this view
No,
// Hides the view and its subviews from the screen reader
NoHideDescendants
}
```
## Props
``` javascript
// Array of strings that will be added as custom actions on iOS
accessibilityActions?: string[] = undefined;
// Label that is read by the screen reader
accessibilityLabel?: string = undefined;
// Overrides or augments default screen reader behavior
accessibilityTraits?: AccessibilityTrait | AccessibilityTrait[] = undefined;
// Screen reader focus behavior
importantForAccessibility?: ImportantForAccessibility = Auto;
// Callback function invoked for accessibility action events
onAccessibilityAction?: (e: SyntheticEvent) => void;
// Keyboard tab order
tabIndex?: number = undefined; // web only
// Used to define the current value for a range widget such as a slider, spinbutton or progressbar
ariaValueNow?: number = undefined; // web only
```
================================================
FILE: docs/docs/animations.md
================================================
---
id: animations
title: Animations
layout: docs
category: Overview
permalink: docs/animations.html
next: accessibility
---
ReactXP supports a powerful animation abstraction. Individual style elements (e.g. transforms, opacity, or backgroundColor) can be animated.
## Animatable Components
Four base RX classes can have animatable styles:
* Animated.View
* Animated.Image
* Animated.Text
* Animated.TextInput
These component types should be used in place of the normal [View](components/view), [Image](components/image), [Text](components/text) or [TextInput](components/textinput) in the render method. In general, style properties expressed as numeric values or colors can be animated. Properties with text values (e.g. flexDirection or fontWeight) cannot be animated.
## Animated Values
The following example shows how to create animated values with an initial value. Animated values are typically stored as instance variables within a component class. They can also be stored in the state structure.
``` javascript
let animatedOpacityValue = RX.Animated.createValue(1.0);
let animatedScaleValue = RX.Animated.createValue(1.0);
```
For animated color values, it is possible to create interpolated values that map from a numeric range to a color range. In this example, the value smoothly transitions from white to red to black as the value increases from 0 to 1.
``` javascript
let animatedColorValue = RX.Animated.createValue(0.0);
let interpolatedValue = RX.Animated.interpolate(animatedColorValue,
[0.0, 0.5, 1.0], ['white', 'red', 'black']);
```
## Animated Styles
Once an animated value is created, it can be associated with an animated style.
Some animated style values are more expensive than others. Some affect the layout of elements (e.g. width, height, top, left), so the layout engine needs to be invoked during each stage of the animations. It's faster to avoid these and stick to styles that don't affect the layout (e.g. opacity and transforms).
This example demonstrates how a style sheet can contain multiple animated values.
``` javascript
let animatedStyle = RX.Styles.createAnimatedViewStyle({
opacity: animatedOpacityValue,
transform: [{
scale: animatedScaleValue
}]
});
```
Animated style sheets can be combined with other static styles.
``` javascript
render() {
}
```
## Simple Timing Animations
To describe an animation, specify the final value of the animated value and a duration (specified in milliseconds). An optional easing function allows for a variety of animation curves including linear, step-wise, and cubic bezier.
Once an animation is defined, a call to the start() method starts the animation. The start method takes an optional parameter, a callback that is executed when the animation completes.
``` javascript
let opacityAnimation = RX.Animated.timing(animatedScaleValue,
{ toValue: 0.0, duration: 250, easing: RX.Animated.Easing.InOut() }
);
opacityAnimation.start(() => this._doSomethingWhenAnimationCompletes());
```
## Composite Animations
Sometimes it's useful to execute multiple animations in parallel or in sequence. This is easily accommodated by calling RX.Animated.parallel() or RX.Animated.sequence(). Composite animations can be nested to create sophisticated sequences.
``` javascript
let compositeAnimation = RX.Animated.parallel([
RX.Animated.timing(animatedScaleValue,
{ toValue: 0.0, duration: 250, easing: RX.Animated.Easing.InOut() }
),
RX.Animated.timing(animatedOpacityValue,
{ toValue: 1.1, duration: 250, easing: RX.Animated.Easing.Linear() }
)
]);
compositeAnimation.start();
```
## Directly Setting Animated Value
The value of an Animated Value can be set directly by calling the method ```setValue```. If this method is called while the value is being animated, the behavior is undefined. Setting the value of an Animated Value directly is faster than using a non-animated style attribute and re-rendering the component with a new attribute value.
``` javascript
let animatedOpacityValue = RX.Animated.createValue(1.0);
animatedOpacityValue.setValue(0.0);
```
## Web Limitations
ReactXP animation APIs on the web are implemented using DOM style transitions, as opposed to using JavaScript code to drive the animation. This results in much better performance and (in most cases) glitch-free animations, but it imposes some limitations on the use of the animation APIs.
* All active animated values associated with a particular element must share the same timing parameters (duration, easing function, delay, loop) and must be started at the same time.
* Each animated value can be associated with only one animated attribute that is actively running.
* Interpolated values used with startTransition are limited to only two values -- a begin and end value -- and must be specified in increasing order.
* Interpolated values not used with startTransition must have numeric outputValues, since we're interpolating between them ourselves.
* For interpolated values, the starting and ending values of a transition animation must correspond to the two interpolation keys.
* If an animation is stopped, the value will not reflect the intermediate position in the case of transforms and interpolated values.
================================================
FILE: docs/docs/apis/accessibility.md
================================================
---
id: apis/accessibility
title: Accessibility
layout: docs
category: Interfaces
permalink: docs/apis/accessibility.html
next: apis/alert
---
This interface provides methods and events related to accessibility.
Sometimes it's important to announce state changes in the app (for example, an incoming message) so visually-impaired users are aware that something changed on the screen. Multiple announcements can be queued.
## Events
``` javascript
// Triggered when the high-contrast setting changes.
highContrastChangedEvent: SubscribableEvent<(isEnabled: boolean) => void>;
// Triggered when the screen reader setting changes.
screenReaderChangedEvent: SubscribableEvent<(isEnabled: boolean) => void>;
```
## Methods
``` javascript
// Sends the specified string to the screen reader.
announceForAccessibility(announcement: string): void;
// Indicates whether a screen reader is currently enabled.
isScreenReaderEnabled(): boolean;
// Indicates whether the OS-level "high-contrast" setting is enabled.
isHighContrastEnabled(): boolean;
```
================================================
FILE: docs/docs/apis/alert.md
================================================
---
id: apis/alert
title: Alert
layout: docs
category: Interfaces
permalink: docs/apis/alert.html
next: apis/app
---
This interface displays an OS-specific alert over the top of the current screen. The appearance of the alert is dictated by the underlying OS platform. Some platforms allow alerts to be displayed even when the app is not in the foreground.
There is no ability to customize the alert by embedding ReactXP views within it or using ReactXP styles.
## Types
``` javascript
interface AlertButtonSpec {
// Button text
text?: string;
// Invoked when button is pressed
onPress?: () => void;
// Alert style to use (supported on some platforms)
style?: 'default' | 'cancel' | 'destructive';
}
interface AlertModalTheme {
// Modal background style
bodyStyle?: StyleRuleSet;
// Style for title text
titleTextStyle?: StyleRuleSet;
// Style for message text
messageTextStyle?: StyleRuleSet;
// Style for button control
buttonStyle?: StyleRuleSet;
// Style applied when hovering over button
buttonHoverStyle?: StyleRuleSet;
// Style for button text
buttonTextStyle?: StyleRuleSet;
// Override style for cancel button
cancelButtonStyle?: StyleRuleSet;
// Override style for cancel button hover state
cancelButtonHoverStyle?: StyleRuleSet;
// Override style for cancel button
cancelButtonTextStyle?: StyleRuleSet;;
}
interface AlertOptions {
// Optional icon (web only)
icon?: string;
// Optional theme (web only)
theme?: AlertModalTheme;
// (Android, iOS, and Windows only)
// Optional: the id of the root view this alert is associated with.
// Defaults to the view set by UserInterface.setMainView().
rootViewId?: string;
// Optional: Prevent the dialog from being dismissed when pressing away from the dialog
preventDismissOnPress?: boolean;
}
```
## Methods
``` javascript
// Displays an alert over the top of the current screen. Theming support is
// provided for web only and is ignored on other platforms.
show(title: string, message?: string, buttons? AlertButtonSpec[], options?: AlertOptions): void;
```
================================================
FILE: docs/docs/apis/app.md
================================================
---
id: apis/app
title: App
layout: docs
category: Interfaces
permalink: docs/apis/app.html
next: apis/clipboard
---
This interface provides core methods associated with the application. It also exposes events for low-memory conditions and activity state changes.
## Types
``` javascript
// Indicates whether the app is active or inactive
enum AppActivationState {
// App is running and in foreground
Active = 1,
// App is running and in background
Background = 2,
// App is inactive
// On RN mobile platforms, it is an intermediate state between when app transitions between foreground and background.
// On desktop platforms, this is currently not being used.
Inactive = 3,
// iOS specific activation state for extensions implemented
// with react-native
Extension = 4
}
```
## Methods
``` javascript
// Initializes the app. This should be one of the first calls made.
// Specifies whether app is running in "debug" mode, typically with
// asserts and unminified code. Also specifies whether in "development"
// mode, which can indicate that additional logging or fewer security
// checks are appropriate.
initialize(debug: boolean, development: boolean): void;
// Returns the current activitation state for the app
getActivationState(): AppActivationState;
```
## Events
``` javascript
// Triggered when the activation state changes
activationStateChangedEvent: SubscribableEvent<
(state: AppActivationState) => void>;
// Triggered when a low-memory warning occurs
memoryWarningEvent: SubscribableEvent<() => void>;
```
================================================
FILE: docs/docs/apis/clipboard.md
================================================
---
id: apis/clipboard
title: Clipboard
layout: docs
category: Interfaces
permalink: docs/apis/clipboard.html
next: apis/input
---
This interface provides access to the system's clipboard.
## Methods
``` javascript
// Retrieves the text from the clipboard (not supported on web)
getText(): SyncTasks.Promise;
// Places the specified text on the clipboard
setText(text: string): void;
```
================================================
FILE: docs/docs/apis/input.md
================================================
---
id: apis/input
title: Input
layout: docs
category: Interfaces
permalink: docs/apis/input.html
next: apis/international
---
This interface provides events that are triggered when specific user input events occur.
## Events
``` javascript
// Triggered when the hardware back button is pressed (Android only).
// Events are triggered in the reverse order in which they were registered.
// Pass true to stop propagation.
backButtonEvent: SubscribableEvent<() => boolean>();
// Triggered when a hardware key up/down event occurs. Events are triggered
// in the reverse order in which they were registered. Pass true to stop
// propagation.
keyDownEvent: SubscribableEvent<(e: Types.KeyboardEvent) => boolean>();
keyUpEvent: SubscribableEvent<(e: Types.KeyboardEvent) => boolean>();
```
================================================
FILE: docs/docs/apis/international.md
================================================
---
id: apis/international
title: International
layout: docs
category: Interfaces
permalink: docs/apis/international.html
next: apis/linking
---
This interface provides interfaces related to globalization (g11n) and internationalization (i18n).
Some languages read right to left. In such cases, it's preferable for the UI to be "mirrored". Buttons that are on the left side of the screen in left-to-right languages are flipped to the right side of the screen for right-to-left languages. This mirroring is mostly automatic, but some code changes may be required --- for example, to flip icons or images that depend on placement. For more details about React Native's support for right-to-left languages, see this helpful [blog article](https://facebook.github.io/react-native/blog/2016/08/19/right-to-left-support-for-react-native-apps.html).
## Methods
``` javascript
// By default, right-to-left mirroring is enabled based on the
// OS or browser settings. This method allows the app to disable
// right-to-left mirroring.
allowRTL(allow: boolean): void;
// This method overrides the right-to-left setting of the system
// or browser, forcing right-to-left mirroring behavior if true.
forceRTL(force: boolean): void;
// Indicates whether the app is currently running in right-to-left mode.
isRTL(): boolean;
```
================================================
FILE: docs/docs/apis/linking.md
================================================
---
id: apis/linking
title: Linking
layout: docs
category: Interfaces
permalink: docs/apis/linking.html
next: apis/location
---
This interface handles deep linking in both incoming and outgoing directions. Incoming deep links instruct the app to take actions requested by other apps. Outgoing deep links instruct other apps to perform actions.
## Types
``` javascript
// Information used to invoke the default SMS app
interface SmsInfo {
phoneNumber?: string;
body?: string;
}
// Information used to invoke the default email app
interface EmailInfo {
to?: string[];
cc?: string[];
bcc?: string[];
subject?: string;
body?: string;
}
// Error returned by promise if the request fails
interface LinkingErrorInfo {
code: LinkingError;
url: string;
error?: string;
}
enum LinkingErrorCode {
NoAppFound = 0,
UnexpectedFailure = 1,
Blocked = 2,
InitialUrlNotFound = 3
}
```
## Methods
``` javascript
// Returns the URL that was used to launch the application
getInitialUrl(): SyncTasks.Promise;
// Requests the URL to be opened by the default app for that protocol
// (e.g. http or https would typically open the system browser)
openUrl(url: string): SyncTasks.Promise;
// Requests the default SMS app to be invoked
launchSms(smsData: SmsInfo): SyncTasks.Promise;
// Requests the default mail app to be invoked
launchEmail(emailData: EmailInfo): SyncTasks.Promise;
```
## Events
``` javascript
// Triggered when a new deep link request arrives
deepLinkRequestEvent: SubscribableEvent<(url: string) => void>;
```
================================================
FILE: docs/docs/apis/location.md
================================================
---
id: apis/location
title: Location
layout: docs
category: Interfaces
permalink: docs/apis/location.html
next: apis/modal
---
This interface provides access to geolocation data.
## Types
``` javascript
enum LocationErrorType {
// User has not granted the app access to location data
PermissionDenied,
// Geolocation information is not currently available
PositionUnavailable,
// Geolocation request has timed out
Timeout
}
interface PositionOptions {
// Enable high-accuracy mode; consumes more battery
enableHighAccuracy?: boolean;
// Number of milliseconds before timeout
timeout?: number;
// Max age (in milliseconds) before cached location is invalidated
maximumAge?: number;
}
// ID for a pending "watch" request
type LocationWatchId = number;
// Delegates that are invoked when call succeeds or fails
type LocationSuccessCallback = (position: Position) => void;
type LocationFailureCallback = (error: LocationErrorType) => void;
```
## Methods
``` javascript
// Indicates whether geolocation services are available on this platform
isAvailable(): boolean;
// Returns the current location
getCurrentPosition(options?: PositionOptions): SyncTasks.Promise;
// Requests a callback when the position changes; useful for geofencing
watchPosition(successCallback: LocationSuccessCallback,
errorCallback?: LocationFailureCallback,
options?: PositionOptions): SyncTasks.Promise;
// Clears a location watch request
clearWatch(watchID: LocationWatchId): void;
```
================================================
FILE: docs/docs/apis/modal.md
================================================
---
id: apis/modal
title: Modal
layout: docs
category: Interfaces
permalink: docs/apis/modal.html
next: apis/network
---
This interface displays a view that overlays all other views rendered by the app, preventing any direct user interaction with the overlayed views.
A modal is identified by a caller-supplied "modal ID". These should be unique.
Only one modal can be displayed at a time. If a modal is already displayed, a newly-displayed modal is pushed onto the modal stack. When a modal is dismissed, it is popped off the stack, and the next modal becomes visible.
A modal can be displayed and dismissed using methods within the ReactXP.App namespace.
A modal covers the entire screen but is transparent. Its children define the visible contents and their position on the screen.
## Types
``` javascript
interface ModalOptions {
// Android, iOS, and Windows only.
// The id of the root view this modal is associated with.
// Defaults to the view set by UserInterface.setMainView();
rootViewId?: string;
}
```
## Methods
``` javascript
// Removes the modal from the modal stack, unmounting it if it's currently
// on the top of the stack and showing the next modal in the stack.
dismiss(modalId: string);
// Removes all modals from the modal stack.
dismissAll();
// Indicates whether the specified modal is in the modal stack. If no id provided indicates if some modal is displayed.
isDisplayed(modalId?: string): boolean;
// Pushes the modal onto the modal stack.
show(modal: React.ReactElement, modalId: string, options?: ModalOptions);
```
## Sample Usage
``` javascript
const _modalId = 'ErrorDialog';
showDialog() {
let dialog = (
'An error occurred'
'OK'
);
RX.Modal.show(dialog, _modalId);
}
private _onOkButtonPress = (e: RX.Types.SyntheticEvent) => {
RX.Modal.dismiss(_modalId);
};
```
================================================
FILE: docs/docs/apis/network.md
================================================
---
id: apis/network
title: Network
layout: docs
category: Interfaces
permalink: docs/apis/network.html
next: apis/platform
---
This has been deprecated from ReactXP Core and moved to an extension (reactxp-netinfo) inline with the React Native Lean Core initiative.
================================================
FILE: docs/docs/apis/platform.md
================================================
---
id: apis/platform
title: Platform
layout: docs
category: Interfaces
permalink: docs/apis/platform.html
next: apis/popup
---
This interface provides information about the OS or runtime platform on which the app is running.
## Types
``` javascript
type PlatformType = 'web' | 'ios' | 'android' | 'windows';
```
## Methods
``` javascript
// Returns the platform type
getType(): Types.PlatformType;
// Returns the value in `specifics` for the current platform type.
select(specifics: { [ platform in Types.PlatformType | 'default' ]?: T }): T | undefined;
```
================================================
FILE: docs/docs/apis/popup.md
================================================
---
id: apis/popup
title: Popup
layout: docs
category: Interfaces
permalink: docs/apis/popup.html
next: apis/statusbar
---
A popup is not technically a component. Rather, it's a collection of methods on the ReactXP.App namespace that allow the app to display a view that overlays a portion of the screen. Popups can be "anchored" to mounted components and follow them as they move around the screen (e.g. in reaction to scroll events).
When a popup is displayed, the caller specifies a PopupOptions structure that includes several callbacks, including a renderPopup method.
Popups by default will not act like a toggle. When Popup.show is called, it will always show the Popup. If a Popup is required to act like a toggle, PopupOptions.dismissIfShown should be set to true. In this case, if Popup.show is called once for a component, it will show the popup. A subsequent call from the same component will dismiss the popup and so on.
The overall dimensions of a popup are assumed to remain constant for the lifetime of the popup. This allows the dimensions to be measured once, and the popup can then be positioned relative to the anchor.
Popups are identified by a caller-specified ID that should be unique.
## Types
``` javascript
// 'context' mode makes it attempt to behave like a context menu -- defaulting
// to the lower right of the anchor element and working its way around. It is not supported
// with inner positioning and will throw an exception if used there.
type PopupPosition = 'top' | 'right' | 'bottom' | 'left' | 'context';
interface PopupOptions {
// Returns a mounted component instance that serves as the
// "anchor" for the popup. Often a button.
getAnchor: () => React.Component;
// Renders the contents of the popup. This is called twice. The
// first time it is called, the parameters are all defaults
// (default position and 0 offset and dimensions). This allows
// the popup to be measured and positioned. It is called a second
// time with the position, offset and dimensions specified. This
// allows the method to modify the appearance based on these
// parameters. The dimensions should not be modified, however.
renderPopup: (anchorPosition: PopupPosition, anchorOffset: number,
popupWidth: number, popupHeight: number) => ReactNode;
// Returns a mounted component instance that controls the triggering
// of the popup. In the majority of cases, "anchor" of popup has
// handlers to control when the popup will be seen and this function
// is not required. In a few cases, where anchor is not the same as
// the whole component that triggers when the popup wil be seen,
// this can be used. For instance, a button combined with a chevron
// icon, which on click triggers a popup below the chevron icon. In
// this example, getElementTriggeringPopup() can return the container
// with button and chevron icon.
getElementTriggeringPopup?: () => React.Component;
// Called when the popup is dismissed. Popup.isDisplayed() will return
// false for the popup being dismissed when this callback runs.
onDismiss?: () => void;
// Prioritized order of positions. Popup is positioned
// relative to the anchor such that it remains on screen.
// Default is ['bottom', 'right', 'top', 'left'].
positionPriorities?: string[];
// Position the popup inside its anchor.
// In this mode only the first position priority will be used.
useInnerPositioning?: boolean;
// On pressed handler to notify whoever wanted to create the popup
// that its anchor has been pressed.
// IMPORTANT NOTE: This handler may be called when the component is
// already unmounted as it uses a time delay accommodate
// fade-out animations.
onAnchorPressed?: (e: SyntheticEvent) => void;
// Determines if the anchor invoking the popup should behave like a toggle.
// If true, calling Popup.show will show the popup. A subsequent call
// will hide the popup. If false or undefined (default), calling Popup.show
// will always show the popup.
dismissIfShown?: boolean;
// By default, clicks or taps outside of a popup (unless they are on the
// anchor) will not dismiss the active popup. If true, this overrides the
// default behavior, in which case the popup must be dismissed explicitly.
preventDismissOnPress?: boolean;
// The popup may be left in the DOM after it's dismissed. This is a
// performance optimization to make the popup appear faster when it's shown
// again, intended for popups that tend to be shown repeatedly. Note that
// this is only a hint, so callers shouldn't rely on caching behavior.
cacheable?: boolean;
// Android, iOS, and Windows only.
// The id of the root view this popup is associated with.
// Defaults to the view set by UserInterface.setMainView();
rootViewId?: string;
}
```
## Methods
``` javascript
// Dismisses an already-displayed popup after a specified number
// of milliseconds
autoDismiss(popupId: string, dismissDelay: number = 0): void;
// Dismisses an already-displayed popup immediately
dismiss(popupId: string): void;
// Dismisses all popups immediately
dismissAll(): void;
// Displays a popup. Returns true if successful, false if the popup is
// already displayed
show(options: PopupOptions, popupId: string, showDelay: number = 0): boolean;
// Indicates whether the specified popup is displayed. If no id provided indicates if some popup is displayed.
isDisplayed(popupId?: string): boolean;
```
## Sample Usage
``` javascript
const _popupId = 'myPopup';
let _popupDisplayed = false;
onHoverStart() {
if (!this._popupDisplayed) {
this.displayPopup();
}
};
onHoverEnd() {
RX.Popup.autoDismiss(_popupId, 2000);
};
displayPopup() {
let popupOptions: RX.Types.PopupOptions = {
getAnchor: () => {
return this._mountedButton;
},
renderPopup: (anchorPosition: Types.PopupPosition, anchorOffset: number,
popupWidth: number, popupHeight: number) => {
return this._renderPopupView(anchorPosition,
anchorOffset, popupWidth, popupHeight);
},
positionPriorities: ['right', 'left', 'bottom', 'top'],
onDismiss: () => {
this._popupDisplayed = false;
}
};
RX.Popup.show(popupOptions, _popupId, 500);
this._popupDisplayed = true;
}
```
================================================
FILE: docs/docs/apis/statusbar.md
================================================
---
id: apis/statusbar
title: StatusBar
layout: docs
category: Interfaces
permalink: docs/apis/statusbar.html
next: apis/storage
---
This interface provides control over the system status bar at the top of the screen on mobile platforms.
## Methods
``` javascript
// Indicates whether the status bar overlays the app's main view (e.g. on iOS)
isOverlay(): boolean;
// Hides or shows the status bar with an optional animation
setHidden(hidden: boolean, showHideTransition: 'fade' | 'slide'): void;
// Specifies the status bar visual style
setBarStyle(style: 'default' | 'light-content' | 'dark-content',
animated: boolean): void;
// Specifies whether the network activity indicator is visible.
setNetworkActivityIndicatorVisible(value: boolean): void;
// Specifies the background color of the status bar (applies on Android only)
setBackgroundColor(color: string, animated: boolean): void;
// Specifies whether the status bar is translucent or transparent
setTranslucent(translucent: boolean): void;
```
================================================
FILE: docs/docs/apis/storage.md
================================================
---
id: apis/storage
title: Storage
layout: docs
category: Interfaces
permalink: docs/apis/storage.html
next: apis/userinterface
---
This interface provides a simple key-based local storage mechanism. If you need more powerful options to persist data and work with them, consider using ReactXP's [Database Extension](/reactxp/docs/extensions/database).
## Methods
``` javascript
// Clears all local storage keys
clear(): SyncTasks.Promise;
// Returns an item by key
getItem(key: string): SyncTasks.Promise;
// Deletes an item by key
removeItem(key: string): SyncTasks.Promise;
// Sets or replaces the value of an item by key
setItem(key: string, value: string): SyncTasks.Promise;
```
================================================
FILE: docs/docs/apis/userinterface.md
================================================
---
id: apis/userinterface
title: UserInterface
layout: docs
category: Interfaces
permalink: docs/apis/userinterface.html
next: apis/userpresence
---
This interface provides a variety of UI-related methods.
## Types
``` javascript
interface LayoutInfo {
x: number;
y: number;
width: number;
height: number;
}
```
## Methods
``` javascript
// Specifies the "main view" of the app, which is rendered on the full
// screen beneath any open modals or popups
setMainView(element: React.ReactElement): void;
// Android & iOS only.
// Wrapper around RN.AppRegistry.registerComponent();
// IMPORTANT: Some APIs, e.g. Popup & Modal, require a string
// `reactxp_rootViewId` prop to be set on the component from the
// native-side.
registerRootView(viewKey: string, getComponentFunc: Function);
// Specifies whether custom scrollbars should be enabled (applies
// to web only)
useCustomScrollbars(enable: boolean): void;
// Indicates whether the screen is "high density" (e.g. retina displays)
isHighPixelDensityScreen(): boolean;
// Measure the location and dimensions of a mounted component relative
// to the window or one of its other containing views
measureLayoutRelativeToWindow(component: React.Component):
SyncTasks.Promise;
measureLayoutRelativeToAncestor(component: React.Component,
ancestor: React.Component): SyncTasks.Promise;
// Measures the dimension of window (based on specified root view id or
// defaults to main window or screen, in the case
// of non-windowed platforms); the dimensions can also be obtained for any
// view (including your app's top-level view) using the onLayout
// callback
measureWindow(rootViewId?: string): Types.Dimensions;
// Indicates the "size multiplier" for text increase or decrease, which
// can be adjusted by users on some platforms; defaults to 1.0
getContentSizeMultiplier(): SyncTasks.Promise;
// Dismisses the on-screen keyboard (on applicable platforms)
dismissKeyboard(): void;
// Enables native -> script touch event latency diagnostic events.
// When latency greater than latencyThresholdMs is observed, the
// touchLatencyEvent will fire (on applicable platforms).
enableTouchLatencyEvents(latencyThresholdMs: number): void;
// Returns true if the application is in the keyboard navigation state,
// when the user is using Tab key to navigate through the focusable
// elements (on applicable platforms).
isNavigatingWithKeyboard(): boolean;
```
## Events
``` javascript
// Triggered when the content size multiplier changes while the
// app is running
contentSizeMultiplierChangedEvent: SubscribableEvent<
(multiplier: number) => void>();
// Triggered when enableTouchLatencyEvents has been called and
// native -> script touch latency exceeding the threshold has
// been observed (on applicable platforms).
touchLatencyEvent: SubscribableEvent<
(observedLatencyMs: number) => void>();
// Triggered when the keyboard navigation state is changed
// (on applicable platforms).
keyboardNavigationEvent: SubscribableEvent<
(isNavigatingWithKeyboard: boolean) => void>();
```
================================================
FILE: docs/docs/apis/userpresence.md
================================================
---
id: apis/userpresence
title: UserPresence
layout: docs
category: Interfaces
permalink: docs/apis/userpresence.html
next: extensions/database
---
This interface provides information about whether the user is currently present. The technique for detecting presence differs by platform. On mobile platforms, the user is assumed to be present as long as the app is in the foreground. On web platforms, the user is assumed to be present if the window is in the foreground, the tab is foremost, and some mouse or keyboard activity has been seen within the window within the past minute.
# Methods
``` javascript
// Indicates whether the user is currently present
// On web platforms, it indicates whether the user has focused on the app and interacted with the app in the last 60 seconds
isUserPresent(): boolean;
```
## Events
``` javascript
// Triggered when the user presence changes
userPresenceChangedEvent: SubscribableEvent<
(isPresent: boolean) => void>();
```
================================================
FILE: docs/docs/components/activityindicator.md
================================================
---
id: components/activityindicator
title: ActivityIndicator
layout: docs
category: Components
permalink: docs/components/activityindicator.html
next: components/button
---
This component displays an animated "spinner" control that tells the user that an operation is pending. Animation continues as long as the component is displayed.
## Props
``` javascript
// Color of indicator
color: color;
// Number of ms to wait before displaying
deferTime: number = 0;
// Size of indicator (exact sizes are platform-specific)
size: 'large' | 'medium' | 'small' | 'tiny';
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
```
## Styles
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
No methods
================================================
FILE: docs/docs/components/button.md
================================================
---
id: components/button
title: Button
layout: docs
category: Components
permalink: docs/components/button.html
next: components/gestureview
---
Like View, this component is a generic container for other components. However, it adds some additional capabilities -- support for presses or clicks and hovering.
## Props
In addition to the [common accessibility props](/reactxp/docs/accessibility.html), the following props are supported.
``` javascript
// Text to be used by screen readers
accessibilityLabel: boolean = false;
// Traits used to hint screen readers, etc.
accessibilityTraits: AccessibilityTrait | AccessibilityTrait[] = undefined;
// It is hard or impossible to tell by a reference to an instance of a component
// from where this component has been instantiated. You can assign this property
// and check instance.props.accessibilityId. For example accessibilityId is used
// in View's FocusArbitrator callback.
accessibilityId: string = undefined;
// Opacity value the button should animate to on button touch
activeOpacity: number = undefined; // iOS and Android only
// Id of an expandable element revealed by the button. Describes a relation
// between button and element to screen reader.
ariaControls: string = undefined; // Web only
// Specifies a unique id for an HTML element
// NOTE: This property may be going away in future versions.
id: string = undefined; // Web only
// Expose the element and/or its children as accessible to Screen readers
importantForAccessibility?: ImportantForAccessibility = ImportantForAccessibility.Yes;
// Delay in ms before onLongPress is called
delayLongPress: number = 1000;
// If disabled, touch and mouse input events are ignored
disabled: boolean = false;
// By default, opacity of a disabled element is 0.5. This value can be
// overriden with this property
disabledOpacity: number = undefined;
// Disable default opacity animation on touch of buttons
disableTouchOpacityAnimation: boolean = false; // iOS and Android only
// Should be focused when the component is mounted, see also View's arbitrateFocus
// property.
// WARNING: autoFocus=true means that this Button's requestFocus() method will be
// called, however calling requestFocus() might have no effect (for example the
// button is disabled), the application has to handle this either while setting
// this property or in the View's FocusArbitrator callback.
autoFocus: boolean = false;
// Called when VoiceOver is on and the user double tapped to
// activate a control
onAccessibilityTapIOS: (e: SyntheticEvent) => void; // iOS Only
// Focus Events
onFocus: (e: FocusEvent) => void = undefined;
onBlur: (e: FocusEvent) => void = undefined;
// Called when the mouse cursor enters or leaves the view bounds
onHoverStart: (e: SyntheticEvent) => void;
onHoverEnd: (e: SyntheticEvent) => void;
// Keyboard Events
onKeyPress: (e: KeyboardEvent) => void = undefined;
// Called when the user has pressed and held for a specified duration
onLongPress: (e: SyntheticEvent) => void;
// Called when the touch or mouse button is released within the
// bounds of the view and the press has not been canceled
onPress: (e: SyntheticEvent) => void;
// Called when touch is initiated or mouse button is pressed
onPressIn: (e: SyntheticEvent) => void;
// Called when touch or the mouse button is released or the
// user's finger or mouse cursor is no longer over the view
onPressOut: (e: SyntheticEvent) => void;
// Rasterize contents using offscreen bitmap (perf optimization)
shouldRasterizeIOS: boolean = false; // iOS only
// See below for supported styles
style: ButtonStyleRuleSet | ButtonStyleRuleSet[] = [];
// Keyboard tab order
tabIndex: number = undefined;
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
// Text for a tooltip
title: string = undefined;
// Background color that will be visible on button touch
underlayColor: string = undefined; // iOS and Android only
```
## Styles
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
``` javascript
// Sets the accessibility focus to the component.
focus(): void;
// The preferable way to focus the component. When requestFocus() is called,
// the actual focus() will be deferred, and if requestFocus() has been
// called for several components, only one of those components will actually
// get a focus() call. By default, last component for which requestFocus() is
// called will get a focus() call, but you can specify arbitrateFocus property
// of a parent View and provide the callback to decide which one of that View's
// descendants should be focused. This is useful for the accessibility: when
// consecutive focus() calls happen one after another, the next one interrupts
// the screen reader announcement for the previous one and the user gets
// confused. autoFocus property of focusable components also uses requestFocus().
requestFocus(): void;
// Blurs the component.
blur(): void;
```
================================================
FILE: docs/docs/components/gestureview.md
================================================
---
id: components/gestureview
title: GestureView
layout: docs
category: Components
permalink: docs/components/gestureview.html
next: components/image
---
This component provides support for common touch gestures -- tapping, double-tapping, long-pressing, panning, and pinching. It also handles common mouse-based gestures including double clicking and scroll wheel input.
Information about pending gestures is returned through event handlers. A caller can specify which gestures they are interested in by specifying those event handlers. For example, if you are interested in double taps and horizontal pans, provide an onDoubleTap and onPanHorizontal handler.
## Props
``` javascript
// Alternate text for screen readers.
accessibilityLabel: string = undefined;
// Traits used to hint screen readers, etc.
accessibilityTraits: AccessibilityTrait | AccessibilityTrait[] = undefined;
// Expose the element and/or its children as accessible to Screen readers
importantForAccessibility?: ImportantForAccessibility =
ImportantForAccessibility.Yes;
// Gestures and attributes that apply only to touch inputs
onPinchZoom: (gestureState: MultiTouchGestureState) => void = undefined;
onRotate: (gestureState: MultiTouchGestureState) => void = undefined;
// Gestures and attributes that apply only to mouse inputs
onScrollWheel: (gestureState: ScrollWheelGestureState) => void = undefined;
mouseOverCursor: GestureMouseCursor = undefined;
// Gestures and attributes that apply to either touch or mouse inputs
onPan: (gestureState: PanGestureState) => void = undefined;
onPanVertical: (gestureState: PanGestureState) => void = undefined;
onPanHorizontal: (gestureState: PanGestureState) => void = undefined;
onTap: (gestureState: TapGestureState) => void = undefined;
onDoubleTap: (gestureState: TapGestureState) => void = undefined;
onContextMenu: (gestureState: TapGestureState) => void = undefined;
onLongPress: (gestureState: TapGestureState) => void = undefined;
// Focus Events
onFocus: (e: FocusEvent) => void = undefined;
onBlur: (e: FocusEvent) => void = undefined;
// Keyboard Events
onKeyPress: (e: KeyboardEvent) => void = undefined;
// We can set vertical or horizontal as preferred
preferredPan: PreferredPanGesture = undefined; // Horizontal or vertical
// How many pixels (in either horizontal or vertical direction) until
// pan is recognized? Default is 10. Can be any value > 0.
panPixelThreshold: number = undefined;
// Something else wants to become responder. Should this view
// release the responder? Setting true allows release.
releaseOnRequest: boolean = false;
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
```
## Styles
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
``` javascript
// Sets the accessibility focus to the component.
focus(): void;
```
================================================
FILE: docs/docs/components/image.md
================================================
---
id: components/image
title: Image
layout: docs
category: Components
permalink: docs/components/image.html
next: components/link
---
This component displays an image, which can come from a local source or from the network. It supports JPEG, GIF and PNG formats.
If child elements are specified, the image acts as a background, and the children are rendered on top of it.
If headers contains 'Cache-Control: max-stale' with no value specified and the image fails to load, the component tries again passing cache: 'only-if-cached' to the underlying native Image (iOS only). This way the app can render otherwise inaccessible stale cached images.
## Props
``` javascript
// Alternate text to display if the image cannot be loaded
// or by screen readers
accessibilityLabel: string = undefined;
// HTTP headers to include when fetching the URL.
headers: { [headerName: string]: string } = undefined;
// Called when an error occurs that prevents the image from loading
onError: (err?: Error) => void;
// Called when the image successfully loads
onLoad: (size: Dimensions) => void;
// Android-specific resize property
resizeMethod: 'auto' | 'resize' | 'scale' = 'auto'; // Android only
// Determines how to resize the image if its natural size
// does not match the size of the container
// Note: In Web version, 'auto' doesn't scale down image
// if width/height smaller than the original image size
resizeMode: 'stretch' | 'contain' | 'cover' | 'auto' | 'repeat' = 'contain';
// URL to image
source: string = undefined;
// See below for supported styles
style: ImageStyleRuleSet | ImageStyleRuleSet[] = [];
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
// Tooltip for image
title: string = undefined;
```
## Styles
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
``` javascript
// Returns the native width and height of the image. Can be called only after
// the onLoad has been called and only while the component is mounted.
getNativeHeight(): number;
getNativeWidth(): number;
```
## Static Methods
```javascript
// Prefetches a remote image and stores it in a cache. This can decrease the
// amount of time it takes when you later want to show the image because the
// image only has to be fetched from the local cache rather than over the
// network.
prefetch(url: string): Promise;
// Similarly to [prefetch], this method loads a remote image and stores it in
// a cache. If prefetching was successful it will also get image dimensions. It will be
// useful if you need [getNativeHeight] or [getNativeWidth] after image
// was loaded because you will get this info together with prefetching and before
// you actually need to show the image.
getMetadata(url: string): Promise
```
================================================
FILE: docs/docs/components/link.md
================================================
---
id: components/link
title: Link
layout: docs
category: Components
permalink: docs/components/link.html
next: components/picker
---
This component displays a hyperlink. On the web, it translates to an <a> tag.
## Props
``` javascript
// It is hard or impossible to tell by a reference to an instance of a component
// from where this component has been instantiated. You can assign this property
// and check instance.props.accessibilityId. For example accessibilityId is used
// in View's FocusArbitrator callback.
accessibilityId: string = undefined;
// Should fonts be scaled according to system setting?
allowFontScaling: boolean = true; // Android and iOS only
// Should be focused when the component is mounted, see also View's arbitrateFocus
// property.
autoFocus: boolean = false;
// For non-zero values, truncates with ellipsis if necessary
numberOfLines: number = 0;
// Called when the mouse cursor enters or leaves the view bounds
onHoverStart: (e: SyntheticEvent) => void = undefined;
onHoverEnd: (e: SyntheticEvent) => void = undefined;
// Event called when the touch or mouse button is released
// within the bounds of the view and the press has not been canceled
onPress: (e: SyntheticEvent, url: string) => void = undefined;
// Event called when a long touch or mouse (> 1000ms) button is released
// within the bounds of the view and the press has not been canceled
onLongPress: (e: SyntheticEvent, url:string) => void = undefined;
// Event called when context menu is triggered, either by
// right mouse button click or context menu key
onContextMenu: (e: MouseEvent) => void = undefined;
// Can the link be included in a text selection?
selectable: boolean = false;
// See below for supported styles
style: LinkStyleRuleSet | LinkStyleRuleSet[] = [];
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
// Text for a tooltip
title: string = undefined;
// URL to follow for hyperlink
url: string;
```
## Styles
[**Text Styles**](/reactxp/docs/styles.html#text-style-attributes)
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
``` javascript
// Sets the focus to the component.
focus(): void;
// The preferable way to focus the component. When requestFocus() is called,
// the actual focus() will be deferred, and if requestFocus() has been
// called for several components, only one of those components will actually
// get a focus() call. By default, last component for which requestFocus() is
// called will get a focus() call, but you can specify arbitrateFocus property
// of a parent View and provide the callback to decide which one of that View's
// descendants should be focused. This is useful for the accessibility: when
// consecutive focus() calls happen one after another, the next one interrupts
// the screen reader announcement for the previous one and the user gets
// confused. autoFocus property of focusable components also uses requestFocus().
requestFocus(): void;
// Blurs the component.
blur(): void;
```
================================================
FILE: docs/docs/components/picker.md
================================================
---
id: components/picker
title: Picker
layout: docs
category: Components
permalink: docs/components/picker.html
next: components/scrollview
---
This component displays a control that allows the user to pick from a list of items.
## Types
``` javascript
interface PickerPropsItem {
label: string;
value: string;
}
```
## Props
``` javascript
// List of items to be displayed in the picker
items: PickerPropsItem[] = [];
// 'dialog': Show a modal dialog
// 'dropdown': Shows a dropdown anchored to the picker view
mode: 'dialog' | 'dropdown' = 'dialog'; // Android only
// Invoked when the selected value changes
onValueChange: (itemValue: string, itemPosition: number) => void;
// Initially-selected item
selectedValue: string;
// See below for supported styles
style: PickerStyleRuleSet | PickerStyleRuleSet[] = [];
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
```
## Styles
``` javascript
**Text Color**
color: 'string';
```
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
No methods
## Sample Usage
``` javascript
const pickerItems: RX.Types.PickerPropsItem[] = [
{
label: 'Cool',
value: 'cool'
},
{
label: 'Super',
value: 'super'
},
{
label: 'Great',
value: 'great'
}
];
class MyComponent extends RX.Component {
constructor() {
super();
this.state = {
selectedValue: 'cool'
}
}
render(): JSX.Element {
return (
{ 'How are you feeling?' }
);
}
private _onValueChange = (itemValue: string, itemIndex: number) => {
this.setState({ selectedValue: itemValue });
}
}
```
================================================
FILE: docs/docs/components/scrollview.md
================================================
---
id: components/scrollview
title: ScrollView
layout: docs
category: Components
permalink: docs/components/scrollview.html
next: components/text
---
Like a View, this component is a container for other components. However, it supports scrolling (panning) and zooming so it is possible to view larger contents.
ScrollViews must have a bounded height (or width, if it scrolls horizontally) since its children are of unbounded height (or width). To bound the dimensions of a ScrollView, either set the height/width directly or make sure that its parent's height/width is bounded.
## Props
``` javascript
// Should scroll bar bounce when user hits the bounds?
bounces: boolean = true; // iOS only
// Controls the scroll direction. When false or undefined, only vertical scroll is enabled, when true, only horizontal scroll is enabled
horizontal: boolean = false;
// When the user scrolls the view, how should the on-screen keyboard react?
keyboardDismissMode: 'none' | 'interactive' | 'on-drag'; // Native only
// Should the on-screen keyboard remain visible when the user taps
// the scroll view?
keyboardShouldPersistTaps: boolean | 'always' | 'never' | 'handled' = 'never'; // Native only
// Invoked when the contents of the scroll view change
onContentSizeChange: (width: number, height: number) => void = undefined;
// Focus Events
onFocus: (e: FocusEvent) => void = undefined;
onBlur: (e: FocusEvent) => void = undefined;
// Keyboard Events
onKeyPress: (e: KeyboardEvent) => void = undefined;
// Invoked when view dimensions or position changes
onLayout: (e: ViewOnLayoutEvent) => void = undefined;
// Called when the scroll position changes
onScroll: (newScrollTop: number, newScrollLeft: number) => void = undefined;
// Called when the user starts or stops scrolling (touch-based systems only)
onScrollBeginDrag: () => void = undefined;
onScrollEndDrag: () => void = undefined;
// Animation helpers to allow usage of the RN.Animated.Event system through ReactXP.
// AnimatedValue objects hooked up to either (or both) of these properties will be automatically
// hooked into the onScroll handler of the scrollview and .setValue() will be called on them
// with the updated values. On supported platforms, it will use RN.Animated.event() to do
// a native-side/-backed coupled animation.
scrollXAnimatedValue?: RX.Types.AnimatedValue;
scrollYAnimatedValue?: RX.Types.AnimatedValue;
// Android only property to control overScroll mode
overScrollMode?: 'auto' | 'always' | 'never';
// Snap to page boundaries?
pagingEnabled: boolean = false; // Android & iOS only
snapToInterval: number = undefined; // iOS only
// Is scrolling enabled?
scrollEnabled: boolean = true;
// Minimum duration (in milliseconds) between scroll events
scrollEventThrottle: number = undefined;
// Inset (in pixels) of scroll indicator from top/bottom (vertical)
// or left/right (horizontal)
scrollIndicatorInsets: ScrollIndicatorInsets = undefined;
// If true, this scroll bar scrolls to the top when the user
// taps on the status bar.
scrollsToTop: boolean = false; // iOS only
// Should the indicator be displayed?
showsHorizontalScrollIndicator: boolean = [same as horizontal];
showsVerticalScrollIndicator: boolean = [same as horizontal];
// See below for supported styles
style: ViewStyleRuleSet | ViewStyleRuleSet[] = [];
// Windows-only property to control tab navigation inside the view
tabNavigation?: 'local' | 'cycle' | 'once';
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
```
## Styles
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
``` javascript
// Sets the accessibility focus to the component.
focus(): void;
// Sets the absolute top or left position (measured in pixels) of the
// viewport within the scroll view. Optionally animates from the current
// position.
setScrollLeft(scrollLeft: number, animate: boolean): void;
setScrollTop(scrollTop: number, animate: boolean): void;
```
================================================
FILE: docs/docs/components/text.md
================================================
---
id: components/text
title: Text
layout: docs
category: Components
permalink: docs/components/text.html
next: components/textinput
---
This component displays basic text. Its children must be a string literal or a series of children that are either Text components or View components with a fixed height and width.
Unlike other ReactXP components, some of the style attributes for an Text cascade to its children. In the following example, the title and body both inherit the styles from their parent RX.Text component, but they can also override specific style elements.
Another difference between Text and other components is that Text children are not layed out according to flexbox layout rules. Instead, an inline text layout is used.
## Props
``` javascript
// Should fonts be scaled according to system setting?
allowFontScaling: boolean = true; // Android and iOS only
// When numberOfLines is set, this prop defines how text will be truncated.
// head: The line is displayed so that the end fits in the container and
// the missing text at the beginning of the line is indicated by an
// ellipsis glyph. e.g., "...wxyz"
// middle: The line is displayed so that the beginning and end fit in
// the container and the missing text in the middle is indicated by an
// ellipsis glyph. "ab...yz"
// tail: The line is displayed so that the beginning fits in the container
// and the missing text at the end of the line is indicated by an ellipsis
// glyph. e.g., "abcd..."
ellipsizeMode: 'head' | 'middle' | 'tail'; // Android & iOS only
// Specifies a unique id for an HTML element.
// NOTE: This property may be going away in future versions.
id: string = undefined; // Web only
// Expose the element and/or its children as accessible to Screen readers
importantForAccessibility: ImportantForAccessibility =
ImportantForAccessibility.Yes;
// It is hard or impossible to tell by a reference to an instance of a component
// from where this component has been instantiated. You can assign this property
// and check instance.props.accessibilityId. For example accessibilityId is used
// in View's FocusArbitrator callback.
accessibilityId: string = undefined;
// Should be focused when the component is mounted, see also View's arbitrateFocus
// property.
// WARNING: autoFocus=true means that this Text's requestFocus() method will be
// called, however calling requestFocus() for Text might make sense only on mobile
// for the accessibility reasons, on web it has no effect, the application has to
// handle this either while setting this property or in the View's FocusArbitrator
// callback.
autoFocus: boolean = false;
// For non-zero values, truncates with ellipsis if necessary. Web platform
// doesn't support values greater than 1. Web platform may also not truncate
// properly if text contains line breaks, so it may be necessary to replace
// line breaks before rendering.
numberOfLines: number = 0;
// Mouse & Touch Events
onPress?: (e: SyntheticEvent) => void = undefined;
onContextMenu?: (e: SyntheticEvent) => void = undefined;
// Is the text selectable (affects mouse pointer and copy command)
selectable: boolean = false;
// See below for supported styles
style: TextStyleRuleSet | TextStyleRuleSet[] = [];
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
```
## Styles
[**Text Styles**](/reactxp/docs/styles.html#text-style-attributes)
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
``` javascript
// Sets the accessibility focus to the component.
focus(): void;
// The preferable way to focus the component. When requestFocus() is called,
// the actual focus() will be deferred, and if requestFocus() has been
// called for several components, only one of those components will actually
// get a focus() call. By default, last component for which requestFocus() is
// called will get a focus() call, but you can specify arbitrateFocus property
// of a parent View and provide the callback to decide which one of that View's
// descendants should be focused. This is useful for the accessibility: when
// consecutive focus() calls happen one after another, the next one interrupts
// the screen reader announcement for the previous one and the user gets
// confused. autoFocus property of focusable components also uses requestFocus().
requestFocus(): void;
// Blurs the component.
blur(): void;
// When selection is enabled, retrieves the selected text.
getSelectedText(): string; // Windows only
```
## Sample Usage
``` javascript
{ this.props.title }
{ this.props.body }
```
``` javascript
static _styles = {
redBox: RX.Styles.createViewStyle({
width: 10,
height: 10,
backgroundColor: 'red'
})
}
{ 'Red box ' }
{ ' inlined view example' }
```
================================================
FILE: docs/docs/components/textinput.md
================================================
---
id: components/textinput
title: TextInput
layout: docs
category: Components
permalink: docs/components/textinput.html
next: components/view
---
This component provides basic text input capabilities.
It can be used in one of two modes. In the first mode, the contents of the text input are static and are specified by the `value` prop. Any attempt to modify the value will result in `onChangeText`, which allows the owning component to re-render with an updated `value`. In the second mode, `value` is unspecified, and the text input value is allowed to be modified as long as it remains mounted. In this mode, the caller can specify an optional `defaultValue` prop to specify the initial value.
## Props
In addition to the [common accessibility props](/reactxp/docs/accessibility.html), the following props are supported.
``` javascript
// Text to be used by screen readers
accessibilityLabel: boolean = false;
// It is hard or impossible to tell by a reference to an instance of a component
// from where this component has been instantiated. You can assign this property
// and check instance.props.accessibilityId. For example accessibilityId is used
// in View's FocusArbitrator callback.
accessibilityId: string = undefined;
// Should fonts be scaled according to system setting?
allowFontScaling: boolean = true; // Android and iOS only
// Auto-capitalization mode
autoCapitalize: 'none' | 'sentences' | 'words' | 'characters';
// Should auto-correction be applied to contents?
autoCorrect: boolean = true;
// Should be focused when the component is mounted, see also View's arbitrateFocus
// property.
// WARNING: autoFocus=true means that this TextInput's requestFocus() method will be
// called, however calling requestFocus() might have no effect (for example the
// input is disabled), the application has to handle this either while setting this
// property or in the View's FocusArbitrator callback.
autoFocus: boolean = false;
// Should focus be lost after submitting?
blurOnSubmit: boolean = false;
// iOS and Windows only property for controlling when the clear button
// should appear on the right side of the text view. Default behavior
// dependends on platform: equivalent to 'never' on iOS, and 'always'
// on Windows.
clearButtonMode: 'never' | 'while-editing' | 'unless-editing' | 'always';
// Initial value that will change when the user starts typing
defaultValue: string = undefined;
// Disable full screen editor mode?
disableFullscreenUI: boolean = false; // Android-specific
// Can text be edited by the user?
editable: boolean = true;
// iOS-only prop for controlling the keyboard appearance
keyboardAppearance: 'default' | 'light' | 'dark';
// On-screen keyboard type to display
keyboardType: 'default' | 'numeric' | 'email-address' | 'number-pad';
// Maximum character count
maxLength: number = undefined;
// Should the control support multiple lines of text?
multiline: boolean = false;
// Called when the control loses focus
onBlur: () => void = undefined;
// Called when the text value changes
onChangeText: (newValue: string) => void = undefined;
// Called when the control obtains focus
onFocus: () => void = undefined;
// Called on a key event
onKeyPress: (e: KeyboardEvent) => void = undefined;
// Called when text is pasted into the control (not currently
// supported on iOS or Android)
onPaste: (e: ClipboardEvent) => void = undefined;
// Called when the selection scrolls due to overflow
onScroll: (newScrollLeft: number, newScrollTop: number) => void = undefined;
// Called when the selection range or insertion point location changes
onSelectionChange: (start: number, end: number) => void = undefined;
// Called when the text input submit button is pressed; invalid if
// multiline is true
onSubmitEditing: () => void = undefined;
// Placeholder text to dislpay when input is empty
placeholder: string = undefined;
// Color of placeholder text
placeholderTextColor: color = '#ccc';
// iOS and android prop for controlling return key type
returnKeyType: 'done' | 'go' | 'next' | 'search' | 'send';
// Obscure the text input (for passwords)?
secureTextEntry: boolean = false;
// Should spell checking be applied to contents?
spellCheck: boolean = [value of autoCorrect];
// See below for supported styles
style: TextInputStyleRuleSet | TextInputStyleRuleSet[] = [];
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
// Text for a tooltip
title: string = undefined;
// If defined, the control value is forced to match this value;
// if undefined, control value can be modified by the user
value: string = undefined;
```
## Styles
[**Text Styles**](/reactxp/docs/styles.html#text-style-attributes)
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
``` javascript
// Forces the control to give up focus
blur(): void;
// Gives the control focus. For mobile, use setAccessibilityFocus()
// for setting screen reader focus
focus(): void;
// The preferable way to focus the component. When requestFocus() is called,
// the actual focus() will be deferred, and if requestFocus() has been
// called for several components, only one of those components will actually
// get a focus() call. By default, last component for which requestFocus() is
// called will get a focus() call, but you can specify arbitrateFocus property
// of a parent View and provide the callback to decide which one of that View's
// descendants should be focused. This is useful for the accessibility: when
// consecutive focus() calls happen one after another, the next one interrupts
// the screen reader announcement for the previous one and the user gets
// confused. autoFocus property of focusable components also uses requestFocus().
requestFocus(): void;
// Gives the control accessibility-only focus
// E.g. screen reader focus is needed, but popping up of native
// keyboard is undesirable
setAccessibilityFocus(): void;
// Does control currently have focus?
isFocused(): boolean;
// Extends selection to include all contents
selectAll(): void;
// Selects a range of text
selectRange(start: number, end: number): void;
// Returns the current selection range
getSelectionRange(): { start: number, end: number };
// Sets the current value
setValue(value: string): void;
```
================================================
FILE: docs/docs/components/view.md
================================================
---
id: components/view
title: View
layout: docs
category: Components
permalink: docs/components/view.html
next: components/webview
---
This component is a generic container for other components.
## Props
In addition to the [common accessibility props](/reactxp/docs/accessibility.html), the following props are supported.
``` javascript
// Alternate text for screen readers.
// If not defined, title prop is used.
accessibilityLabel: string = undefined;
// Traits used to hint screen readers, etc.
accessibilityTraits: AccessibilityTrait | AccessibilityTrait[] = undefined;
// Region for accessibility mechanisms
accessibilityLiveRegion: AccessibilityLiveRegion =
undefined; // Android and web only
// It is hard or impossible to tell by a reference to an instance of a component
// from where this component has been instantiated. You can assign this property
// and check instance.props.accessibilityId. For example accessibilityId is used
// in View's FocusArbitrator callback.
accessibilityId: string = undefined;
// Opacity value the button should animate to, on touch on views that
// have onPress handlers
activeOpacity: number = undefined; // iOS and Android only
// Animation of children
// - Every child must have a `key`.
// - String refs aren't supported on children. Only callback refs are.
animateChildEnter: boolean = false;
animateChildLeave: boolean = false;
animateChildMove: boolean = false;
// Id of an element that describes the view for screenreader.
ariaLabelledBy?: string = undefined; // Web only
// A custom role description to be read by the screen readers.
ariaRoleDescription?: string = undefined; // Web only
// Block touches for this component and all of its children
blockPointerEvents: boolean = false;
// Disable default opacity animation on touch on views that have
// onPress handlers
disableTouchOpacityAnimation: boolean = false; // iOS and Android only
// Specifies a unique id for an HTML element
// NOTE: This property may be going away in future versions.
id: string = undefined; // Web only
// Ignore clicks and other mouse events, allowing children or
// components behind to receive them
ignorePointerEvents: boolean = false;
// Expose the element and/or its children as accessible to Screen readers
importantForAccessibility?: ImportantForAccessibility = Auto;
// When the keyboard navigation is happening, restrict the focusable
// elements within this view. Useful for popups and modals, you
// might want to prevent the focus from going outside of the popup or
// modal. The views with restrictFocusWithin are stacked and the last
// mounted view is a winner. This means if you, for example, have
// restricted the focus within some modal, and you have a popup (which
// also desires for a restricted focus) inside this modal, the popup
// will get the restriction, but when dismissed, the restriction will
// be restored for the modal. See also the companion method
// setFocusRestricted() below.
// WARNING: For the sake of performance, this property is readonly and
// changing it during the View life cycle will produce an error.
restrictFocusWithin: boolean = false;
// When the keyboard navigation is happening, do not focus on this view
// and on all focusable elements inside this view. See also the companion
// method setFocusLimited() below.
// Useful for the list items, allows to skip the consecutive focusing on
// one list item (and item's internal focusable elements) after another
// using the Tab key and implement the switching between the items using
// the arrow keys (or using some other behaviour).
// When limitFocusWithin=LimitFocusType.Limited, the View and the focusable
// components inside the View get both tabIndex=-1 and aria-hidden=true.
// When limitFocusWithin=LimitFocusType.Accessible, the View and the focusable
// components inside the View get only tabIndex=-1.
// WARNING: For the sake of performance, this property is readonly and
// changing it during the View life cycle will produce an error.
limitFocusWithin: LimitFocusType = LimitFocusType.Unlimited;
// Should be focused when the component is mounted, see also arbitrateFocus
// property below.
// WARNING: autoFocus=true means that this View's requestFocus() method will be
// called, however calling requestFocus() might have no effect (for example on web
// View is focusable only when tabIndex is specified), the application has to handle
// this either while setting this property or in the View's FocusArbitrator callback.
autoFocus: boolean = false;
// When multiple components with autoFocus=true inside this View are mounting at
// the same time, and/or multiple components inside this view have received focus()
// call during the same render cycle, this callback will be called so that it's
// possible for the application to decide which one should actually be focused.
arbitrateFocus: FocusArbitrator = undefined;
// Additional invisible DOM elements will be added inside the view
// to track the size changes that are performed behind our back by
// the browser's layout engine faster (ViewBase checks for the layout
// updates once a second and sometimes it's not fast enough)
importantForLayout: boolean = false; // web only
// Mouse-specific Events
// For drag specific events, if onDragStart is present, the view is draggable.
// onDragStart/onDrag/onDragEnd are source specific events
// onDragEnter/onDragOver/onDragLeave/onDrop are destination specific events
onDragStart: (e: DragEvent) => void = undefined;
onDrag: (e: DragEvent) => void = undefined;
onDragEnd: (e: DragEvent) => void = undefined;
onDragEnter: (e: DragEvent) => void = undefined;
onDragOver: (e: DragEvent) => void = undefined;
onDragLeave: (e: DragEvent) => void = undefined;
onDrop: (e: DragEvent) => void = undefined;
onMouseEnter: (e: MouseEvent) => void = undefined;
onMouseLeave: (e: MouseEvent) => void = undefined;
onMouseMove: (e: MouseEvent) => void = undefined;
onMouseOver: (e: MouseEvent) => void = undefined;
// Mouse & Touch Events
onContextMenu: (e: React.SyntheticEvent) => void;
onPress: (e: SyntheticEvent) => void = undefined;
// Focus Events
onFocus: (e: FocusEvent) => void = undefined;
onBlur: (e: FocusEvent) => void = undefined;
// Keyboard Events
onKeyPress: (e: KeyboardEvent) => void = undefined;
// Touch-specific Events
onTouchStartCapture: (e: React.SyntheticEvent) => void = undefined;
onTouchMoveCapture: (e: React.SyntheticEvent) => void = undefined;
onLongPress: (e: SyntheticEvent) => void = undefined;
onMoveShouldSetResponder: (e: React.SyntheticEvent) => boolean =
undefined;
onMoveShouldSetResponderCapture: (e: React.SyntheticEvent) => boolean =
undefined;
onResponderGrant: (e: React.SyntheticEvent) => void = undefined;
onResponderReject: (e: React.SyntheticEvent) => void = undefined;
onResponderRelease: (e: React.SyntheticEvent) => void = undefined;
onResponderStart: (e: React.TouchEvent) => void = undefined;
onResponderMove: (e: React.TouchEvent) => void = undefined;
onResponderEnd: (e: React.TouchEvent) => void = undefined;
onResponderTerminate: (e: React.SyntheticEvent) => void = undefined;
onResponderTerminationRequest: (e: React.SyntheticEvent) => boolean =
undefined;
onStartShouldSetResponder: (e: React.SyntheticEvent) => boolean =
undefined;
onStartShouldSetResponderCapture: (e: React.SyntheticEvent) => boolean =
undefined;
// Other Events
onLayout: (e: ViewOnLayoutEvent) => void = undefined;
// Rasterize contents using offscreen bitmap (perf optimization)
shouldRasterizeIOS: boolean = false; // iOS only
// Keyboard tab order
tabIndex: number = undefined;
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
// Text for a tooltip
title: string = undefined;
// See below for supported styles
style: ViewStyleRuleSet | ViewStyleRuleSet[] = [];
// Should use hardware or software rendering?
viewLayerTypeAndroid: 'none' | 'software' | 'hardware'; // Android only property
// Background color that will be visible on touch on views that have onPress
// handlers
underlayColor: string = undefined; // iOS and Android only
// When true
// - renders children within the safe area boundaries of a device, i.e. with
// padding with ensure the children don't cover navigation bars,
// toolbars etc.
// - Applies a style of { flex: 1, alignSelf: 'stretch' } to this view.
// - Some ViewProps may be ignored.
useSafeInsets: boolean = false; // iOS only
```
## Styles
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
``` javascript
// Sets the focus to the component.
focus(): void;
// The preferable way to focus the component. When requestFocus() is called,
// the actual focus() will be deferred, and if requestFocus() has been
// called for several components, only one of those components will actually
// get a focus() call. By default, last component for which requestFocus() is
// called will get a focus() call, but you can specify arbitrateFocus property
// of a parent View and provide the callback to decide which one of that View's
// descendants should be focused. This is useful for the accessibility: when
// consecutive focus() calls happen one after another, the next one interrupts
// the screen reader announcement for the previous one and the user gets
// confused. autoFocus property of focusable components also uses requestFocus().
requestFocus(): void;
// Blurs the component.
blur(): void;
// The focus does not go outside the view with restrictFocusWithin by default,
// setFocusRestricted() allows to turn this restriction off and back on.
setFocusRestricted(restricted: boolean): void; // web only
// The focus does not go inside the view with limitFocusWithin by default,
// setFocusLimited() allows to turn this limitation off and back on.
setFocusLimited(limited: boolean): void; // web only
```
================================================
FILE: docs/docs/components/webview.md
================================================
---
id: components/webview
title: WebView
layout: docs
category: Components
permalink: docs/components/webview.html
next: apis/alert
---
This has been deprecated from ReactXP Core and moved to an extension (reactxp-webview) inline with the React Native Lean Core initiative.
================================================
FILE: docs/docs/extensions/database.md
================================================
---
id: extensions/database
title: Database
layout: docs
category: Extensions
permalink: docs/extensions/database.html
next: extensions/imagesvg
---
ReactXP provides a [Storage API](/reactxp/docs/apis/storage) for reading and writing simple key/value pairs. Many apps have more complex storage requirements. For this, we developed a no-SQL database wrapper that works on React Native and on browsers. The solution isn't tied to ReactXP, but they work well together.
For more details, refer to the [NoSqlProvider](https://github.com/Microsoft/NoSQLProvider) GitHub site.
To install: ```npm install nosqlprovider```
================================================
FILE: docs/docs/extensions/imagesvg.md
================================================
---
id: extensions/imagesvg
title: ImageSvg
layout: docs
category: Extensions
permalink: docs/extensions/imagesvg.html
next: extensions/navigator
---
This component displays a vector image (SVG format). Props control the fill color, stroke color and stroke width.
The path(s) are specified using the standard SVG string format. Paths must be specified in a nested SvgPath component instance. Multiple SvgPath children can be specified, each with different stroke and fill parameters. SvgRect children
are also supported at this time, with the limited props available in react-native-svg.
To install: ```npm install reactxp-imagesvg``` or ```yarn add reactxp-imagesvg```
## ImageSvg Props
``` javascript
// See below for supported styles
style: RX.ImageSvgStyleRuleSet | RX.ImageSvgStyleRuleSet[] = [];
// Color and opacity of fill; default values are provided by SVG
fillColor: color;
fillOpacity: number;
// Preserve aspect ratio or stretch?
preserveAspectRatio: boolean = true;
// Color, width and opacity of stroke; default values are provided by SVG
strokeColor: color;
strokeWidth: number;
strokeOpacity: number;
// Tooltip for image
title: string = undefined;
// Bounding box
viewBox: string = undefined;
// Shadow
webShadow: boolean = false; // web-specific
```
## SvgCommon Props
These props apply to all of the sub-SVG-element types below:
``` javascript
// Color and opacity of fill; default values are provided by SVG
fillColor: color;
fillOpacity: number;
// Color, width and opacity of stroke; default values are provided by SVG
strokeColor: color;
strokeWidth: number;
strokeOpacity: number;
```
## SvgPath Props
``` javascript
// Path definition string
d: string = undefined;
```
## SvgRect Props
``` javascript
// Position and dimension information for the rect
x: number;
y: number;
width: number;
height: number;
```
## Styles
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
No methods
## Sample Usage
``` javascript
import { default as RXImageSvg, SvgPath as RXSvgPath, Types as SvgTypes }
from 'reactxp-imagesvg';
return (
);
```
================================================
FILE: docs/docs/extensions/navigator.md
================================================
---
id: extensions/navigator
title: Navigator
layout: docs
category: Extensions
permalink: docs/extensions/navigator.html
next: extensions/netinfo
---
This component provides a way for the app to present a virtual stack of "cards", allowing the user to push or pop those cards onto the stack in an animated manner. The caller can control the animation type and direction.
When a new card is presented, the Navigator calls the renderScene method, allowing the caller to render the contents. Cards are identified by "routes", which contain a unique ID and configuration parameters that control the behavior of the transition.
When a Navigator is first mounted, the stack is empty. The caller must wait for the mount to complete, then specify the list of routes to present.
The current implementation of Navigator on React Native makes use of the deprecated "Navigator Experimental". We will look at moving away from this implementation to the now-recommended "react-navigation" in the near future. Some of the more advanced interfaces may need to change. These are listed at the end of this article. Use these with caution.
To install: ```npm install reactxp-navigation```
## Types
``` javascript
// Specifies the behavior when transitioning from one
// card to another within the stack.
enum NavigatorSceneConfigType {
FloatFromRight,
FloatFromLeft,
FloatFromBottom,
Fade,
FadeWithSlide
}
// Provides information about a card and how it should
// be presented when pushed onto the stack or popped
// off the stack.
interface NavigatorRoute {
// Uniquely identifies the card
routeId: number;
// Animation parameter
sceneConfigType: NavigatorSceneConfigType;
// Optional gesture response distance override;
// 0 is equivalent to disabling gestures;
// works only on React Native platforms
gestureResponseDistance?: number;
// Optional custom scene config;
// works only on React Native platforms
customSceneConfig?: CustomNavigatorSceneConfig;
}
```
## Props
``` javascript
// Style to apply to the card
cardStyle: ViewStyleRuleSet = undefined;
// Called to render the specified scene
renderScene: (route: NavigatorRoute) => JSX.Element = undefined;
// Called when a transition between cards is complete
transitionCompleted: () => void = undefined;
```
## Methods
``` javascript
// Returns the current list of routes
getCurrentRoutes(): Types.NavigatorRoute[];
// Replaces the current list of routes with a new list
immediatelyResetRouteStack(
nextRouteStack: Types.NavigatorRoute[]): void;
// Pops the top route off the stack
pop(): void;
// Pops zero or more routes off the top of the stack until
// the specified route is top-most
popToRoute(route: Types.NavigatorRoute): void;
// Pops all routes off the stack except for the last
// remaining item in the stack
popToTop(): void;
// Push a new route onto the stack
push(route: Types.NavigatorRoute): void;
// Replaces the top-most route with a new route
replace(route: Types.NavigatorRoute): void;
// Replaces an existing route (identified by index) with
// a new route
replaceAtIndex(route: Types.NavigatorRoute, index: number): void;
// Replaces the next-to-top-most route with a new route
replacePrevious(route: Types.NavigatorRoute): void;
```
## Sample Usage
This sample demonstrates how an app can use Navigator to present a two-card stack, allowing the user to navigate between them. It omits some details for brevity. For a full working example, check out the hello-world sample app.
``` javascript
enum NavigationRouteId {
MainPanel,
SecondPanel
};
class App extends RX.Component {
private _navigator: RX.Navigator;
componentDidMount() {
// Now that the app is mounted, specify the initial
// navigator route.
this._navigator.immediatelyResetRouteStack([{
routeId: NavigationRouteId.MainPanel,
sceneConfigType: RX.Types.NavigatorSceneConfigType.Fade
}]);
}
render() {
return (
);
}
private _onNavigatorRef = (navigator: RX.Navigator) => {
// Stash away a reference to the mounted navigator
this._navigator = navigator;
}
private _renderScene = (navigatorRoute: NavigatorRoute) => {
switch (navigatorRoute.routeId) {
case NavigationRouteId.MainPanel:
return (
);
case NavigationRouteId.SecondPanel:
return (
);
}
return null;
}
// Called when the user presses a button on the MainPanel
// to navigate to the SecondPanel.
private _onPressNavigate = () => {
this._navigator.push({
routeId: NavigationRouteId.SecondPanel,
sceneConfigType:
RX.Types.NavigatorSceneConfigType.FloatFromRight,
customSceneConfig: {
hideShadow: true
}
});
}
// Called when the user presses a back button on the
// SecondPanel to navigate back to the MainPanel.
private _onPressBack = () => {
this._navigator.pop();
}
}
```
## Experimental Types
These types apply only to React Native platforms, and they currently rely on the soon-to-be-deprecated "Experimental Navigator". Some or all of these types may be deprecated in the near future, so use with caution.
``` javascript
// Additional options that affect card transitions
type CustomNavigatorSceneConfig = {
// Optional transition styles
transitionStyle?: (sceneIndex: number,
sceneDimensions: Dimensions) =>
NavigationTransitionStyleConfig;
// Optional overrides for duration, easing, and timing
transitionSpec?: NavigationTransitionSpec;
// Optional cardStyle override
cardStyle?: ViewStyleRuleSet;
// Optionally hide drop shadow
hideShadow?: boolean;
// Optionally flip the visual order of the last two scenes
presentBelowPrevious?: boolean;
};
// Parameters to control transition animations
type NavigationTransitionSpec = {
duration?: number;
easing?: Animated.EasingFunction;
};
// Parameters to control transition appearance
type NavigationTransitionStyleConfig = {
// By default, input range is defined as [index - 1, index, index + 1];
// Input and output ranges must contain the same number of elements
inputRange?: number[];
opacityOutput: number | number[];
scaleOutput: number | number[];
translateXOutput: number | number[];
translateYOutput: number | number[];
};
```
## Experimental Props
These props apply only to React Native platforms, and they currently rely on the soon-to-be-deprecated "Experimental Navigator". Some or all of these props may be deprecated in the near future, so use with caution.
``` javascript
// Called after the user swipes back in the stack and the transition
// is complete
navigateBackCompleted: () => void;
// Called when a transition begins; works only
// on native
transitionStarted: (progress?: RX.AnimatedValue,
toRouteId?: string, fromRouteId?: string,
toIndex?: number, fromIndex?: number) => void = undefined;
```
================================================
FILE: docs/docs/extensions/netinfo.md
================================================
---
id: extensions/netinfo
title: NetInfo
layout: docs
category: Extensions
permalink: docs/extensions/netinfo.html
next: extensions/restclient
---
This interface provides information about network connectivity.
## Types
``` javascript
enum DeviceNetworkType {
Unknown,
None,
Wifi,
Mobile2G,
Mobile3G,
Mobile4G
}
```
## Methods
``` javascript
// Returns a promise that specifies whether the device currently
// has network connectivity
isConnected(): SyncTasks.Promise;
// Returns the type of network
getType(): SyncTasks.Promise;
```
## Events
``` javascript
// Triggered when the connectivity changes
connectivityChangedEvent: SubscribableEvent<(isConnected: boolean) => void>;
```
## Sample Usage
``` javascript
private isConnected: boolean;
constructor() {
// Query the initial connectivity state.
this.isConnected = false;
RXNetInfo.isConnected().then(isConnected => {
this.isConnected = isConnected;
});
RXNetInfo.connectivityChangedEvent.subscribe(isConnected => {
// Update the connectivity state.
this.isConnected = isConnected;
});
}
```
## Other Notes
On Android, the following permission must be added to make use of the network interfaces.
``` xml
```
================================================
FILE: docs/docs/extensions/restclient.md
================================================
---
id: extensions/restclient
title: REST Client
layout: docs
category: Extensions
permalink: docs/extensions/restclient.html
next: extensions/video
---
ReactXP provides basic [Network APIs](/reactxp/docs/apis/network) for determining network connectivity, but it doesn't provide ways to access the network once connected.
This extension provides a cross-platform mechanism for wrapping a simple REST API. It supports optional retry logic (including exponential backoff).
For more details, refer to the [SimpleRestClients](https://github.com/Microsoft/SimpleRestClients) GitHub site.
To install: ```npm install simplerestclients``` or ```yarn add simplerestclients```
### Sample Usage
``` javascript
import { GenericRestClient, ApiCallOptions } from 'simplerestclients';
import SyncTasks = require('synctasks');
export interface User {
id: string;
firstName: string;
lastName: string;
}
export default class MyRestClient extends GenericRestClient {
constructor(private _appId: string) {
super('https://myhost.com/api/v1/');
}
// Override _getHeaders to append a custom header with the app ID.
protected _getHeaders(options: ApiCallOptions): { [key: string]: string } {
let headers = super._getHeaders(options);
headers['X-AppId'] = this._appId;
return headers;
}
// Define public methods that expose the APIs provided through
// the REST service.
getAllUsers(): SyncTasks.Promise {
return this.performApiGet('users');
}
getUserById(id: string): SyncTasks.Promise {
return this.performApiGet('user/' + id);
}
setUser(user: User): SyncTasks.Promise {
return this.performApiPut('user/' + user.id, user);
}
}
```
================================================
FILE: docs/docs/extensions/video.md
================================================
---
id: extensions/video
title: Video
layout: docs
category: Extensions
permalink: docs/extensions/video.html
next: extensions/virtuallistview
---
This component provides video playback capabilities, presenting optional controls for play, pause, etc.
To install: ```npm install reactxp-video```
## Types
``` javascript
// Used to return progress information in the onProgress callback
interface VideoProgress {
currentTime: number;
playableDuration: number;
atValue?: number;
target?: number;
atTimeScale?: number;
}
// Used to return information about the video once its metadata
// has been loaded; returned by the onLoadedData callback
interface VideoInfo {
duration?: number;
naturalSize?: {
width: number;
height: number;
};
}
```
## Props
``` javascript
// Alternate text to display if the image cannot be loaded
// or by screen readers
accessibilityLabel: string = undefined;
// Authentication token to include in request (not supported
// on some React Native implementations)
authToken: string = undefined;
// Should video playback loop to beginning after it completes?
loop: boolean = false;
// Called when the video is paused for buffering
onBuffer: () => void = undefined;
// Called when enough of the video has been loaded that playback
// is possible
onCanPlay: () => void = undefined;
// Called when enough of the video has been loaded that playback
// can proceed all the way to the end without buffering pauses
onCanPlayThrough: () => void = undefined;
// Called when the video playback reaches the end
onEnded: () => void = undefined;
// Called when the video cannot be loaded
onError: () => void = undefined;
// Called when the video's metadata has been loaded; returns
// information about the video
onLoadedData: (info: VideoInfo) => void = undefined;
// Called when the video data is starting to load
onLoadStart: () => void = undefined;
// Called periodically when the video is playing; reports
// progress information
onProgress: (progress: VideoProgress) => void = undefined;
// Indiciates which portion of the video should be pre-loaded
// when the component is mounted
preload: 'auto'|'metadata'|'none' = 'none';
// Determines how to resize the image if its natural size
// does not match the size of the container
resizeMode: 'contain'|'cover'|'stretch' = 'contain';
// Displays controls for play, pause, etc.
showControls: boolean = false;
// Source of video (URL) or resource `id` as resolved by `require()` for `react-native` targets.
source: string | number;
// See below for supported styles
style: ViewStyleRuleSet | ViewStyleRuleSet[] = [];
```
## Styles
[**Flexbox Styles**](/reactxp/docs/styles.html#flexbox-style-attributes)
[**View Styles**](/reactxp/docs/styles.html#view-style-attributes)
[**Transform Styles**](/reactxp/docs/styles.html#transform-style-attributes)
## Methods
``` javascript
// Mutes or unmutes the sound for the video
mute(muted: boolean): void;
// Pauses the video
pause(): void;
// Plays the video at its current position
play(): void;
// Seeks to the specified position (specified in seconds)
seek(position: number): void;
```
## Sample Usage
``` javascript
return (
);
```
================================================
FILE: docs/docs/extensions/virtuallistview.md
================================================
---
id: extensions/virtuallistview
title: VirtualListView
layout: docs
category: Extensions
permalink: docs/extensions/virtuallistview.html
next: extensions/webview
---
This components supports a vertical list of items within a scrolling area. The visible portion of the list is referred to as the "view port". The list is virtualized, which means that items are rendered only when they are within the view port (or just above or below the view port).
Unlike other list views (such as the ListView provided in React Native), this component supports lists of heterogeneous items with different heights and completely different types.
Each item in the list is described by a VirtualListViewItemInfo object. The list of items is specified by an ordered list passed in the itemList prop. When an item comes into view, it is rendered through the use of the renderItem callback. Additional fields can be added to the VirtualListViewItemInfo by the caller as it sees fit. For example, it is sometimes useful to include additional identifying information such as an item type or an item-specific render method.
A height must be specified for each item. By default, this height is assumed to be accurate and constant. If you do not know the height of the object or it may change, the height can be measured at runtime. This option is expensive, so it should be avoided if possible.
It optionally supports animation of items when they are added, removed or moved within the list.
When items are added before or after the visible region, it attempts to maintain the current position of the visible items, adjusting the scroll position and list height as necessary.
To install: ```npm install reactxp-virtuallistview```
## Performance Techniques
The VirtualListView employs a number of tricks to improve performance.
It uses a technique called "cell recycling" to minimize the number of mounts and unmounts. A cell is a container for a list item. When a cell is no longer visible, it can be temporarily hidden and then reused for the next item that becomes visible. This optimization is most effective when the recycled cell and its contents are used for an item that is similar in content. For this reason, callers need to specify a "template" field to identify similar items. In some cases, disabling cell recycling can be benificial as recycled cells continue their regular react lifecycle even when not visible, which can lead to excessive background re-rendering in some cases. When combining VLV with react libraries (like ReSub) that have subscriptions managed by components can cause this behaviour to manifest. To disable cell recycling on specific cells, exclude the template field from the item descriptor.
It also supports "overdraw" to render items above and below the view port. This minimizes chances that the user will scroll to a new portion of the list before new items can be rendered in that area. Overdraw is employed only after the view port is initially filled. This reduces the performance impact of rendering.
It also limits the number of items it renders each time to avoid consuming too many cycles on the javascript thread.
It supports a special mode where items are re-rendered only if the corresponding VirtualListViewItemInfo changes. This mode requires that the renderItem method use only information within this object. To use this mode, set the skipRenderIfItemUnchanged prop to true.
## Example
``` javascript
import { VirtualListView, VirtualListViewItemInfo }
from 'reactxp-virtuallistview';
// Extend VirtualListViewItemInfo to include display text
interface FruitListItemInfo extends VirtualListViewItemInfo {
text: string;
}
interface FruitListState {
items: FruitListItemInfo[];
}
const _headerItemHeight = 20;
const _fruitItemHeight = 32;
const _headerItemTemplate = 'header';
const _fruitItemTemplate = 'fruit';
class FruitListView extends RX.Component {
constructor() {
super();
this.state = {
items: [{
key: 'header1',
height: _headerItemHeight,
text: 'Domstic Fruits',
template: _headerItemTemplate
}, {
key: 'bannana',
height: _fruitItemHeight,
text: 'Banana',
template: _fruitItemTemplate
}, {
key: 'apple',
height: _fruitItemHeight,
text: 'Apple',
template: _fruitItemTemplate
}]
};
}
render() {
return (
);
}
private _renderItem(details: VirtualListViewCellRenderDetails) {
const viewStyle = RX.Styles.createViewStyle({
height: details.item.height,
backgroundColor: item.template === _headerItemTemplate ?
'#ddd' : '#fff',
alignItems: 'center'
}, false);
return (
{ details.item.text }
);
}
}
```
## Interfaces
``` javascript
interface VirtualListViewItemInfo {
// A string that uniquely identifies this item.
key: string;
// Specifies the known height of the item or a best guess if the
// height isn't known.
height: number;
// Specifies that the height is not known and needs to be measured
// dynamically. This has a big perf overhead because it requires a
// double layout (once offscreen to measure the item). It also
// disables cell recycling. Wherever possible, it should be avoided,
// especially for perf-critical views.
measureHeight?: boolean;
// Specify the same "template" string for items that are rendered
// with identical or similar view hierarchies. When a template is
// specified, the list view attempts to recycle cells whose templates
// match. When an item scrolls off the screen and others appear on
// screen, the contents of the cell are simply updated rather than
// torn down and rebuilt.
template: string;
// Is the item navigable by keyboard or through accessibility
// mechanisms?
isNavigable?: boolean;
}
```
## Props
``` javascript
// Should the list animate additions, removals and moves within the list?
animateChanges?: boolean;
initialSelectedKey?: string;
// Ordered list of descriptors for items to display in the list.
itemList: VirtualListViewItemInfo[];
// Use this if you want to vertically offset the focused item from the
// top of the viewport when using keyboard nav
keyboardFocusScrollOffset?: number;
// Logging callback to debug issues related to the VirtualListView.
logInfo?: (textToLog: string) => void;
// Callback when an item in the VLV is selected
onItemSelected?: (item: ItemInfo) => void;
// Optional padding around the scrolling content within the list.
padding?: number;
// Callback for rendering item when it becomes visible within view port.
renderItem: (renderDetails: VirtualListCellRenderDetails) =>
JSX.Element | JSX.Element[];
// If true, allows each item to overflow its visible cell boundaries;
// by default, item contents are clipped to cell boundaries.
showOverflow?: boolean;
// By default, VirtualListView re-renders every item during the render.
// Setting this flag to true allows the list view to re-render only
// items from itemList whose descriptor has changed, thus avoiding
// unnecessary rendering. It uses _.isEqual to perform this check. In
// this mode, renderItem should not depend on any external state, only
// on VirtualListViewItemInfo, to render item.
skipRenderIfItemUnchanged?: boolean;
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
// Pass-through properties for scroll view.
keyboardDismissMode?: 'none' | 'interactive' | 'on-drag';
keyboardShouldPersistTaps?: boolean;
disableScrolling?: boolean;
scrollsToTop?: boolean; // iOS only, scroll to top when user taps on status bar
disableBouncing?: boolean; // iOS only, bounce override
scrollIndicatorInsets?: { top: number, left: number,
bottom: number, right: number }; // iOS only
onLayout?: (e: RX.Types.ViewOnLayoutEvent) => void;
scrollEventThrottle?: number;
onScroll?: (scrollTop: number, scrollLeft: number) => void;
scrollXAnimatedValue?: RX.Types.AnimatedValue;
scrollYAnimatedValue?: RX.Types.AnimatedValue;
```
## Methods
``` javascript
// Scrolls the view to the specified top value (specified in pixels).
scrollToTop(animated = true, top = 0);
// Sets selection & focus to specified key
selectItemKey(key: string);
```
================================================
FILE: docs/docs/extensions/webview.md
================================================
---
id: extensions/webview
title: WebView
layout: docs
category: Extensions
permalink: docs/extensions/webview.html
---
This component displays HTML contents in an embedded browser control.
To limit the functionality of the browser control, specify one or more sandbox options. For detailed definitions of sandbox flags, refer to the [HTML documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe).
## Types
``` javascript
enum WebViewSandboxMode {
None = 0,
AllowForms = 1 << 0,
AllowModals = 1 << 1,
AllowOrientationLock = 1 << 2,
AllowPointerLock = 1 << 3,
AllowPopups = 1 << 4,
AllowPopupsToEscapeSandbox = 1 << 5,
AllowPresentation = 1 << 6,
AllowSameOrigin = 1 << 7,
AllowScripts = 1 << 8,
AllowTopNavigation = 1 << 9,
// Control https mixed content behavior, never by default
AllowMixedContentAlways = 1 << 10,
AllowMixedContentCompatibilityMode = 1 << 11
}
interface WebViewNavigationState {
canGoBack: boolean;
canGoForward: boolean;
loading: boolean;
url: string;
title: string;
readonly navigationType:
| 'click'
| 'formsubmit'
| 'backforward'
| 'reload'
| 'formresubmit'
| 'other';
}
interface WebViewErrorState {
description: string;
domain: string;
code: string;
}
interface WebViewSource {
html: string;
baseUrl?: string; // Native only
}
```
## Props
``` javascript
// Allow javascript code to call storage methods?
domStorageEnabled: boolean = true; // Native only
// JavaScript code that is injected into the control and executed
injectedJavaScript: string = undefined; // Native only
// Is JavaScript executed within the control?
javaScriptEnabled: boolean = true;
// Determines whether HTML5 audio and video requires the user to tap them before they start playing.
mediaPlaybackRequiresUserAction: boolean = true; // Native only
// Determines whether HTML5 videos play inline or use the native full-screen controller.
allowsInlineMediaPlayback: boolean = false; // iOS only
// HTTP headers to include when fetching the URL.
headers: { [headerName: string]: string } = undefined;
// Called when an error occurs that prevents the contents from loading
onError: (e: SyntheticEvent) => void = undefined; // Native only
// Called when the contents successfully load
onLoad: (e: SyntheticEvent) => void = undefined;
// Called when the contents start to load
onLoadStart: (e: SyntheticEvent) => void = undefined; // Native only
// Called when a message is posted from within a WebView
onMessage: (e: WebViewMessageEvent) => void = undefined;
// Called when the navigation state changes
onNavigationStateChange: (navigationState: WebViewNavigationState) => void; // Native only
// Flags that restrict behaviors within the control
sandbox: WebViewSandboxMode = None;
// Zooms the page contents to fit the available space; deprecated on
// iOS in RN 0.57
scalesPageToFit: boolean = false; // Native only
// HTML to display in webview (if url is not specified)
source: WebViewSource = undefined;
// Start loading immediately or wait for reload?
startInLoadingState: boolean = true; // Native only
// See below for supported styles
style: WebViewStyleRuleSet | WebViewStyleRuleSet[] = [];
// ID that can be used to identify the instantiated element for testing purposes.
testId: string = undefined;
// URL to HTML content
url: string = undefined;
```
## Styles
No specialized styles
## Methods
``` javascript
// Navigate back and forward
goBack();
goForward();
// Posts a message to the web control, allowing for communication between
// the app and the JavaScript code running within the web control. On native
// platforms, the targetOrigin is ignored.
postMessage(message: string, targetOrigin?: string = '*'): void;
// Force the page to reload
reload();
```
================================================
FILE: docs/docs/extensions.md
================================================
---
id: extensions
title: Extensions
layout: docs
category: Overview
permalink: docs/extensions.html
next: components/activityindicator
---
The ReactXP library is designed to be as lightweight as possible, including only those cross-platform APIs and "primitive" components that are required by almost every app. Functionality that is needed less commonly is provided in the form of optional extensions.
There are two distinct types of extensions.
1. Higher-level components that contain no platform-specific code but build upon the lower-level primitives to provide new (typically more complex) functionality.
2. Additional APIs or "primitive" components that have separate implementations for each of the supported platforms.
Extensions are published as separate npm packages. They typically start with the prefix "reactxp-".
## Higher-level ReactXP Components
Using a higher-level component is just like using any other component in React. Because these components are built on cross-platform ReactXP primitives, they also work in a cross-platform manner.
## Primitive ReactXP Extensions
To use a primitive extension in your app, add it to your package.json like any other npm module. Then import it at the top of the code module where you want to consume it.
Here is an example of how to use a hypothetical extension that provides video playback capabilities in a cross-platform manner.
``` typescript
import RXVideoPlayer from 'reactxp-videoplayer';
class MyVideoPanel extends RX.Component {
render() {
return (
);
}
}
```
### Implementing a new "Primitive"
A new "primitive" extension (either a component or an API namespace) must have a cross-platform interface and a platform-specific implementation of this interface for each of the platforms supported by ReactXP. In some cases, the implementation may be shared between platforms.
An implementation should be provided for each of the supported platforms. Web, iOS and Android are required. React Native for MacOS and Windows are still under development and are currently considered optional, but they may become required in the future. The main implementation file for each platform is named ```index.[platform].js```. For the web, the platform is omitted (that is, the file is named ```index.js```). These implementation files must live at the top level of the extension's published directory, but it is typical for them to simply import code from a ```dist``` directory.
Many ReactXP apps are written in TypeScript, so it's highly recommended that all ReactXP extensions provide a type definition file that defines the cross-platform interface to the extension. The extension's ```package.json``` should refer to this type file. For example: ```"types": "dist/MyExtension.d.ts"```. If you are implementing the extension in TypeScript, the compiler can be used to ensure that the implementation for each platform matches the public interface.
## Sample Extensions
Refer to the "Extensions" section of the documentation for a full list of available ReactXP extensions. Some of these are in separate git repositories, and others are in the [extensions](https://github.com/Microsoft/reactxp/tree/master/extensions) directory of the reactxp repository. The [reactxp-video](https://github.com/Microsoft/reactxp/tree/master/extensions/video) extension is a good example of a complete extension that involves native code for each of the supported platforms. To build any of these sample extensions:
1. Clone the repository.
2. Switch to the top-level directory of the extension (e.g. ```reactxp/extensions/video```).
3. Install the dependent npm packages: ```npm install```.
4. Build the code: ```npm run build```.
The resulting code will be found in the ```dist``` directory.
================================================
FILE: docs/docs/faq.md
================================================
---
id: faq
title: Frequently Asked Questions
layout: docs
category: Overview
permalink: docs/faq.html
next: react_concepts
---
### What platforms does ReactXP support?
ReactXP currently supports the following platforms:
* iOS (React Native)
* Android (React Native)
* Web (React)
* Windows 10 -- UWP (React Native) - in progress
Other platforms such as Windows 7 & 8, MacOS, and Linux can be targeted using a web wrapper solution like [Electron](https://electron.atom.io/).
### What browsers does ReactXP support?
We've tested ReactXP with recent versions of the following browsers:
* Chrome (Windows and Mac)
* Internet Explorer (9 and newer)
* Edge
* Firefox
Other HTML5 browsers should theoretically work as well.
### Can I use ReactXP without TypeScript?
Yes, you can write your application's code in JavaScript or any language that compiles to JavaScript (TypeScript, Flow, etc.). ReactXP is implemented in TypeScript, and the distribution includes TypeScript type definitions for the ReactXP interface.
### How does ReactXP relate to React Native?
ReactXP builds upon React Native. ReactXP's components and APIs are inspired by React Native --- and in most cases are the same as React Native. ReactXP generally exposes APIs, props, style attributes, and animation interfaces that are common to the React Native implementations on iOS and Android (as well as the nascent implementation on Windows). It also implements these same APIs, props, style attributes, and animation interfaces on the web.
ReactXP is neither a proper subset nor a proper superset of React Native. It doesn't expose every component provided by React Native. In particular, it doesn't expose any components that are platform-specific (such as PickerIOS or MapView). It also exposes some components and APIs that are not implemented in React Native, such as [GestureView](components/gestureview.html) and [UserPresence](apis/userpresence.html).
Apps that use ReactXP can directly access React Native components and APIs. They can also directly access React DOM elements on the web. But such code will need to include build-time or run-time conditionals to avoid using these components or APIs on platforms where they don't apply.
All components and APIs exposed through ReactXP are implemented for all supported platforms. In some cases, API calls are no-ops, but they are guaranteed to be implemented. For example, Input.backButtonEvent is a no-op on iOS and the web, and StatusBar APIs are no-ops on the web. Apps that use only ReactXP abstractions can generally avoid per-platform conditional checks.
### How does ReactXP differ from React Native for Web?
[React Native for Web](https://github.com/necolas/react-native-web) is an open-sourced library developed by engineers at Twitter. We started implementing ReactXP before React Native for Web was available.
The goals behind these two efforts are similar, but the approaches differ. ReactXP is a layer that sits on top of React Native and React, whereas React Native for Web is a parallel implementation of React Native --- a sibling to React Native for iOS and Android.
ReactXP generally exposes only those props, style attributes, and APIs that are available across all platforms. If you write to ReactXP's abstraction, you can have high confidence that it will run on all supported platforms in the same manner. The same can be achieved with React Native for Web/iOS/Android, but you need to be more careful about which components, props, and APIs you use.
One of our goals in writing ReactXP was to enable developers to write and debug code in whatever platform environment they felt most comfortable while having confidence that the resulting code would run the same on other platforms.
### How does ReactXP differ from Xamarin?
[Xamarin](https://www.xamarin.com/) is a cross-platform solution that allows developers to create apps on iOS, Android and Windows Phone using a single code base. Xamarin apps are written in C# and XAML, allowing .NET developers to leverage their skills and experience. Xamarin apps can be more efficient than React Native apps, which are limited by JavaScript performance and the overhead of the React Native bridge. Xamarin was acquired by Microsoft in early 2016 and is supported by a dedicated team of engineers. It offers a comprehensive development solution including tools for coding, debugging, performance analysis, builds, automated testing, and distribution.
ReactXP, unlike Xamarin, provides a way to create mobile apps _and_ web apps using the same source base. ReactXP (like React and React Native) allow experienced web developers to make use of their existing skills and knowledge. ReactXP was developed by the Skype team at Microsoft in support of their development needs. ReactXP builds upon the work of Facebook and the broader React open source community.
Both Xamarin and ReactXP are great solutions, but they solve somewhat different problems.
================================================
FILE: docs/docs/getting-started.md
================================================
---
id: getting-started
title: Getting Started
layout: docs
category: Overview
permalink: docs/getting-started.html
next: using-reactxp
redirect_from:
- "docs/index.html"
---
## Building Your First ReactXP App
To create your first ReactXP app, do the following:
1. Clone the ReactXP repo locally: ```git clone https://github.com/microsoft/reactxp```.
2. Open the ```samples``` directory and pick one of the samples and copy its directory to a new location. [Hello-World](https://github.com/Microsoft/reactxp/tree/master/samples/hello-world) provides a bare-bones starting app. [Hello-World-js](https://github.com/Microsoft/reactxp/tree/master/samples/hello-world-js) is a variant of Hello-World written in Javascript rather than TypeScript. [TodoList](https://github.com/Microsoft/reactxp/tree/master/samples/TodoList) is a more complex example and is a more appropriate starting point for production applications.
3. Follow the build instructions for the sample you've chosen.
4. If desired, rename the directory and project files to reflect the name of your app.
If you want to create a new ReactXP app, use the command-line tool [create-rx-app](https://github.com/a-tarasyuk/create-rx-app).
## Web Technologies
React apps are written using web programming techniques. This documentation assumes that you are already familiar with web programming concepts including the use of JavaScript, the browser DOM, browser event handling, and CSS styling. There are many online tutorials that cover these concepts.
While it is possible to write ReactXP apps in JavaScript, we recommend using TypeScript or Flow instead. These languages add type safety, compile-time error detection, and IntelliSense capabilities to JavaScript. If you are already familiar with JavaScript, it is easy to learn TypeScript. Here is a recommended [TypeScript tutorial](http://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html).
## Understanding React
If you are new to React, you should first familiarize yourself with the core concepts. We provide an overview and some [simple examples](react_concepts.html). Also refer to the official [React](https://reactjs.org/) and [React Native](https://facebook.github.io/react-native/) documentation sites.
================================================
FILE: docs/docs/react_concepts.md
================================================
---
id: react_concepts
title: React Concepts
layout: docs
category: Overview
permalink: docs/react_concepts.html
next: react_lifecycle
---
## Components
UI elements in React are called *components*. A component defines the appearance (layout, style, motion) and the behavior of the UI element. Once a component is defined, it can be incorporated within other components to build a complete user interface.
## Rendering
React components derive from the templated base class React.Component. P and S refer to *props* and *state*, two concepts that we will explore below. The most important method in a React component is the *render* method. The example below shows a minimal React component that simply renders some text.
class HelloWorld extends React.Component {
render() {
return Hello World;
}
}
This example uses the JSX angle bracket syntax. TypeScript 1.6 contains native support for this notation. Simply name your source file with a "tsx" file extension rather than "ts".
Note that this component is emitting a "div" tag, which is valid only in browser environments. To make this into a ReactXP component, simply replace the "div" with a "RX.Text" tag.
class HelloWorld extends RX.Component {
render() {
return Hello World ;
}
}
Also note that `RX.Component` replaces `React.Component` in the above example. [ReactXP *re-exports* `React.Component`](https://github.com/Microsoft/reactxp/blob/master/src/web/ReactXP.ts#L131) as `RX.Component` so your imports remain tidy, you don't need to import `React` specifically.
## Props
It's convenient for parent components to customize child components by specifying parameters. React allows components to define a set of properties (or "props" for short). Some props are required, others are optional. Props can be simple values, objects, or even functions.
We will modify the Hello World example to introduce an optional "userName" prop. If specified, the component will render a hello message to the user. Methods within the component class can access the props using "this.props".
interface HelloWorldProps {
userName?: string; // Question mark indicates prop is optional
}
class HelloWorld extends RX.Component {
render() {
return (
{ 'Hello ' + (this.props.userName || 'World') }
);
}
}
## Styles
The example above renders a string using default styles (font, size, color, etc.). You can override style defaults by specifying a "style" prop. In this example, we render bold text on a green background. Note that styles within React (and ReactXP) borrow heavily from CSS.
// By convention, styles are created statically and referenced
// through a private (not exported) _styles object.
const _styles = {
container: RX.Styles.createViewStyle({
backgroundColor: 'green'
}),
text: RX.Styles.createTextStyle({
color: 'red',
fontSize: 36, // Size in pixels
fontWeight: 'bold'
})
};
class HelloWorld extends RX.Component {
render() {
return (
Hello World
);
}
}
For more details about style attributes, refer to the [styles](/reactxp/docs/styles.html) documentation or the documentation for each component.
## Layout Directives
React uses flexbox directives for component layout. These directives are specified along with styling information. A number of flexbox tutorials are available online. [Here](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) is one we especially recommend. Using flexbox directives, you can specify the primary layout direction (row or column), justification, alignment, and spacing.
React also adopts the notion of margin and padding from CSS. Margin is the amount of space around a component, and padding is the amount of space between the boundary of the component and its children.
Here is an example style that incorporates margin, padding and flexbox directives.
const _styles = {
container: RX.Styles.createViewStyle({
flexDirection: 'column',
flexGrow: 1,
flexShrink: 1,
alignSelf: 'stretch',
justifyContent: 'center',
margin: 4,
padding: 4,
backgroundColor: 'green'
})
};
For more details about layout directives, refer to the [styles](/reactxp/docs/styles.html) documentation.
## Event Handling
Events, such as user gestures, key presses or mouse actions, are reported by way of event-handler callbacks that are specified as props. In this example, the component registers an onPress callback for a button.
class CancelButton extends RX.Component {
render() {
return (
Cancel
);
}
private _onPress = (e: RX.SyntheticEvent) => {
e.stopPropagation();
// Cancelation logic goes here.
}
}
This example makes use of a TypeScript lambda function to bind the _onPress variable to the method instance at class creation time. It also demonstrates a few conventions (use of the variable name "e" to represent the event object and a method name beginning with an underscore to indicate that it's private). It also demonstrates a best practice (calling the stopPropagation method to indicate that the event was handled).
## State
As we saw in the examples above, a component's appearance and behavior can change based on externally-provided props. It can also change based on its own internally-managed state. As a simple example, the visual style may change when a user mouses over the component.
React components can define a *state* object. When this object is updated through the use of the *setState* method, the component's render method is automatically called. In the example below, we implement a simple stop light with two states. Depending on the current state, the light is drawn in red or green. A press or click toggles the state.
interface StopLightState {
// Fields within a state object are usually defined as optional
// (hence the question mark below) because calls to setState
// typically update only a subset of the fields.
isStopped?: boolean;
}
const _styles = {
redButton: RX.Styles.createViewStyle({
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: 'red'
}),
greenButton: RX.Styles.createViewStyle({
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: 'green'
})
};
class StopLight extends RX.Component {
getInitialState(): StopLightState {
return { isStopped: true };
}
render() {
// Choose the appropriate style for the current state.
var buttonStyle = this.state.isStopped ?
_styles.redButton : _styles.greenButton;
return (
);
}
private _onToggleState = (e: RX.MouseEvent) => {
e.stopPropagation();
// Flip the value of "isStopped" and re-render.
this.setState({ isStopped: !this.state.isStopped });
}
}
Component state can also be stored as instance variables defined by the class. However, if a piece of data is used by the render method, it is better to add it to the state object and update it through the use of a setState call. That way, the rendered component will always reflect the current state.
================================================
FILE: docs/docs/react_lifecycle.md
================================================
---
id: react_lifecycle
title: Component Lifecycle
layout: docs
category: Overview
permalink: docs/react_lifecycle.html
next: react_stores
---
## Virtual DOM
As we learned from previous examples, a component's *render* method returns a tree of component specs. The React framework converts these specs into actual DOM elements (in the case of React JS) or native controls (in the case of React Native).
We also saw in previous examples that the render method is called every time the props change or the internal state is modified through the setState method. It would be very inefficient if every one of these changes resulted in the deallocation and re-allocation of a DOM element or native control. React avoids this overhead by allocating the DOM element or control the first time it appears in the component spec tree and deallocating it only when it no longer appears in the spec tree. Any other changes result in lightweight updates.
How does React know whether a component instance already exists -- or whether it requires updating? It makes use of a *virtual DOM*, a cached instance of the component spec tree. Using simple differencing logic, it efficiently determines whether nodes within this tree need to be added, removed, or updated (when props change).
## Keys
Lists of components present an interesting challenge for React's tree diffing approach. How can the diffing algorithm efficiently detect the case where a component is moved to a new location in the list? This is done through the use of *keys*. A key is a string that uniquely identifies a component instance from other components in a list. A key must be specified by the render method any time a list child components of the same type are returned.
In this example, a UserInfoCard component is rendered for each user in a list. Each UserInfoCard instance is given a key based on a unique ID corresponding to each user.
render() {
var users = _.map(this.state.userList, user => {
return (
);
});
return (
{ users }
);
}
## Mounting & Unmounting
When React encounters a component spec that has no corresponding node in the current virtual DOM, it inserts the spec into the virtual DOM. It also allocates a corresponding DOM element (in the case of React JS) or native control (in the case of React Native). This is referred to as *mounting* a component. Likewise, when a component instance is removed from the real DOM or native control hierarchy, it is said to be *unmounted*. Certain methods, such as *setState*, can be called only while a component is mounted.
The React.Component base class, from which all components derive, defines several methods that are called immediately before and after a component is mounted and before a component is unmounted. Component classes can override these methods if desired. For example, if you want to set the focus to a text input box, this can be done within the componentDidMount method.
protected componentWillMount();
protected componentDidMount();
protected componentWillUnmount();
## Updating
A previously-mounted component can be updated in one of two ways -- through modification of its props or its state. By default, React performs a deep comparison of props and state to determine whether they have changed. Components may override this behavior to provide optimized comparison logic based on more detailed knowledge about the component.
protected shouldComponentUpdate(nextProps: P, nextState: S);
Once it is determined that a component should be updated, it is informed before and after the update.
protected componentWillReceiveProps(props: P);
protected componentWillUpdate(nextProps: P, nextState: S);
protected componentDidUpdate(prevProps: P, prevState: S);
================================================
FILE: docs/docs/react_stores.md
================================================
---
id: react_stores
title: Stores & Services
layout: docs
category: Overview
permalink: docs/react_stores.html
next: styles
---
## Stores
React is concerned only with the view layer of your app. It doesn't dictate the data model layer. However, it is common within React apps to refer to the data sources as _stores_. There are many ways that stores can be used, but most of them employ some form of subscription model whereby components register with a store, indicating that they are interested in all or part of the store's data. When the data within a store changes, it notifies all components that have expressed an interest in the change. Components can then update their internal state accordingly, triggering a re-render if appropriate.
## Flux
One popular model for stores and store updates is called _Flux_. It was designed by Facebook engineers for use with React. There are several good tutorials about Flux online. [Here](https://drewdevault.com/2015/07/20/A-practical-understanding-of-Flux.html) is one that we recommend. It is worth noting that Flux is a programming pattern -- a set of principles, not a framework or collection of APIs. You may choose to adopt all or some of the Flux principles.
## ReSub
The Skype team initially adopted the Flux principles, but we found it to be cumbersome. It requires the introduction of a bunch of new classes (dispatchers, action creators, and dispatch events), and program flow becomes difficult to follow and debug. Over time, we abandoned Flux and created a simpler model for stores. It leverages a new language feature in TypeScript (annotations) to automatically create subscriptions between components and stores. This eliminates most of the code involved in subscribing and unsubscribing. This pattern, which we refer to as [ReSub](https://github.com/Microsoft/ReSub), is independent of ReactXP, but they work well together.
================================================
FILE: docs/docs/styles.md
================================================
---
id: styles
title: Styles
layout: docs
category: Overview
permalink: docs/styles.html
next: animations
---
Each component type supports a predefined set of style attributes. ReactXP defines strongly-typed style objects for each base component. This allows for compile-time checking and IntelliSense. Styles must be allocated using the provided RX.Styles methods. These methods also perform validation of values and simple platform-specific or browser-specific transforms. Here are a few examples:
``` javascript
const myViewStyle = RX.Styles.createViewStyle({
backgroundColor: 'green'
});
const myTextStyle = RX.Styles.createTextStyle({
fontSize: 36,
fontWeight: 'bold'
});
```
Many base components share common subsets of style attributes. For example, almost every base component supports the flexbox attributes. See below for some of these common attribute sets.
## Combining Styles
All of the base components support a *style* prop that can accept a single style or an array of styles (or a nested array). If an array of styles is provided, the styles are combined in a depth-first manner left to right. Falsy values (false, null, undefined) can also be specified in a style array. This allows for the following common pattern.
``` javascript
let buttonTextStyles = [_styles.baseText, this.state.hovering && _styles.hoverText];
```
Here is another variant that does the same thing -- a little more verbose but arguably easier to read.
``` javascript
let buttonTextStyles = [_styles.baseText];
if (this.state.hovering) {
buttonTextStyles.push(_styles.hoverText);
}
```
If "margin" and "padding" attributes are combined with edge-specific attributes (e.g. "marginLeft" or "paddingBottom"), the specific attributes always override the general. This matches the [combining behavior of React Native](https://github.com/necolas/react-native-web/blob/0.10.0/docs/guides/style.md#how-styles-are-resolved) (but differs from CSS).
``` javascript
// this.props.style might be undefined, a single style, or a (potentially-nested)
// array of styles.
```
## Style Caching
By default, styles allocated by ReactXP are cached. This allows callers to refer to the style by a handle rather than rebuilding a full style structure each time it is used. For styles that depend on dynamic parameters or are allocated in non-static code paths, it is important to disable caching. Failing to do so will result in memory leaks. To disable caching, specify false as a second parameter to the creation method.
``` javascript
let dynamicViewStyle = RX.Styles.createViewStyle({
backgroundColor: userColor
}, false);
```
## Style Documentation Conventions
For each style attribute, the type and default value is specified. For enumerated values, the first item in the enumeration is the default value.
Color values are specified as strings. They can be specified as color names (e.g. 'red'), three-digit or six-digit hex values (e.g. '#444' or '#ff00ee'), or rgb or rgba values (e.g. 'rgb(255, 0, 67)' or 'rgba(255, 0, 67, 0.5)').
## Flexbox Style Attributes
ReactXP adopts the simplified flexbox rules and defaults defined by React Native. It differs somewhat from the flexbox standard in CSS in the following ways.
+ All components follow flex rules by default (with the notable exception of Text). Unlike CSS, there is no need to specify "display: flex".
+ Widths, heights, and other measurements are assumed to be pixel values. Other units (including percentages) are not supported.
+ While it is possible to specify flexGrow, flexShrink and flexBasis values independently, it is more common to specify the flex parameters using a shortcut called "flex". It accepts an integer value and covers the following common cases.
* "flex: 0" implies "flex: 0 0 auto"
* "flex: n" (where n is negative) implies "flex: 0 n auto"
* "flex: p" (where p is positive) implies "flex: p 1 auto"
+ The default flexDirection is 'column' rather than 'row'.
+ The default position for all elements is 'relative' rather than 'auto'.
+ The default justifyContent for buttons is 'center' rather than 'flex-start'.
**Container layout**
```javascript
flex: number = 0;
alignSelf: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'stretch';
```
**Child layout**
```javascript
alignContent: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'stretch';
alignItems: 'stretch' | 'flex-start' | 'flex-end' | 'center';
flexWrap: 'wrap' | 'nowrap';
flexDirection: 'column' | 'row' | 'column-reverse' | 'row-reverse';
justifyContent: 'flex-start' | 'flex-end' | 'center' | 'space-between' |
'space-around';
```
**Size Overrides**
```javascript
height: number = undefined; // Can be animated
width: number = undefined; // Can be animated
maxHeight: number = undefined;
maxWidth: number = undefined;
minHeight: number = undefined;
minWidth: number = undefined;
```
**Position Overrides**
```javascript
position: 'absolute' | 'relative';
top: number = undefined; // Can be animated
right: number = undefined; // Can be animated
bottom: number = undefined; // Can be animated
left: number = undefined; // Can be animated
```
**Margins**
```javascript
margin: number; // Sets all four margin attributes
marginHorizontal: number; // Sets left and right
marginVertical: number; // Sets top and bottom
marginTop: number = 0;
marginRight: number = 0;
marginBottom: number = 0;
marginLeft: number = 0;
```
**Padding**
```javascript
padding: number; // Sets all four padding attributes
paddingHorizontal: number; // Sets left and right
paddingVertical: number; // Sets top and bottom
paddingTop: number = 0;
paddingRight: number = 0;
paddingBottom: number = 0;
paddingLeft: number = 0;
```
## View Style Attributes
**Color & Opacity**
```javascript
backgroundColor: color = undefined; // Value is animatable
opacity: number = 1.0; // Value is animatable
acrylicOpacityUWP: number = 1.0; // UWP only
acrylicSourceUWP: 'host' | 'app'; // UWP only
acrylicTintColorUWP: string = undefined; // UWP only; default = backgroundColor
```
**Overflow**
```javascript
overflow: 'hidden' | 'visible';
```
**Borders**
```javascript
borderWidth: number;
borderTopWidth: number;
borderRightWidth: number;
borderBottomWidth: number;
borderLeftWidth: number;
borderColor: color;
borderStyle: 'solid' | 'dotted' | 'dashed' | 'none';
borderRadius: number; // Sets all four border radius attributes; value is animatable
borderTopRightRadius: number = 0;
borderBottomRightRadius: number = 0;
borderBottomLeftRadius: number = 0;
borderTopLeftRadius: number = 0;
```
**Shadows**
```javascript
// NOTE: If applied to a Text element, these properties translate to text shadows,
// not a box shadow.
shadowOffset: { height: number; width: number } = { 0, 0 };
shadowRadius: number = 0;
shadowColor: color = 'black';
elevation: number; // Android only
```
**Miscellaneous**
```javascript
wordBreak: 'break-all' | 'break-word'; // Web only
appRegion: 'drag' | 'no-drag'; // Web only
cursor: 'pointer' | 'default'; // Web only
```
## Transform Style Attributes
**Transforms**
```javascript
transform: {
// All transform values are animatable
perspective: string = undefined;
rotate: string = undefined;
rotateX: string = undefined;
rotateY: string = undefined;
rotateZ: string = undefined;
scale: number = 0;
scaleX: number = 0;
scaleY: number = 0;
translateX: number = 0;
translateY: number = 0;
}
```
## Text Style Attributes
**Font Information**
```javascript
// Attributes in this group cascade to child RX.Text components
fontFamily: string = undefined;
fontStyle: 'normal' | 'italic';
fontWeight: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' |
'600' | '700' | '800' | '900';
fontSize: number = undefined;
// Shortcut that sets all three font attributes
font: {
fontFamily: string = undefined;
fontStyle: 'normal' | 'italic';
fontWeight: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' |
'600' | '700' | '800' | '900';
}
```
**Text Color**
```javascript
// Attributes in this group cascade to child RX.Text components
color: color = 'black'; // Value is animatable
```
**Spacing Overrides**
```javascript
// Attributes in this group cascade to child RX.Text components
letterSpacing: number = 1;
lineHeight: number = 1;
```
**Alignment**
```javascript
textAlign: 'auto' | 'left' | 'right' | 'center' | 'justify';
textAlignVertical: 'auto' | 'top' | 'bottom' | 'center'; // Android specific
```
**Text Decoration**
```javascript
textDecorationLine: 'none' | 'underline' | 'line-through' |
'underline line-through';
textDecorationStyle: 'solid' | 'double' | 'dotted' | 'dashed';
textDecorationColor: string = 'black';
```
**Writing Direction**
```javascript
writingDirection?: 'auto' | 'ltr' | 'rtl';
```
**Miscellaneous**
```javascript
includeFontPadding: boolean = true; // Android specific
```
================================================
FILE: docs/docs/using-reactxp.md
================================================
---
id: using-reactxp
title: Using ReactXP
layout: docs
category: Overview
permalink: docs/using-reactxp.html
next: faq
---
## Tips for Web
ReactXP assumes that your main web page will have a DOM element container called "app-container". The root view of the app will be rendered within this container. Typically, this DOM element will be a <div> that covers the entire page.
## Tips for Native
The main module is assumed to be called "RXApp", and it must be registered as such by the native code. Refer to the sample app for how to register the module in Android and iOS.
## TypeScript Support
ReactXP is written in TypeScript and includes TypeScript type definition (".d.ts") files for the library.
## TSLint Support
ReactXP includes several [tslint](https://www.npmjs.com/package/tslint) custom rules that can be used in your project.
To use these rules, modify your tslint.json file to point to the rules within the reactxp dist directory, as follows.
```
"rulesDirectory": [
"./node_modules/reactxp/dist/tslint"
]
```
The following tslint rules are provided:
### grouped-import
This rule enforces that all ambient (non-relative) module imports are grouped together and are above the group of relative imports.
### incorrect-this-props
This rule checks for common errors in referencing ```this.props``` within methods that pass ```props``` as an input parameter.
### no-unreferenced-styles
This rule detects and reports any unreferenced entries within a ```_styles``` array.
================================================
FILE: docs/index.md
================================================
---
layout: hero
title: A library for building cross-platform apps
id: home
---
Building on React
ReactXP builds on the popular React JS and React Native frameworks.
XP means X-Platform
Share most of your code between the web, iOS, Android, and Windows.
Apps Users Will Love
Create great-looking, responsive web pages and mobile apps that feel native.
The authors of React use the phrase “learn once, write anywhere”. With React and React
Native, your web app can share most its logic with your iOS and Android apps, but the view
layer needs to be implemented separately for each platform. We have taken this a step further
and developed a thin cross-platform layer we call ReactXP. If you write your app to this
abstraction, you can share your view definitions, styles and animations across multiple target
platforms. Of course, you can still provide platform-specific UI variants, but this can be done
selectively where desired.
ReactXP is designed with cross-platform development in mind. In general, it exposes
APIs, components, props, styles and animation parameters that are implemented in
a consistent way across React JS (HTML) and React Native for iOS and Android. A few
platform-specific props and style attributes have been exposed, but we have tried to
keep these to a minimum.
ReactXP is meant to be as lightweight as possible. The “core” components and APIs are
limited to the functionality required for almost all applications. Extensions to
ReactXP expose more specialized functionality in a similar cross-platform manner.
================================================
FILE: docs/versions/version_history.md
================================================
---
id: version_history
title: Version History
layout: hero
sectionid: versions
permalink: versions/version_history.html
redirect_from:
- "versions/index.html"
---
## ReactXP Versioning
### Versioning Strategy
A new version of ReactXP will be released periodically following the same general update timeline of React Native. Each new version will get its own branch, allowing consumers of the library to take a dependency on a stable code base.
Following semver rules, the major version (the first number in the version string) will be incremented for breaking changes. The minor version (the second number) will be incremented for major new functionality that does not break existing contracts or behaviors.
### Version History
#### Version 2.0.0 of reactxp - _30 November 2019_
* All releases and notes going forward will be tracked on the [Github ReactXP Releases page](https://github.com/microsoft/reactxp/releases)
#### Version 2.0.0-rc.2 of reactxp-video - _9 October 2019_
* Updated react-native-video to 4.X
#### Version 2.0.0-rc.2 of reactxp-webview - _12 September 2019_
* Upgraded react-native-webview dependency
#### Version 2.0.0-rc.1 of reactxp - _27 July 2019_
* Adopted new major version number to reflect breaking changes with netinfo and webview.
* No other functionality since 1.7.0-rc.1 was published.
#### Version 1.7.0-rc.1 of reactxp-video, reactxp-imagesvg, reactxp-navigation - _14 July 2019_
* No new functionality, just version and dependency updates.
#### Version 1.7.0-rc.1 of reactxp - _14 July 2019_
* #1077: Added longPress support for web.
* #1080: Addes upport for delayLongPress on web.
* #1079: Fixed bug that caused onTouchMove not to trigger on mobile web.
* #1082: Added support on web for onTouchStart, onTouchEnd, and onTouchCancel props for RX.View.
* #1087: Added support on web for onHoverEnd.
* #1059: Improved support for RN 0.59.
* #1089: Added support on web for onTouchMoveCapture and onTouchStartCapture props of RX.View.
* #1098: Fixed bug on web where reference to HTML element was not released properly, resulting in leak.
* #1088: Added support on web for scrollEnabled prop in RX.ScrollView.
* #1098: Fixed bug in handling of images on RN platforms.
* #1118: Fixed bug on web related to pan gestures.
* #1114: Fixed crash on Android relating to TextInput event processing.
* #1102: Fixed bug on web where animated properties were not properly updated if they were interpolated.
* #1117: Added support on web for blockPointerEvents prop on RX.View.
* #1101: Extracted RX.WebView out of ReactXP core into an extension.
* #1091: Fixed bug on web in onPressIn processing where event could get triggered twice.
#### Version 2.0.0 of reactxp-virtuallistview - _6 Apr 2019_
* #1073: Removed internal string-based refs with React.RefObject.
#### Version 1.6.1 of reactxp - _23 Mar 2019_
* #1062: Fixed TextInput defaultValue not working on native platforms
* #1060: Fixed Ref type.
* #1058: Fixed ref assert on Windows.
#### Version 1.6.1 of reactxp-video - _23 Mar 2019_
* #1066: Fixed video extension to support react-native local assets.
#### Version 1.6.1 of reactxp-navigation - _23 Mar 2019_
* #1063: Fixed regression in reactxp-navigation.
#### Version 1.6.0 of reactxp-imagesvg, reactxp-navigation, reactxp-video - _16 Mar 2019_
* No new features; updated dependencies.
#### Version 1.6.0 of reactxp - _16 Mar 2019_
* No new features; updated dependencies.
#### Version 1.6.0-rc.4 of reactxp - _15 Mar 2019_
* #1055: Fixed crash due to incompatibilty with newly-released RN 59.
#### Version 1.6.0-rc.3 of reactxp - _10 Mar 2019_
* #891: Fixed bad interaction between RX.Clipboard.getText() and iOS Safari.
#### Version 1.6.0-rc.2 of reactxp - _9 Mar 2019_
* #1041: Fixed textDecorationStyle and textDecorationColor on web.
* #1042: Fixed transform rotate styles on web so they take a unit (e.g. "deg") for consistency with RN.
* #1040: Fixed auto-dismissing popups on the web.
* #1026: Changed margin and padding style combination rules on web to match RN.
#### Version 1.6.0-rc.1 of reactxp - _17 Feb 2019_
* #961: Fixed crash when calling setScrollTop/Left on native version of ScrollView.
* #972: If blurOnSubmit is specified on a TextInput, the code now respects that value.
* #974: Fixed bug in native Button implementation where 'false' style would be passed when disableTouchOpacityAnimation was specified.
* #980: Fixed bug in web implementation of onLongPress.
* #976: Made multiline TextInput growth behavior on web match native.
* #984: Added code to catch exception in web implementation of Storage.setItem method.
* #996: Added stronger typings for ref callbacks.
* #993: Added support for mediaPlaybackRequiresUserAction and allowsInlinedMediaPlayer props for WebView.
* #957: Added context mode to the popup container handling type list.
* #994: Removed msHyphens css property in web implementation of Text.
* #1001: Fixed units for rotateZ translation type for web animations.
* #1004: Fixed bug that resulted in crash when ReactXP ran in node (test) environment.
* #1006: Made selectItemKey scrollTo behaviour configurable - scrolling isn't always desired.
* #1016: View responder events props are now triggered properly.
* #1024: Added more mouse cursor types for web version of GestureView.
* Added support for interpolated animation values that have angle units (e.g. "90deg") on web.
#### Version 2.0.0-rc.3 of reactxp-virtuallistview - _30 Jan 2019_
* #1006: Make selectItemKey scrollTo behaviour configurable - scrolling isn't always desired
* #1007: Perf Improvements
#### Version 2.0.0-rc.2 of reactxp-virtuallistview - _15 Jan 2019_
* #989: Auto-scroll VLV on mount when initialScrollKey prop is provided
* #989: Fix keyboard scrolling bugs when non-keyboard-navigable items are interspersed with keyboard-navigable items
#### Version 2.0.0-rc.1 of reactxp-virtuallistview - _11 Jan 2019_
* #944: Rework VirtualListView for improved accessibility/performance (this contains breaking API changes - see documentation for new API)
#### Version 1.5.0 of reactxp-imagesvg, reactxp-navigation, reactxp-video, reactxp-virtuallistview - _1 Dec 2018_
* No new features; updated dependencies.
#### Version 1.5.0 of reactxp - _1 Dec 2018_
* #935: Maded ReactXP compatible with React.Fragment.
* #933: Reset VoiceOver queue when app goes inactive or in background (iOS and Windows).
* #939: Added keyboard event mapping for kePress handling in View (Windows and MacOS).
* #941: Updated RX.Animated.InterpolatedValue to allow for chaining interpolations.
* #942: Added key mappings for MacOS and wired onKeyPress for RX.View.
* #943: Added focus and blur calls for RX.Button, RX.Link, RX.Text and RX.View.
* #946: Fixed crashing bug in accessibility for iOS and MacOS.
* #949: Added onFocus, onBlur and onKeyPress support for GestureView.
* #948: Respect negative tabIndex in Button on MacOS.
* #953: Wired up tabIndex prop for web implementation of RX.TextInput.
#### Version 1.5.0-rc.4 of reactxp - _18 Nov 2018_
* #909: Eliminated not-prebound callbacks in view resize detector on web (perf improvement).
* #912: Fixed VoiceOver iOS 12 issue.
* #913 and #923: Added key codes that are specific for MacOS.
* #916: Added basic tabindex handling on views for MacOS.
* #920: Removed deep comparison on every button prop change (perf improvement).
* #917: Added onKeyDown support for MacOS ScrollView.
* #922: Removed undocumented and broken (onRN) API: addToScrolLeft and addToScrollTop on RX.ScrollView.
* #928: Fixed bug in _buildInternalProps of View relating to tabIndex.
* #927: Fixed drag support on MacOS.
* Added CONTRIBUTION guide.
#### Version 1.5.0-rc.1 of reactxp-virtuallistview - _11 Nov 2018_
* #899: Fix web accessibility
* #902: Mac accessibility improvements (keyboard focus).
#### Version 1.5.0-rc.3 of reactxp - _11 Nov 2018_
* #898: Ensure taht onKeyDown/Focus/Blur events are sent from ScrollView.
* #895: On Windows, make FocusManager track View instances with negative tabIndex.
* #901: Optimization on native platforms: when there are no accessibilityTraits, don't create an additional unneeded array.
* #902: Mac accessibility improvements (keyboard focus).
#### Version 1.5.0-rc.2 of reactxp - _28 Oct 2018_
* Fixed regression introduced recently in web version of ScrollView.
#### Version 1.5.0-rc.1 of reactxp - _27 Oct 2018_
New functionality:
* #848: Added support for RTL (right-to-left) change event on native platforms.
* #843: Added new AlertOption preventDismissOnPress.
* #847: Added support for Animated.Event, which allows animations to be associated with scroll events.
* #868: Added support for placeholder text color on web platform.
Bug fixes:
* #833: Eliminated rerender of app when mousing over popups.
* #846: Disabled mouse gestures in main view when a modal dialog is overlaying the main view.
* #854: Removed draggable="false" in web implementation of View, since this is the default value.
* #853: Emulate cache-control: max stale on iOS.
* #869: Eliminate excess focus calls.
* #877 and #875: Fixed crashes relating to popups due to race condition in unmounting.
* #873: Fixed scroll view focus issue on MacOS.
* Fixed several "hanging promises" that didn't properly deal with error conditions.
#### Version 1.4.0 of reactxp - _30 Sep 2018_
* Added the ability to programmatically retrieve the text selection for RX.Text (if selectable attribute is true). Includes test cases in RXPTest sample.
* Added drag and drop capabilities to RX.View (including onDragStart, onDrag, onDragEnd props). Includes test cases in RXPTest sample.
#### Version 1.4.0-rc.1 of reactxp-navigation - _21 Sep 2018_
* Changed renderScene to accept a return value of null rather than undefined, making it consistent with other render functions.
#### Version 1.4.0-rc.2 of reactxp - _15 Sep 2018_
* Fixed bug #800: If there are only cached popups and no visible one the main remains inaccessible for screen readers.
* Fixed bug #815: Show warning in console if disallowed style combinations appear.
#### Version 1.4.0-rc.1 of reactxp - _9 Sep 2018_
* Worked around change in recent versions of RN that removed support for Images with children.
* Changed announceForAccessibility implementation to use Assertive live region type instead of Polite.
* In web implementation of RX.Image, always show the img tag if XHR request option is used.
* Fixed bug #744: On web implementation, styling behavior differed between developmenta nd production.
* Added support for the new React.RefObject variant of ref prop.
* Added support for multi-window apps to RX.Alert.
* Added support for onLongPress to RX.GestureView.
* Fixed bug #793: In web implementation, cursor type was overridden in some cases.
* Added useWebKit prop for native RN.WebView in prep for NR 0.57 on iOS.
* Fixed bug that caused testID not to be set properly for RX.View instances.
#### Version 1.3.2 of reactxp-imagesvg - _25 Aug 2018_
* Added support for SvgRect children within SvgImage.
#### Version 1.3.1 of reactxp-imagesvg - _7 Aug 2018_
* Fixed bug in native implementation that caused runtime warning every time SvgPath was used.
#### Version 1.3.2 of reactxp - _2 Aug 2018_
* Fixed bug #745: WebView.onMessage receives additional props which are not defined in Types.WebViewMessageEvent.
* Fixed bug #746 relating to cursor overrides for buttons on the web implementation.
* Fixed bug #749: Set size attribute of input HTML element to 1.
* Fixed bug #753: isRightMouseButton implementation that works also on Mac.
* Fixed bug #752: Accessibility fix for Clipboard.
* Fixed bug #756: Prevent a crash during onLayout handling on web.
* Fixed bug #761: Update usages of findDOMNode to indicate that it returns null.
* Fixed bug #767: buttonStyles may be undefined; fixed inconsistency in onAccessibilityTapIOS definition for Button and View.
* Fixed regression in Timers.ts. Some environments don't define "global" and use "window" instead for the built-in timer functions.
* Use semver for reactxp dependencies.
* Feature #764: Add support for Android WebView compatibility modes.
#### Version 1.3.1 of reactxp-virtuallistview - _2 Aug 2018_
* Added support for testId prop.
#### Version 1.3.2 of reactxp - _7 Jul 2018_
* Updated custom tslint rule groupedImportRule to treat imports starting with '@' as relative rather than ambient.
* Made 'auto' a valid resizeMode for images in web implementation.
* Added support for 'title' prop on RX.TextInput.
#### Version 1.3.0 of reactxp-imagesvg, reactxp-navigation, reactxp-video, reactxp-virtuallistview - _28 Jun 2018_
* No new features; updated dependencies.
#### Version 1.3.0 of reactxp - _28 Jun 2018_
* Added support for function keys in native desktop implmenetations.
* Fixed recent regression that caused a crash when using the web implementation in a non-browser environment like node.
* Reverted change in behavior on web implementation of ActivityState.Inactive. This state is no longer used on the web, as before 1.3.0-rc.4.
#### Version 1.3.0-rc.6 of reactxp - _25 Jun 2018_
* Added support for testId prop to all reactxp components. On native, it uses testID. On web, it adds a data-test-id attribute to the node.
* Added three new tslint custom rules that are useful for reactxp projects.
#### Version 1.3.0-rc.5 of reactxp - _24 Jun 2018_
* Added missing definition for getMetadata method in RX.Image.
* Implemented RX.International methods for web platform.
#### Version 1.3.0-rc.4 of reactxp - _19 Jun 2018_
* Enabled context menu on keyboard input in Windows UWP implementation.
* On web implementation, augmented AppActivityState. Inactive state now indicates that main window is not in focus.
* On web implementation, eliminated dependency on ifvisible library, shrinking the footprint of reactxp.
#### Version 1.3.0-rc.3 of reactxp - _16 Jun 2018_
* [Breaking Change] Removed hideDeleteButton prop in RX.TextInput in favor of clearButtonMode prop, which applies to iOS and Windows UWP.
#### Version 1.3.0-rc.2 of reactxp - _14 Jun 2018_
* [Breaking Change] Renamed several methods in RX.Animated.ValueListener that were meant to be private.
* Improved screen reader support in Windows UWP implementation.
* On iOS implementation, automatically set decelerationRate to "fast" if snapToInterval is set.
* Added hideDeleteButton prop to RX.TextInput for Windows UWP.
#### Version 1.3.0-rc.1 of reactxp - _13 Jun 2018_
* Fixed accessibility issues with screen reader in Windows UWP implementation.
* Removed shouldRasterizeIOS prop on RX.Image. It was never supported in RN, so it was just a no-op.
* Fixed bug in RX.Network.getType() which returned the wrong response if the device was connected to ethernet on Android.
#### Version 1.3.0-rc.0 of reactxp - _10 Jun 2018_
* Fixed a few bugs in keyboard focus handling for Windows UWP.
* Web implementation of RX.Image now uses credentials for image fetches if origin header is specified.
* Added support for text shadows in RX.Text (shadowColor, shadowOffset, shadowRadius style attributes).
* Fixed keyboard navigation logic for multi-root-view case.
* Wired up tooltips in web implementation of RX.Button, RX.Image, RX.Link, and RX.View (title prop).
* Extended RX.UserInterface.measureWindow to support multi-root-view case. It now takes an optional rootViewId parameter.
* Added drag and drop handler support for Mac OS.
* Added new static API RX.Image.getMetadata that returns dimensions of an image.
* Avoided the use of the deprecated RN.NetInfo "change" event in favor of "connectionChange" so RN doesn't emit warning.
* Added back parameter to onBlur event handler that was incorrectly removed in version 1.2.x.
#### Version 1.2.1 of reactxp - _24 May 2018_
* Wired up onPaste handler for Windows implementation.
* Fixed bug that resulted in cached popups to appear when they shouldn't.
* Fixed bug in web implementation of RX.ScrollView that resulted in incorrect screen reader announcements.
#### Version 1.2.0 of reactxp-imagesvg, reactxp-navigation, reactxp-video, reactxp-virtuallistview - _22 May 2018_
* Automatically suppressed system context menu for selectable RX.Text items on Windows if containing view has onContextMenu handler.
#### Version 1.2.0 of reactxp - _22 May 2018_
* Automatically suppressed system context menu for selectable RX.Text items on Windows if containing view has onContextMenu handler.
#### Version 1.1.2-rc.3 of reactxp - _21 May 2018_
* Fixed bug in RX.Linking.getInitialUrl, which was returning null rather than undefined for the url in some cases.
* Added support for Dilaog trait to trigger yes-dont-hide importantForAccessibility behavior in Windows implementation.
* Fixed UWP accessibility announce to make it act the same way as Android.
* Create correct Animated.TextInput for Windows.
#### Version 1.1.2-rc.2 of reactxp - _15 May 2018_
* Improved keyboard focus support. Added focus, blur and requestFocus methods to several existing components. Added new focusArbitrator prop to RX.View, which allows callback to arbitrate between multiple children that are requesting autofocus.
* Fixed bug that caused group view children to be invisible to UI automation.
* Improved keyboard support for Windows implementation.
#### Version 1.1.2-rc.1 of reactxp - _9 May 2018_
* Fixed bug in Windows implementation that prevented RX.Input.keyDownEvent from being dispatched.
* Fixed bugs in Windows implementation that reported wrong key codes for keyboard events.
* Fixed focus management for cacheable popups in Windows implementation.
* Fixed type definitions of GestureView, ScrollView and WebView. They were incorrectly extending ViewBase, which pulled in a number of unsupported props.
* Fixed bug that caused cached popups to appear on screen when not appropriate.
* Added onContextMenu prop to GestureView.
* Added accessibility trait for ListItem.
* In Windows implementation, use RNW.Hyperlink for rendering RX.Link.
* Removed "justifyEnd" prop from RX.ScrollView. It was never implemented as documented.
* Added useSafeInsets prop onR X.View to support rendering within safe area on iOS.
* Fixed bug RX.StatusBar.setBarStyle where animated parameter was not properly passed to RN.
* Added onContextMenu to RX.Link.
#### Version 1.1.1 of reactxp - _13 Apr 2018_
* Fixed reentrancy issue in popup support. Displaying a popup from within the onDismiss callback was recently broken.
* Added disabledOpacity prop for RX.Button component.
* Improved handling of RX.Clipboard.setText on web. It now properly handles carriage returns.
* Fixed TextInput focusability after focus restriction on Windows.
* Fixed accessibility issue related to voice over on web.
#### Version 1.0.18 of reactxp-navigation - _10 Apr 2018_
* Fixed bug in web implementation that caused a problem when popping multiple items from the navigation stack.
#### Version 0.2.10 of reactxp-imagesvg, 1.0.17 of reactxp-navigation, 0.2.5 of reactxp-video, 0.1.9 of reactxp-virtuallistview - _4 Apr 2018_
* Rebuilt using reactxp 1.1.0.
#### Version 1.1.0 of reactxp - _4 Apr 2018_
* Added workaround to enable the keyboard navigation mode when the screen reader is used.
#### Version 1.1.0-rc.2 of reactxp - _28 Mar 2018_
* Fixed bug in web implementation of animation where completion callback was called multiple times in some cases.
#### Version 1.1.0-rc.1 of reactxp - _26 Mar 2018_
* Added support for "cacheable" popups. This is useful for popups that involve many views and are costly to recreate from scratch.
* Fixed keyboard handler in RX.Link, it was defferring to base class only when onPress was defined, and that was wrong.
* Fixed bug in native implementation of TextInput where selection was sometimes lost.
* Fixed TextInput reference for focus control on windows.
* Implemented hidden scroll indicators in web/ScrollView.
#### Version 1.0.2 of reactxp - _16 Mar 2018_
* Fixed bug in web TextInput implementation that resulted in a console warning with the latest versions of ReactJS.
* Fixed bug in native Button implementation that resulted in corrupted styles.
* Fixed accessibility (screen reader) bug in web implementation.
#### Version 1.0.1 of reactxp - _5 Mar 2018_
* Moved event handlers from ViewProps to ViewPropsShared so AnimatedView has them too.
* Fixed recent regression in handling of popups in native implementation.
* Events passed to onPress and onLongPress now include touch or mouse coordinates.
#### Version 0.2.9 of reactxp-imagesvg, 1.0.16 of reactxp-navigation, 0.2.4 of reactxp-video, 0.1.8 of reactxp-virtuallistview - _2 Mar 2018_
* Rebuilt using reactxp 1.0.0 and latest typescript compiler version.
#### Version 1.0.0 of reactxp - _2 Mar 2018_
* Filled in fields for MouseEvent on web.
* Fixed a few style leaks in web implementation.
#### Version 1.0.0-rc.1 of reactxp - _28 Feb 2018_
* Improved accessibility handling for Button.
* Added select() method to RX.Platform namespace to make it easier to implement platform-specific behavior.
* Improved accessibility performance.
* Improved performance by avoiding triggering synchronous layout on web.
* Fixed behavior of onContextMenu.
* Removed unused "type" field from RX.CommonProps interface.
* Updated typescript compiler to 2.7.2 and enabled strictPropertyInitialization.
* Added ability to set limitFocusWithin without automatically setting aria-hidden=true on web.
* Implemented RX.Linking APIs for Windows UWP platform.
* Removed "currentTarget" from SyntheticEvent.
* Added coordinates/modifiers/button information to MouseEvent definition.
#### Version 1.0.0-alpha.2 of reactxp - _21 Feb 2018_
* Switched to new versioning scheme that's independent of RN.
* Updated default RN dependency from 0.51.x to 0.53.x, although backward compatibility is maintained.
* Added support for numeric keyboards in mobile browsers.
* Fixed announceForAccessibility API for Mac.
* Added MacOS implementation of Button and Animated.
* Added valuenow attribute for slider role support.
* Fixed bug in web implementation of Animated where it wasn't properly managing listener subscriptions when animated styles were added to (or removed from) an animated component.
* Fix random Android crashes when Talkback is enabled.
* Fixed bug in web animation that caused certain CSS properties not to animate correctly because transition was specifying the attribute using camel case rather than CSS (hyphenated) case. This affected attributes like "backgroundColor".
* Removed parameter from onBlur event.
* Fixed bug in RX.TextInput that affected Windows version: Pass selection in render only after explicitly set.
* Added right-click support for Windows platform.
* Added more accessibility support for Windows platform.
* Use white-space:pre for the aria-live region.
#### Version 0.51.1 of reactxp - _19 Jan 2018_
* Fixed regression in native implementation of Animated.View's blur() method.
#### Version 0.2.3 of reactxp-video, 1.0.15 of reactxp-navigator, 0.2.8 of reactxp-imagesvg, 0.1.7 of reactxp-virtuallistview - _18 Jan 2018_
* Updated for RN 0.51 compatibility.
#### Version 0.51.0 of reactxp - _18 Jan 2018_
* Fixed focusable View condition for VoiceOver in web implementation.
* Exposed web-specific ariaRoleDescription prop to work around other VoiceOver issues.
* Updated RN for Windows UWP dependency.
* Fixed recent regression that broke focus and blur calls in iOS and Android implementations of AnimatedTextInput.
#### Version 0.51.0-alpha.9 of reactxp - _17 Jan 2018_
* Removed console error related to animations of values not currently associated with any mounted component. It was too noisy.
#### Version 0.51.0-alpha.8 of reactxp - _16 Jan 2018_
* Fixed bug in Windows UWP implementation related to selection ranges in TextInput.
* Fixed screen reader issue in Mac implementation.
* Removed ```textAlign``` prop from TextInput. It was extraneous, since it's already supported as a style attribute.
* Updated Windows UWP dependency to use the latest version of RN for UWP.
* Worked around issue with screen readers on Chrome browsers.
* Added currentTarget field back to SyntheticEvent.
* Added support for "switch" aria type (web implementation).
* Fixed recent regression in web animation code.
#### Version 0.51.0-alpha.5 of reactxp - _11 Jan 2018_
* Eliminated limitation within RX.Button where only one child element was allowed.
* Fixed regression where native implementation of RX.View with onPress handler didn't provide touch feedback.
#### Version 0.51.0-alpha.4 of reactxp - _10 Jan 2018_
* Added code to native implementation of RX.Network so it works with versions of RN before and after 0.48.x.
* Fixed regression in web implementation of RX.TextInput.
* Fixed bug that caused incorrect behavior of RX.Modal on native platforms.
#### Version 0.51.0-alpha.2 of reactxp - _9 Jan 2018_
* Fixed bug in native implementation of RX.ActivityIndicator. It wasn't properly handling the delay prop.
* Added support in web implementation of RX.TextInput for custom keyboard types on mobile web browsers.
* Added focus and keyboard navigation support for native UWP platform.
* Added support for injection of HTML content into RX.WebView.
* Added support for postMessage and onMessage handler in RX.WebView for bidirectional communication.
#### Version 0.51.0-alpha.1 of reactxp - _6 Jan 2018_
* Fixed break in device dimension change event due to change in RN.
* Removed use of RX.Button implementation within RX.View for native-common implementation.
* Fixed bug in web implementation of RX.Clipboard.getText. It shouldn't throw.
* Fixed timing bug in web implementation of GestureView.
* Fixed popup positioning for right-to-left languages in native-common implementation.
* Fixed bug in web implementation of TextInput that caused assertion in React.
* Breaking change: Removed 'cursor' prop from RX.Button. It was redundant with the 'cursor' style.
* New feature: RX.Input.key[Up|Down]Event now allow event subscribers to cancel the event.
* New feature: Changed RX.Modal.isDisplayed to accept undefined parameter, in which case it determines whether _any_ modal is displayed.
* New feature: Added RX.Popup.isDisplayed method.
* Reimplemented web implementation of animation APIs. Removed many limitations of previous implementation and fixed several bugs. Documented remaining limitations.
* Fixed bug in web implementation of RX.TextInput that resulted in assertions within React.
* Fixed bug in web implementation of RX.Picker. It wasn't correctly combining styles.
* Added support in web implementation of RX.TextInput for keyboard type (applicable on mobile web browsers).
* Removed use of deprecated RX.NetInfo.fetch method.
#### Version 0.46.6 of reactxp - _13 Dec 2017_
* Fixed potential crash in web implementation of RX.ScrollView.
* Fixed bug in UWP implementation of RX.Popup, allowing background to be clickable.
* In web implementation of RX.ScrollView, added support for clicking on scroll bar to adjust position of thumb.
* Added dev warning when using nested RX.Button items.
* Fixed a potential crash in web implementation of RX.GestureView.
* Implemented drag-and-drop support in UWP implementation of RX.View.
#### Version 0.46.5 of reactxp - _31 Oct 2017_
* Added Android "mode" prop to Picker.
* Added type definitions for RX.Stateless and RX.ComponentBase.
* Updated to React 16.0.0 and React-Dom 16.0.0.
* Added RX.UserInterface.registerRootView API to allow registration of secondary views. Also added rootViewId option to RX.Modal and RX.Popup so they can be displayed on secondary views.
* Fixed bug in focus restoration on web implementation.
* Replaced use of deprecated BackAndroid with BackHandler, avoiding deprecation warnings.
* Fixed bug in RX.Button RN implementation where opacity was not property restored after changing disabled prop.
#### Version 0.46.3 of reactxp - _7 Oct 2017_
* Added missing focus() method to RX.Animated.View interface.
* Exported RX.AnimatedImage, RX.AnimatedText, RX.AnimatedTextInput, and RX.AnimatedView.
* Added accessibilityLiveRegion prop to ViewProps for Android and web.
* Exported explicit types for ShadowOffset, ScrollIndicatorInsets.
* Fixed crash in web RootView when clicking on a popup anchor.
* Fixed race condition in hover of Button on web.
#### Version 0.2.2 of reactxp-video, 1.0.13 of reactxp-navigator, 0.2.7 of reactxp-imagesvg, 0.1.6 of reactxp-virtuallistview - _21 Sep 2017_
* Updated for RN 0.46 compatibility.
* Removed custom react.d.ts and react-dom.d.ts files in favor of public versions.
#### Version 0.46.2 of reactxp - _21 Sep 2017_
* Added onContextMenu support on web for Text components.
* Exposed attributes to make menus and listboxes accessible on web.
* Added new RX.Animated.createValue and RX.Animated.interpolate methods. The old way of instantiating a value and creating an interpolation will be deprecated going forward.
* Made a breaking change to Alert.show interface - combined optional parameters into an AlertOptions interface. This will allow for better extensibility in the future.
* Updated RN dependency to 0.46.
* Removed custom react.d.ts and react-dom.d.ts files in favor of public versions.
#### Version 0.46.0_rc.2 of reactxp - _19 Sep 2017_
* Changed RX.Link props to make url mandatory.
* Added new Alert implementation for web. It now presents a modal-based themable dialog box.
* Fixed bug in ScrollView styles from previous release.
* Exposed aria-checked property on button type for web.
* Added key attribute to KeyboardEvent.
* Fixed bug in web code where onScrollBeginDrag and onScrollEndDrag were called unconditionally even if they were undefined.
* Enabled strict null checks in TS compiler and fixed a number of bugs that were exposed.
#### Version 0.46.0_rc.1 of reactxp - _5 Sep 2017_
* First pre-release version of 0.46.
* Removed Navigator component and moved to an extension.
* Fixed style definition for ScrollView so it doesn't include child-related flexbox styles, which aren't supported.
#### Version 0.42.0 of reactxp - _5 Sep 2017_
* Removed rc from version.
#### Version 0.42.0_rc.25 of reactxp - _18 Aug 2017_
* On web, if there's a queued onScroll event when the scroll position is manually set, cancel the onScroll.
#### Version 0.42.0_rc.24 of reactxp - _9 Aug 2017_
* Added accessibility support for GestureView.
* Fixed bug in web version that prevented animated fontSize from working.
#### Version 0.42.0_rc.22 of reactxp - _30 July 2017_
* On RN platforms, initialProps is now passed to the main view.
* Added new PopupOptions field preventDismissOnPress that prevents the popup from being dismissed implicitly when the user clicks or taps outside of the popup or the anchor.
* Fixed bug in web implementation of TextInput where border styling was not honored.
* Fixed bug on Android that allowed presses to background of Modal to go through.
#### Version 0.42.0_rc.20 of reactxp - _15 July 2017_
* Updated to TypeScript 2.4, which caught several bugs in the ReactXP code.
* Made Styles.combine much more flexible - it now supports arbitrarily nested arrays of styles.
* Changed Network API namespace for detecting network type so it's consistent with other ReactXP APIs. Added documentation.
#### Version 0.1.6 of reactxp-video - _15 July 2017_
* Updated to TypeScript 2.4 and made changes to work with latest ReactXP core.
#### Version 0.2.4 of reactxp-imagesvg - _15 July 2017_
* Updated to TypeScript 2.4 and made changes to work with latest ReactXP core.
#### Version 0.42.0_rc.18 of reactxp - _13 July 2017_
* Fixed runtime crash when running web implementation in Electron.
* Added a way to provide screen reader focus to TextInput on web.
#### Version 0.42.0_rc.17 of reactxp - _4 July 2017_
* Added ability to set accessibility focus for text input controls.
* Added support for iOS-specific ActivationState for RN extensions.
#### Version 0.42.0_rc.16 of reactxp - _30 June 2017_
* Fixed another bug in handling of default border width on web.
* Added setFocusRestricted and setFocusLimited methods and support for nested keyboard focus on web.
* Added isNavigatingWithKdyboard method and keyboardNavigationEvent.
#### Version 0.42.0_rc.12 of reactxp - _16 June 2017_
* Added support for new limitFocusWidth prop for constraining keyboard focus.
* Fixed bug in handling of mailto URLs on web in the Link component.
* Added support for flexGrow, flexShrink, and flexBasis props.
#### Version 0.42.0_rc.11 of reactxp - _13 June 2017_
* Fixed bugs in web implementation of focus manager.
* Added new API (enableTouchLatencyEvents) and event (touchLatencyEvent) in UserInterface namespace for detecting delays in touch event handling.
* Fixed crash in native implementation of Link component that resulted in uncaught exception for some types of links.
* Fixed flexDirection style default for web implementation in Image component.
* Fixed inconsistency in handling of borders between web and RN when borderStyle is not specified.
#### Version 0.42.0_rc.10 of reactxp - _25 May 2017_
* Added new International API namespace for controlling right-to-left mirroring behavior.
#### Version 0.1.2 of reactxp-virtualistview - _23 May 2017_
* Republished because index files were missing in previous publish.
#### Version 0.42.0_rc.9 of reactxp - _17 May 2017_
* Fixed bug in Navigator that caused crash in hello-world sample.
#### Version 0.42.0_rc.8 of reactxp - _16 May 2017_
* Removed Profiling API namespace and dependency on react-addons-perf.
#### Version 0.1.1 of reactxp-virtualistview - _11 May 2017_
* Republished because "dist" directory was missing in previous publish.
#### Version 0.1.2 of reactxp-video - _11 May 2017_
* Published first version of reactxp-video extension.
#### Version 0.2.0 of reactxp-imagesvg - _10 May 2017_
* Switched from old version of react-native-art-svg to latest version of react-native-svg.
#### Version 0.42.0_rc.5 of reactxp - _10 May 2017_
* Fixed incorrect import path (using wrong case).
* Fixed bug in native View implementation - was using stale props.
* Added importantForLayout prop on View (web specific).
* Fixed bug in native NavigatorExperimentalDelegate - was using wrong props.
#### Version 0.42.0_rc.4 of reactxp - _27 Apr 2017_
* Changed web implementation of Text to prevent copying text to clipboard.
* Eliminated the need to specify box-sizing CSS in external CSS file.
* Fixed accessibility focus bugs.
#### Version 0.42.0_rc.3 of reactxp - _26 Apr 2017_
* Added onLongPress prop for Link.
* Fixed accessibility bug relating to Modal dialogs.
#### Version 0.42.0_rc.2 of reactxp - _18 Apr 2017_
* Added missing box-sizing CSS directives for web.
* Fixed bug in native implementation of View related to accessibility.
#### Version 0.1.0 of reactxp-imagesvg - _26 Apr 2017_
* Published first version of reactxp-imagesvg extension.
#### Version 0.42.0_rc.1 of reactxp - _9 Apr 2017_
* Updated project to use recent versions of React (15.5.3) and React Native (0.42.3).
#### Version 0.34.3 of reactxp - _7 Apr 2017_
* Added new props to ScrollView. Updated package.json to properly reflect peerDependencies on react and react-native.
#### Version 0.34.1 of reactxp - _6 Apr 2017_
* This is the initial public release of the ReactXP core library. It is built against React Native 0.34. We are working on updating it to a newer version of React Native, and this will be released shortly.
================================================
FILE: extensions/README.md
================================================
# ReactXP Extensions
The ReactXP library is designed to be as lightweight as possible, including only those cross-platform APIs and "primitive" components that are required by almost every app. Functionality that is needed less commonly is provided in the form of optional extensions.
There are two distinct types of extensions.
1. Additional APIs or "primitive" components that have separate implementations for each of the supported platforms.
2. Higher-level components that contain no platform-specific code but build upon the lower-level primitives to provide new (typically more complex) functionality.
================================================
FILE: extensions/imagesvg/.eslintrc.json
================================================
{
"parserOptions": {
"project": "./tsconfig.json"
},
"extends": ["skype", "skype/react"],
"rules": {},
"overrides": [
{
"files": [
"*.tsx"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off"
}
},
{
"files": [
"PluginBaseChecker.ts"
],
"rules": {
"@typescript-eslint/no-unused-vars": "off"
}
}
]
}
================================================
FILE: extensions/imagesvg/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
package-lock.json
# Optional npm cache directory
.npm
# Build artifacts
**/dist
# Miscellaneous user files
*.user
.vscode
.DS_STORE
================================================
FILE: extensions/imagesvg/.npmignore
================================================
/node_modules
/src/.vs
/src/bin
/src/obj
*.user
================================================
FILE: extensions/imagesvg/README.md
================================================
# reactxp-imagesvg
This module provides cross-platform support for SVG (Scalable Vector Graphics) within the [ReactXP](https://microsoft.github.io/reactxp/) library.
## Documentation
For detailed documentation, look [here](https://microsoft.github.io/reactxp/docs/extensions/imagesvg.html).
### Prerequisites
* [ReactXP](https://github.com/microsoft/reactxp/)
## Contributing
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
You must sign a Contribution License Agreement (CLA) before your PR will be merged. This a one-time requirement for Microsoft projects in GitHub. You can read more about [Contribution License Agreements (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) on Wikipedia. You can sign the Microsoft Contribution License Agreement by visiting https://cla.microsoft.com/. Use your GitHub account to login.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
================================================
FILE: extensions/imagesvg/index.android.js
================================================
'use strict';
module.exports = require('./dist/android/PluginBase.js');
================================================
FILE: extensions/imagesvg/index.ios.js
================================================
'use strict';
module.exports = require('./dist/ios/PluginBase.js');
================================================
FILE: extensions/imagesvg/index.js
================================================
'use strict';
// Export web by default. Other platforms have custom index.[platform].js files
module.exports = require('./dist/web/PluginBase.js');
================================================
FILE: extensions/imagesvg/index.macos.js
================================================
'use strict';
module.exports = require('./dist/macos/PluginBase.js');
================================================
FILE: extensions/imagesvg/index.windows.js
================================================
'use strict';
module.exports = require('./dist/windows/PluginBase.js');
================================================
FILE: extensions/imagesvg/package.json
================================================
{
"name": "reactxp-imagesvg",
"version": "2.0.0",
"description": "Plugin for ReactXP that provides support for SVG (scalable vector graphics) for all platforms",
"author": "ReactXP Team ",
"license": "MIT",
"scripts": {
"build": "tsc",
"lint": "eslint --ext .ts,.tsx src",
"lint:fix": "npm run lint -- --fix"
},
"dependencies": {
"react-native-svg": "^9.13.3"
},
"peerDependencies": {
"react": "^16.3",
"reactxp": "^2.0.0",
"react-dom": "^16.3",
"react-native": ">=0.57",
"react-native-windows": "^0.57.1"
},
"devDependencies": {
"@types/react-native": "^0.60.23",
"@typescript-eslint/eslint-plugin": "2.15.0",
"eslint": "6.8.0",
"eslint-config-skype": "1.5.0",
"eslint-plugin-import": "2.19.1",
"eslint-plugin-react": "7.17.0",
"reactxp": "^2.0.0",
"typescript": "3.7.4"
},
"types": "dist/web/PluginBase.d.ts"
}
================================================
FILE: extensions/imagesvg/src/android/PluginBase.tsx
================================================
/*
* PluginBase.ts
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the Android implementation of the plugin.
*/
import * as Types from '../common/Types';
import ImageSvg from '../native-common/ImageSvg';
import SvgPath from '../native-common/SvgPath';
import SvgRect from '../native-common/SvgRect';
export { ImageSvg as default, SvgPath, SvgRect, Types };
================================================
FILE: extensions/imagesvg/src/common/Interfaces.ts
================================================
/*
* Interfaces.ts
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Interface exposed by ImageSvg component.
*/
import * as React from 'react';
import * as Types from './Types';
export abstract class ImageSvg extends React.Component {
}
export abstract class SvgPath extends React.Component {
}
export abstract class SvgRect extends React.Component {
}
export interface PluginInterface {
Types: typeof Types;
default: typeof ImageSvg;
SvgPath: typeof SvgPath;
SvgRect: typeof SvgRect;
}
================================================
FILE: extensions/imagesvg/src/common/PluginBaseChecker.ts
================================================
/*
* PluginBaseChecker.ts
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Type check all the pluginbase exports against the desired interface.
*/
import * as AndroidPlugin from '../android/PluginBase';
import * as iOSPlugin from '../ios/PluginBase';
import * as macOSPlugin from '../macos/PluginBase';
import * as WebPlugin from '../web/PluginBase';
import * as WindowsPlugin from '../windows/PluginBase';
import * as Interfaces from './Interfaces';
const _typeCheckerAndroid: Interfaces.PluginInterface = AndroidPlugin;
const _typeCheckeriOS: Interfaces.PluginInterface = iOSPlugin;
const _typeCheckermacOS: Interfaces.PluginInterface = macOSPlugin;
const _typeCheckerWeb: Interfaces.PluginInterface = WebPlugin;
const _typeCheckerWindows: Interfaces.PluginInterface = WindowsPlugin;
================================================
FILE: extensions/imagesvg/src/common/Types.ts
================================================
/*
* Types.ts
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Type definitions to support the plugin.
*/
import { Types as RXTypes } from 'reactxp';
import * as React from 'react';
export interface ImageSvgStyle extends RXTypes.ViewStyle {
}
export declare type ImageSvgStyleRuleSet = RXTypes.StyleRuleSet;
export interface SvgCommonProps {
key?: string | number;
strokeColor?: string;
strokeWidth?: number;
strokeOpacity?: number;
fillColor?: string;
fillOpacity?: number;
}
export interface ImageSvgProps extends SvgCommonProps, RXTypes.CommonStyledProps {
children?: RXTypes.ReactNode;
height: number;
width: number;
accessibilityLabel?: string;
title?: string;
viewBox?: string;
preserveAspectRatio?: string;
webShadow?: string;
}
export interface SvgPathProps extends SvgCommonProps {
d?: string;
}
export interface SvgRectProps extends SvgCommonProps {
width: number;
height: number;
x: number;
y: number;
}
export class ImageSvg extends React.Component {}
================================================
FILE: extensions/imagesvg/src/common/assert.ts
================================================
/**
* assert
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
*/
const assert = (cond: any, message?: string | undefined): void => {
if (!cond) {
throw new Error(message || 'Assertion Failed');
}
};
export default assert;
================================================
FILE: extensions/imagesvg/src/ios/PluginBase.tsx
================================================
/*
* PluginBase.ts
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the iOS implementation of the plugin.
*/
import * as Types from '../common/Types';
import ImageSvg from '../native-common/ImageSvg';
import SvgPath from '../native-common/SvgPath';
import SvgRect from '../native-common/SvgRect';
export { ImageSvg as default, SvgPath, SvgRect, Types };
================================================
FILE: extensions/imagesvg/src/macos/PluginBase.tsx
================================================
/*
* PluginBase.ts
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the iOS implementation of the plugin.
*/
import * as Types from '../common/Types';
import ImageSvg from '../native-common/ImageSvg';
import SvgPath from '../native-common/SvgPath';
import SvgRect from '../native-common/SvgRect';
export { ImageSvg as default, SvgPath, SvgRect, Types };
================================================
FILE: extensions/imagesvg/src/native-common/ImageSvg.tsx
================================================
/**
* ImageSvg.tsx
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* RN-specific implementation of the cross-platform abstraction for
* SVG (scalable vector graphics) images.
*/
import * as React from 'react';
import * as RNSvg from 'react-native-svg';
import assert from '../common/assert';
import { ImageSvgProps } from '../common/Types';
export class ImageSvg extends React.Component {
render() {
assert(this.props.width && this.props.height, 'The width and height on imagesvg are mandatory.');
if (this.props.width > 0 && this.props.height > 0) {
return (
{ this.props.children }
);
}
return null;
}
}
export default ImageSvg;
================================================
FILE: extensions/imagesvg/src/native-common/SvgPath.tsx
================================================
/**
* SvgPath.tsx
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* RN-specific implementation of the cross-platform abstraction for
* SVG Path elements.
*/
import * as React from 'react';
import * as RNSvg from 'react-native-svg';
import { SvgPathProps } from '../common/Types';
export class SvgPath extends React.Component {
render() {
return (
);
}
}
export default SvgPath;
================================================
FILE: extensions/imagesvg/src/native-common/SvgRect.tsx
================================================
/**
* SvgPath.tsx
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* RN-specific implementation of the cross-platform abstraction for
* SVG Rect elements.
*/
import * as React from 'react';
import * as RNSvg from 'react-native-svg';
import { SvgRectProps } from '../common/Types';
export class SvgRect extends React.Component {
render() {
return (
);
}
}
export default SvgRect;
================================================
FILE: extensions/imagesvg/src/typings/react-native-svg.d.ts
================================================
/**
* react-native-svg.d.ts
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Type definition file for the React Native SVG module.
* https://github.com/react-native-community/react-native-svg
*/
declare module 'react-native-svg' {
import * as React from 'react';
import * as RN from 'react-native';
type ArrayType = [string] | [number];
interface SvgProps extends RN.ComponentPropsBase {
height?: string;
width?: string;
viewBox?: string;
opacity?: number;
preserveAspectRatio?: string;
style?: RN.StyleRuleSet;
}
interface BaseProps extends RN.ComponentPropsBase {
fill?: string; // The fill prop refers to the color inside the shape.
fillOpacity?: number; // This prop specifies the opacity of the color or the content the current object is filled with.
stroke?: string; // The stroke prop controls how the outline of a shape appears.
strokeWidth?: number; // The strokeWidth prop specifies the width of the outline on the current object.
strokeOpacity?: number; // The strokeOpacity prop specifies the opacity of the outline on the current object.
x?: number;
y?: number;
strokeLinecap?: string; // oneOf(['butt', 'square', 'round']),
strokeCap?: string; // .oneOf(['butt', 'square', 'round']),
strokeLinejoin?: string; // oneOf(['miter', 'bevel', 'round']),
strokeJoin?: string; // .oneOf(['miter', 'bevel', 'round']),
strokeDasharray?: ArrayType;
scale?: number; // Scale value on the current object.
rotate?: number; // Rotation degree value on the current object.
originX?: number; // Transform originX coordinates for the current object.
originY?: number; // Transform originY coordinates for the current object.
}
interface TransformProps extends BaseProps {
scaleX: number;
scaleY: number;
transform: number;
}
interface PathProps extends BaseProps {
d: string;
}
interface RectProps extends BaseProps {
width?: number;
height?: number;
}
interface TextProps extends BaseProps {
textAnchor: ('start' | 'middle' | 'end');
fontFamily: string;
fontSize: number;
}
export class Svg extends React.Component { }
export class Path extends React.Component { }
export class Rect extends React.Component { }
export class Text extends React.Component { }
}
================================================
FILE: extensions/imagesvg/src/web/ImageSvg.tsx
================================================
/*
* ImageSvg.tsx
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Web-specific implementation of the cross-platform abstraction for
* SVG (scalable vector graphics) images.
*/
import * as React from 'react';
import { Styles as RXStyles } from 'reactxp';
import assert from '../common/assert';
import { ImageSvgProps } from '../common/Types';
export class ImageSvg extends React.Component {
render() {
assert(this.props.width && this.props.height, 'The width and height on imagesvg are mandatory.');
if (this.props.width > 0 && this.props.height > 0) {
const combinedStyles = RXStyles.combine([{
display: 'flex',
position: 'relative',
} as any, this.props.style]);
if (this.props.fillColor !== undefined) {
combinedStyles.fill = this.props.fillColor;
}
if (this.props.fillOpacity !== undefined) {
combinedStyles.fillOpacity = this.props.fillOpacity.toString();
}
if (this.props.strokeColor !== undefined) {
combinedStyles.stroke = this.props.strokeColor;
}
if (this.props.strokeOpacity !== undefined) {
combinedStyles.strokeOpacity = this.props.strokeOpacity.toString();
}
if (this.props.strokeWidth !== undefined) {
combinedStyles.strokeWidth = this.props.strokeWidth.toString();
}
if (combinedStyles.flex === undefined) {
combinedStyles.flex = '0 0 auto';
}
if (combinedStyles.overflow === undefined) {
combinedStyles.overflow = 'hidden';
}
if (this.props.webShadow) {
const aliases = RXStyles.getCssPropertyAliasesCssStyle();
if (this._isFilterDropShadowSupported()) {
const filterAlias = aliases.filter || 'filter';
combinedStyles[filterAlias] = 'drop-shadow(' + this.props.webShadow + ')';
}
}
return (
);
} else {
return null;
}
}
private _isFilterDropShadowSupported() {
// Edge (actually, Windows 10) contains a bug where it renders this incorrectly.
// We'll disable it specifically on Edge browsers for now.
// Safari also has a bug where it renders this incorrectly (transparent background),
// so we'll disable it there also.
// For now, enable it in Chrome only.
let isChrome = window.hasOwnProperty('chrome');
// The latest versions of Edge implement the "chrome" global variable (presumably
// for compatibility with Chrome browser), so we need to do one more check
// to make sure it's really chrome.
if (isChrome && navigator.appName === 'Netscape' && navigator.appVersion.indexOf('Edge') >= 0) {
isChrome = false;
}
return isChrome;
}
}
export default ImageSvg;
================================================
FILE: extensions/imagesvg/src/web/PluginBase.ts
================================================
/*
* PluginBase.ts
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the Web implementation of the plugin.
*/
import * as Types from '../common/Types';
import ImageSvg from './ImageSvg';
import SvgPath from './SvgPath';
import SvgRect from './SvgRect';
export { ImageSvg as default, SvgPath, SvgRect, Types };
================================================
FILE: extensions/imagesvg/src/web/SvgPath.tsx
================================================
/*
* SvgPath.tsx
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Web-specific implementation of the cross-platform abstraction for
* SVG Path elements.
*/
import * as React from 'react';
import { SvgPathProps } from '../common/Types';
export class SvgPath extends React.Component {
render() {
return (
);
}
}
export default SvgPath;
================================================
FILE: extensions/imagesvg/src/web/SvgRect.tsx
================================================
/*
* SvgRect.tsx
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Web-specific implementation of the cross-platform abstraction for
* SVG Rect elements.
*/
import * as React from 'react';
import { SvgRectProps } from '../common/Types';
export class SvgRect extends React.Component {
render() {
return (
);
}
}
export default SvgRect;
================================================
FILE: extensions/imagesvg/src/windows/ImageSvg.tsx
================================================
/**
* ImageSvg.tsx
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* RN-specific implementation of the cross-platform abstraction for
* SVG (scalable vector graphics) images.
*/
import * as React from 'react';
import { ImageSvgProps } from '../common/Types';
// TODO: #694092 Not implemented
export class ImageSvg extends React.Component {
render(): any {
return null;
}
}
export default ImageSvg;
================================================
FILE: extensions/imagesvg/src/windows/PluginBase.ts
================================================
/*
* PluginBase.ts
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the UWP implementation of the plugin.
*/
import * as Types from '../common/Types';
import ImageSvg from './ImageSvg';
import SvgPath from './SvgPath';
import SvgRect from './SvgRect';
export { ImageSvg as default, SvgPath, SvgRect, Types };
================================================
FILE: extensions/imagesvg/src/windows/SvgPath.tsx
================================================
/*
* SvgPath.tsx
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* RN-specific implementation of the cross-platform abstraction for
* SVG Path elements.
*/
import * as React from 'react';
import { SvgPathProps } from '../common/Types';
// TODO: #694092 Not implemented
export class SvgPath extends React.Component {
render(): any {
return null;
}
}
export default SvgPath;
================================================
FILE: extensions/imagesvg/src/windows/SvgRect.tsx
================================================
/*
* SvgRect.tsx
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* RN-specific implementation of the cross-platform abstraction for
* SVG Rect elements.
*/
import * as React from 'react';
import { SvgRectProps } from '../common/Types';
// TODO: #694092 Not implemented
export class SvgRect extends React.Component {
render(): any {
return null;
}
}
export default SvgRect;
================================================
FILE: extensions/imagesvg/tsconfig.json
================================================
{
"compilerOptions": {
"declaration": true,
"noResolve": false,
"jsx": "react",
"module": "commonjs",
"target": "es5",
"experimentalDecorators": true,
"noImplicitAny": true,
"outDir": "dist/",
"skipLibCheck": true
},
"include": [
"src/**/*"
],
"exclude": [
"dist",
"node_modules"
]
}
================================================
FILE: extensions/navigation/.eslintrc.json
================================================
{
"parserOptions": {
"project": "./tsconfig.json"
},
"extends": ["skype", "skype/react"],
"rules": {
"no-console": [
"error",
{
"allow": [
"error"
]
}
]
},
"overrides": [
{
"files": [
"*.tsx"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off"
}
},
{
"files": [
"Navigator.tsx"
],
"rules": {
"no-duplicate-imports": "off"
}
},
{
"files": [
"*.d.ts"
],
"rules": {
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/member-naming": "off"
}
}
]
}
================================================
FILE: extensions/navigation/.gitignore
================================================
dist
node_modules
.DS_Store
package-lock.json
================================================
FILE: extensions/navigation/.npmignore
================================================
!dist
================================================
FILE: extensions/navigation/README.md
================================================
# reactxp-navigation
This module provides cross-platform navigation support [ReactXP](https://microsoft.github.io/reactxp/) library.
## Documentation
For detailed documentation, look [here](https://microsoft.github.io/reactxp/docs/extensions/navigator.html).
### Prerequisites
* [ReactXP](https://github.com/microsoft/reactxp/)
## Contributing
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
You must sign a Contribution License Agreement (CLA) before your PR will be merged. This a one-time requirement for Microsoft projects in GitHub. You can read more about [Contribution License Agreements (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) on Wikipedia. You can sign the Microsoft Contribution License Agreement by visiting https://cla.microsoft.com/. Use your GitHub account to login.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
================================================
FILE: extensions/navigation/index.android.js
================================================
'use strict';
module.exports = require('./dist/native-common/Navigator');
================================================
FILE: extensions/navigation/index.ios.js
================================================
'use strict';
module.exports = require('./dist/native-common/Navigator');
================================================
FILE: extensions/navigation/index.js
================================================
'use strict';
// Export web by default. Other platforms have custom index.[platform].js files
module.exports = require('./dist/web/Navigator');
================================================
FILE: extensions/navigation/index.macos.js
================================================
'use strict';
module.exports = require('./dist/native-common/Navigator');
================================================
FILE: extensions/navigation/index.windows.js
================================================
'use strict';
module.exports = require('./dist/native-common/Navigator');
================================================
FILE: extensions/navigation/package.json
================================================
{
"name": "reactxp-navigation",
"version": "2.0.0",
"description": "Plugin for ReactXP that provides a navigation framework",
"scripts": {
"build": "tsc",
"lint": "eslint --ext .ts,.tsx src",
"lint:fix": "npm run lint -- --fix"
},
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/reactxp/tree/master/extensions/navigation"
},
"keywords": [
"react-native",
"reactxp",
"react"
],
"dependencies": {
"lodash": "^4.17.15",
"rebound": "^0.1.0",
"reactxp-experimental-navigation": "1.0.14"
},
"peerDependencies": {
"react": "^16.3.0",
"reactxp": "^2.0.0",
"react-dom": "^16.3.0",
"react-native": ">=0.57",
"react-native-windows": "^0.57.1"
},
"devDependencies": {
"@types/lodash": "^4.14.149",
"@types/react-dom": "^16.9.4",
"@types/react-native": "^0.60.23",
"@typescript-eslint/eslint-plugin": "2.15.0",
"eslint": "6.8.0",
"eslint-config-skype": "1.5.0",
"eslint-plugin-import": "2.20.0",
"eslint-plugin-react": "7.17.0",
"react": "^16.3.0",
"react-dom": "^16.3.0",
"reactxp": "^2.0.0",
"typescript": "3.7.4"
},
"author": "ReactXP Team ",
"license": "MIT",
"types": "dist/web/Navigator.d.ts"
}
================================================
FILE: extensions/navigation/src/common/Types.ts
================================================
/**
* Types.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
* Type definitions for reactxp-naviigation extension.
*/
// Use only for type data
import * as React from 'react';
import * as RX from 'reactxp';
export type ReactNode = React.ReactNode;
//
// Navigator
// ----------------------------------------------------------------------
export enum NavigatorSceneConfigType {
FloatFromRight,
FloatFromLeft,
FloatFromBottom,
Fade,
FadeWithSlide
}
export interface NavigatorRoute {
routeId: number;
// Route's animation configuration
sceneConfigType: NavigatorSceneConfigType;
// NOTE: The following props are for the experimental navigator.
// They aren't considered when working with the standard navigator.
// Optional gesture response distance override
// 0 is equivalent to disabling gestures
gestureResponseDistance?: number;
// Optional custom scene config
customSceneConfig?: CustomNavigatorSceneConfig;
}
// NOTE: Experimental navigator only
export interface NavigationTransitionSpec {
duration?: number;
// NOTE: Elastic and bounce easing will not work as expected due to how the navigator interpolates styles
easing?: RX.Types.Animated.EasingFunction;
}
// NOTE: Experimental navigator only
export interface NavigationTransitionStyleConfig {
// By default input range is defined as [index - 1, index, index + 1];
// Input and output ranges must contain the same number of elements
inputRange?: number[];
opacityOutput: number | number[];
scaleOutput: number | number[];
translateXOutput: number | number[];
translateYOutput: number | number[];
}
// NOTE: Experimental navigator only
export interface CustomNavigatorSceneConfig {
// Optional transition styles
transitionStyle: (sceneIndex: number, sceneDimensions: RX.Types.Dimensions) => NavigationTransitionStyleConfig;
// Optional overrides for duration, easing, and timing
transitionSpec?: NavigationTransitionSpec;
// Optional cardStyle override
cardStyle?: RX.Types.ViewStyleRuleSet;
// Optionally hide drop shadow
hideShadow?: boolean;
// Optionally flip the visual order of the last two scenes
presentBelowPrevious?: boolean;
}
export interface NavigatorProps extends RX.CommonProps {
renderScene: (route: NavigatorRoute) => JSX.Element | null;
navigateBackCompleted?: () => void;
// NOTE: Arguments are only passed to transitionStarted by the experimental navigator
transitionStarted?: (progress?: RX.Types.AnimatedValue,
toRouteId?: string,
fromRouteId?: string,
toIndex?: number,
fromIndex?: number) => void;
transitionCompleted?: () => void;
cardStyle?: RX.Types.ViewStyleRuleSet;
children?: ReactNode;
// Selector of the navigator delegate. Currently make difference only in react-native.
delegateSelector?: NavigatorDelegateSelector;
}
export enum CommandType {
Push,
Pop,
Replace
}
export interface CommandParam {
route?: NavigatorRoute;
value?: number;
}
export interface NavigationCommand {
type: CommandType;
param: CommandParam;
}
// Empty state
export interface NavigatorState {
}
export abstract class Navigator extends React.Component {
abstract push(route: NavigatorRoute): void;
abstract pop(): void;
abstract replace(route: NavigatorRoute): void;
abstract replacePrevious(route: NavigatorRoute): void;
abstract replaceAtIndex(route: NavigatorRoute, index: number): void;
abstract immediatelyResetRouteStack(nextRouteStack: NavigatorRoute[]): void;
abstract popToRoute(route: NavigatorRoute): void;
abstract popToTop(): void;
abstract getCurrentRoutes(): NavigatorRoute[];
}
export interface NavigatorDelegateSelector {
getNavigatorDelegate(navigator: Navigator): NavigatorDelegate;
}
export abstract class NavigatorDelegate {
protected _owner: Navigator;
constructor(navigator: Navigator) {
this._owner = navigator;
}
onBackPress = (): boolean => {
const routes = this.getRoutes();
if (routes.length > 1) {
this.handleBackPress();
if (this._owner.props.navigateBackCompleted) {
this._owner.props.navigateBackCompleted();
}
// Indicate that we handled the event.
return true;
}
return false;
};
abstract getRoutes(): NavigatorRoute[];
abstract immediatelyResetRouteStack(nextRouteStack: NavigatorRoute[]): void;
abstract render(): JSX.Element | null;
abstract processCommand(commandQueue: NavigationCommand[]): void;
abstract handleBackPress(): void;
}
================================================
FILE: extensions/navigation/src/common/assert.ts
================================================
/**
* assert
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
*/
const assert = (cond: any, message?: string | undefined): void => {
if (!cond) {
throw new Error(message || 'Assertion Failed');
}
};
export default assert;
================================================
FILE: extensions/navigation/src/common/lodashMini.ts
================================================
/**
* lodashMini.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Imports a subset of lodash library needed for ReactXP's implementation.
*/
import assign = require('lodash/assign');
import clone = require('lodash/clone');
import cloneDeep = require('lodash/cloneDeep');
import flatten = require('lodash/flatten');
import get = require('lodash/get');
import isEmpty = require('lodash/isEmpty');
import isEqual = require('lodash/isEqual');
import isNumber = require('lodash/isNumber');
import map = require('lodash/map');
import mapValues = require('lodash/mapValues');
export interface Dictionary {
[index: string]: T;
}
export {
assign,
clone,
cloneDeep,
flatten,
get,
isEmpty,
isEqual,
isNumber,
map,
mapValues,
};
================================================
FILE: extensions/navigation/src/native-common/Navigator.tsx
================================================
/**
* Navigator.tsx
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Common native implementation for Navigator on mobile.
*/
import * as RX from 'reactxp';
import * as Types from '../common/Types';
import {
Navigator as BaseNavigator,
NavigatorDelegate,
NavigatorDelegateSelector as DelegateSelector,
NavigationCommand,
NavigatorState,
NavigatorProps,
NavigatorRoute,
CommandType,
} from '../common/Types';
import NavigatorExperimentalDelegate from './NavigatorExperimentalDelegate';
export class DefaultDelegateSelector implements DelegateSelector {
getNavigatorDelegate(navigator: BaseNavigator) {
return new NavigatorExperimentalDelegate(navigator);
}
}
export class NavigatorImpl extends BaseNavigator {
private _delegate: NavigatorDelegate;
private _commandQueue: NavigationCommand[] = [];
constructor(initialProps: NavigatorProps) {
super(initialProps);
if (!initialProps.delegateSelector) {
this._delegate = new NavigatorExperimentalDelegate(this);
} else {
this._delegate = initialProps.delegateSelector.getNavigatorDelegate(this);
}
}
componentDidMount() {
RX.Input.backButtonEvent.subscribe(this._delegate.onBackPress);
}
componentWillUnmount() {
RX.Input.backButtonEvent.unsubscribe(this._delegate.onBackPress);
}
componentDidUpdate() {
// Catch up with any pending commands
this._processCommand();
}
protected getRoutes(): NavigatorRoute[] {
return this._delegate.getRoutes();
}
// Push a new route if initial route doesn't exist
push(route: NavigatorRoute): void {
this._enqueueCommand({
type: CommandType.Push,
param: {
route: route,
},
});
}
pop(): void {
this._enqueueCommand({
type: CommandType.Pop,
param: {},
});
}
replace(route: NavigatorRoute): void {
this._enqueueCommand({
type: CommandType.Replace,
param: {
route: route,
},
});
}
replacePrevious(route: NavigatorRoute): void {
this._enqueueCommand({
type: CommandType.Replace,
param: {
route: route,
value: -1,
},
});
}
// This method replaces the route at the given index of the stack and pops to that index.
replaceAtIndex(route: NavigatorRoute, index: number): void {
const routes = this.getRoutes();
// Pop to index route and then replace if not last one
if (index < routes.length - 1) {
const route = routes[index];
this.popToRoute(route);
}
// Schedule a replace
this.replace(route);
}
// Reset route stack with default route stack
immediatelyResetRouteStack(nextRouteStack: NavigatorRoute[]): void {
this._delegate.immediatelyResetRouteStack(nextRouteStack);
}
popToRoute(route: NavigatorRoute): void {
this._enqueueCommand({
type: CommandType.Pop,
param: {
route: route,
},
});
}
popToTop(): void {
this._enqueueCommand({
type: CommandType.Pop,
param: {
value: -1,
},
});
}
getCurrentRoutes(): NavigatorRoute[] {
return this.getRoutes();
}
// Render without initial route to get a reference for Navigator object
render(): JSX.Element {
return this._delegate.render();
}
private _enqueueCommand(command: NavigationCommand): void {
this._commandQueue.push(command);
this._processCommand();
}
private _processCommand(): void {
this._delegate.processCommand(this._commandQueue);
}
}
export default NavigatorImpl;
export const Navigator = NavigatorImpl;
export const NavigatorDelegateSelector = new DefaultDelegateSelector();
export { Types };
================================================
FILE: extensions/navigation/src/native-common/NavigatorExperimentalDelegate.tsx
================================================
/**
* NavigatorExperimentalDelegate.tsx
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Delegate which encapsulates experimental react-native Navigator experience.
* The main difference of Experimental Navigator is that it uses Animated for navigation animation
* so we can enable useNativeDriver options for those animations.
*
* Currently, Android support on NativeAnimations is more stable and performant than iOS.
* That's why we need to have the ability to pick different implementations for different platforms.
*/
import * as React from 'react';
import * as RN from 'react-native';
import * as RX from 'reactxp';
import * as Navigation from 'reactxp-experimental-navigation';
import assert from '../common/assert';
import * as _ from '../common/lodashMini';
import {
CommandType,
CustomNavigatorSceneConfig,
Navigator,
NavigatorSceneConfigType,
NavigationCommand,
NavigatorDelegate,
NavigatorRoute,
NavigatorState,
} from '../common/Types';
type NavigationSceneRendererProps = Navigation.NavigationSceneRendererProps;
type NavigationState = Navigation.NavigationState;
type NavigationRoute = Navigation.NavigationRoute;
type NavigationTransitionProps = Navigation.NavigationTransitionProps;
const StateUtils = Navigation.StateUtils;
interface NavigationRouteState extends NavigationRoute {
route: NavigatorRoute;
}
interface TransitionSpec {
enableGesture: boolean;
direction: Navigation.NavigationGestureDirection;
gestureResponseDistance: number;
customTransitionConfig?: Navigation.NavigationCustomTransitionConfig;
cardStyle?: RX.Types.ViewStyleRuleSet;
hideShadow?: boolean;
}
export class NavigatorExperimentalDelegate extends NavigatorDelegate {
private _state: Navigation.NavigationState;
private _transitionSpec: TransitionSpec;
constructor(navigator: Navigator) {
super(navigator);
const route: NavigationRouteState = { key: '0', route: { routeId: 0, sceneConfigType: 0 } };
this._state = { index: 0, routes: [route] };
this._transitionSpec = this._buildTransitionSpec(this._state);
}
getRoutes(): NavigatorRoute[] {
return _.map(this._state.routes, element => {
const routeState = element as NavigationRouteState;
return routeState.route;
});
}
// Reset route stack with default route stack
immediatelyResetRouteStack(nextRouteStack: NavigatorRoute[]): void {
const prevState = this._state;
this._state = this._createParentState(nextRouteStack, prevState);
this._transitionSpec = this._buildTransitionSpec(this._state);
this._owner.setState({ state: this._state });
}
// Render without initial route to get a reference for Navigator object
render(): JSX.Element {
return (
);
}
private _convertCustomTransitionConfig(
config: CustomNavigatorSceneConfig | undefined): Navigation.NavigationCustomTransitionConfig | undefined {
if (!config) {
return undefined;
}
const nativeConfig: Navigation.NavigationCustomTransitionConfig = {
transitionStyle: config.transitionStyle,
presentBelowPrevious: config.presentBelowPrevious,
};
if (config.transitionSpec) {
const transitionSpec: Navigation.NavigationTransitionSpec = {};
if (config.transitionSpec.duration) {
transitionSpec.duration = config.transitionSpec.duration;
}
if (config.transitionSpec.easing) {
transitionSpec.easing = config.transitionSpec.easing.function;
}
nativeConfig.transitionSpec = transitionSpec;
}
return nativeConfig;
}
private _buildTransitionSpec(state: Navigation.NavigationState): TransitionSpec {
const route = (state.routes[state.index] as NavigationRouteState).route;
let direction: Navigation.NavigationGestureDirection = 'horizontal';
let customSceneConfig: Navigation.NavigationCustomTransitionConfig | undefined;
let enableGesture = false;
let responseDistance = 0;
const hideShadow = route && route.customSceneConfig && route.customSceneConfig.hideShadow;
const cardStyle: RX.Types.ViewStyleRuleSet | undefined = route && route.customSceneConfig
? route.customSceneConfig.cardStyle
: undefined;
let gestureDistanceSet = false;
if (route) {
// If defined, use the gestureResponseDistance override
if (route.gestureResponseDistance !== undefined && route.gestureResponseDistance !== null) {
responseDistance = route.gestureResponseDistance;
gestureDistanceSet = true;
}
customSceneConfig = this._convertCustomTransitionConfig(route.customSceneConfig);
switch (route.sceneConfigType) {
case NavigatorSceneConfigType.FloatFromBottom:
direction = 'vertical';
if (!gestureDistanceSet) {
responseDistance = 150;
gestureDistanceSet = true;
}
break;
case NavigatorSceneConfigType.Fade:
case NavigatorSceneConfigType.FadeWithSlide:
direction = 'fade';
if (!gestureDistanceSet) {
responseDistance = 0;
gestureDistanceSet = true;
}
break;
// Currently we support only right to left animation
// case NavigatorSceneConfigType.FloatFromRight:
// case NavigatorSceneConfigType.FloatFromLeft:
default:
break;
}
}
// Fall back to 30 as a default for responseDistance
if (!gestureDistanceSet) {
responseDistance = 30;
}
// Conditionally enable gestures
enableGesture = responseDistance > 0;
return {
enableGesture: enableGesture,
gestureResponseDistance: responseDistance,
direction: direction,
customTransitionConfig: customSceneConfig,
cardStyle: cardStyle,
hideShadow: hideShadow,
};
}
private _onTransitionEnd = () => {
this._transitionSpec = this._buildTransitionSpec(this._state);
this._owner.setState({ state: this._state });
if (this._owner.props.transitionCompleted) {
this._owner.props.transitionCompleted();
}
};
private _onTransitionStart = (transitionProps: NavigationTransitionProps, prevTransitionProps?: NavigationTransitionProps) => {
if (this._owner.props.transitionStarted) {
const fromIndex = prevTransitionProps && prevTransitionProps.scene ? prevTransitionProps.scene.index : undefined;
const toIndex = transitionProps.scene ? transitionProps.scene.index : undefined;
const fromRouteId = prevTransitionProps && prevTransitionProps.scene ? prevTransitionProps.scene.route.key : undefined;
const toRouteId = transitionProps.scene ? transitionProps.scene.route.key : undefined;
this._owner.props.transitionStarted(
transitionProps.position,
toRouteId,
fromRouteId,
toIndex,
fromIndex);
}
};
// Callback from Navigator.js to RX.Navigator
private _renderScene = (props: NavigationSceneRendererProps): JSX.Element => {
const parentState: NavigationState = props.navigationState;
const sceneState: NavigationRouteState = parentState.routes[props.scene.index] as NavigationRouteState;
// Does the route exist?
if (sceneState && sceneState.route) {
// Call the renderScene callback.
return this._owner.props.renderScene(sceneState.route);
}
// No route? Return empty scene.
return ;
};
handleBackPress(): void {
this._owner.pop();
}
processCommand(commandQueue: NavigationCommand[]): void {
// Return if nothing to process
if (!commandQueue.length) {
return;
}
const previousState: NavigationState = this._state;
let useNewStateAsScene = false;
const command = commandQueue.shift()!;
const route = command.param.route;
const value = command.param.value;
switch (command.type) {
case CommandType.Push:
useNewStateAsScene = true;
this._state = StateUtils.push(this._state, this._createState(route));
break;
case CommandType.Pop:
if (route !== undefined) {
this._state = this._popToRoute(this._state, route);
} else if (value !== undefined) {
if (value > 0) {
this._state = this._popN(this._state, value);
} else {
this._state = this._popToTop(this._state);
}
} else {
this._state = StateUtils.pop(this._state);
}
break;
case CommandType.Replace:
if (value === -1) {
this._state = StateUtils.replaceAtIndex(this._state, this._state.routes.length - 2,
this._createState(route));
} else {
this._state = StateUtils.replaceAtIndex(this._state, this._state.routes.length - 1,
this._createState(route));
}
break;
default:
console.error('Undefined Navigation command: ', command.type);
break;
}
if (previousState !== this._state) {
if (useNewStateAsScene) {
this._transitionSpec = this._buildTransitionSpec(this._state);
} else {
this._transitionSpec = this._buildTransitionSpec(previousState);
}
this._owner.setState({ state: this._state });
}
}
/**
* This method is going to be deprecated in later releases
*/
private _onNavigateBack = () => {
this.onBackPress();
};
private _createState(route: NavigatorRoute): NavigationRouteState {
return { key: route.routeId.toString(), route: route };
}
private _createParentState(routes: NavigatorRoute[], prevState: NavigationState): NavigationState {
const prevRoutes = prevState.routes as NavigationRouteState[];
const children = _.map(routes, (element: NavigatorRoute, index: number) => {
if (prevRoutes.length > index) {
const prevRoute = prevRoutes[index];
// Navigator state reducer is a little bit naive,
// let's make sure it's scene rendering caching would work properly
if (prevRoute.route.routeId === element.routeId) {
return prevRoute;
}
}
return this._createState(element);
});
return { routes: children, index: routes.length - 1 };
}
private _popToTop(state: NavigationState): NavigationState {
const popCount: number = state.routes.length - 1;
if (popCount > 0) {
return this._popN(state, popCount);
}
return state;
}
private _popN(state: NavigationState, n: number): NavigationState {
assert(n > 0, 'n < 0 please pass positive value');
const initialRoutes = state.routes;
const initialLength = initialRoutes.length;
assert(initialLength >= n, 'navigation stack underflow');
const result: NavigationState = _.clone(state);
result.routes = initialRoutes.slice(0, initialLength - n);
result.index = initialLength - n - 1;
return result;
}
private _popToRoute(state: NavigationState, route: NavigatorRoute): NavigationState {
let popCount = 0;
for (let i = state.routes.length - 1; i >= 0; i--) {
const child = state.routes[i] as NavigationRouteState;
if (route.routeId === child.route.routeId) {
break;
} else {
popCount++;
}
}
if (popCount > 0) {
return this._popN(state, popCount);
} else {
return state;
}
}
}
export default NavigatorExperimentalDelegate;
================================================
FILE: extensions/navigation/src/typings/react-native-deprecated-custom-components.d.ts
================================================
declare module 'react-native-deprecated-custom-components' {
import * as RN from 'react-native';
interface NavigatorProps extends RN.ComponentPropsBase {
configureScene?: Function;
initialRoute?: any;
initialRouteStack?: any[];
navigatorBar?: any;
navigator?: Navigator;
onDidFocus?: Function; // deprecated
onWillFocus?: Function; // deprecated
renderScene: Function;
sceneStyle?: RN.StyleRuleSet | RN.StyleRuleSet[];
}
class Navigator extends RN.ReactNativeBaseComponent {
static SceneConfigs: {
PushFromRight: any;
FloatFromRight: any;
FloatFromLeft: any;
FloatFromBottom: any;
FloatFromBottomAndroid: any;
FadeAndroid: any;
HorizontalSwipeJump: any;
};
getCurrentRoutes(): any[];
jumpBack(): void;
jumpForward(): void;
jumpTo(route: any): void;
push(route: any): void;
pop(): void;
replace(route: any): void;
replaceAtIndex(route: any, index: number): void;
replacePrevious(route: any): void;
immediatelyResetRouteStack(routeStack: any[]): void;
popToRoute(route: any): void;
popToTop(): void;
}
}
================================================
FILE: extensions/navigation/src/typings/rebound.d.ts
================================================
declare module 'rebound' {
class SpringSystem {
createSpring(): any;
}
}
================================================
FILE: extensions/navigation/src/web/Navigator.tsx
================================================
/*
* Navigator.tsx
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Web specific implementation of Navigator. This is inspired from React.Navigator.
* This component is set with props, which are callback methods. It is primarily driven
* by state updates instigated by its public helpers like immediatelyResetRouteStack, push,
* pop, which update the state and cause transitions.
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as RX from 'reactxp';
import * as rebound from 'rebound';
import { Styles, View } from 'reactxp';
import * as _ from '../common/lodashMini';
import * as Types from '../common/Types';
import {
NavigatorDelegateSelector as DelegateSelector,
NavigatorState as BaseNavigatorState,
Navigator as NavigatorBase,
NavigatorProps,
} from '../common/Types';
import { NavigatorSceneConfigFactory, NavigatorSceneConfig } from './NavigatorSceneConfigFactory';
// [Bug:506870] Move web navigator to RX animated API
export interface SpringSystem {
createSpring(): any;
}
export interface Spring {
setRestSpeedThreshold?: (restSpeed: number) => void;
setCurrentValue?: (val: number) => any;
addListener?: (obj: any) => any;
getCurrentValue? (): any;
setOvershootClampingEnabled? (a: boolean): void;
getSpringConfig? (): any;
setVelocity?(velocity: any): any;
setEndValue?(endVal: number): any;
}
// Default styles
const _styles = {
container: Styles.createViewStyle({
flex: 1,
flexDirection: 'column',
alignSelf: 'stretch',
overflow: 'hidden',
}),
defaultSceneStyle: Styles.createViewStyle({
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
top: 0,
}),
baseScene: Styles.createViewStyle({
position: 'absolute',
overflow: 'hidden',
left: 0,
right: 0,
bottom: 0,
top: 0,
}),
disabledScene: Styles.createViewStyle({
top: 0,
bottom: 0,
flex: 1,
}),
transitioner: Styles.createViewStyle( {
flex: 1,
flexDirection: 'column',
backgroundColor: 'transparent',
overflow: 'hidden',
alignItems: 'stretch',
}),
sceneStyle: Styles.createViewStyle({
flex: 1,
shadowOffset: { height: 0, width: 0 },
shadowRadius: 40,
shadowColor: 'rgba(0, 0, 0, 0.2)',
}),
};
// Transition types
export type TransitionToCallback = () => void;
export type ReplaceAtIndexCallback = () => void;
export interface TransitionToQueueItem {
// The destination route index.
destIndex: number;
// The callback to call after this transition is finished.
transitionFinished: TransitionToCallback;
// This velocity will be passed to the spring system for transitioning. If no velocity is specified, a default value
// will be used.
velocity: number;
}
export interface NavigatorState extends BaseNavigatorState {
// Current stack of animation configurations for a scene
sceneConfigStack?: NavigatorSceneConfig[];
// The current stack of user-defined route objects.
routeStack?: Types.NavigatorRoute[];
// The currently presented route index.
presentedIndex?: number;
// During transition, the presentedIndex will be updated at the beginning and transitionFromIndex will be set to the
// previous index.
transitionFromIndex?: number;
// If told to transition while another transition is occurring, it will be added to this queue and executed after.
transitionQueue?: TransitionToQueueItem[];
// The callback to call after the current transition is finished.
transitionFinished?: TransitionToCallback;
}
export class NavigatorImpl extends NavigatorBase {
// Keep a map of all rendered scenes, keyed off their routeId
private _renderedSceneMap: { [routeId: number]: JSX.Element } = {};
// References to mounted components.
private _containerRef: RX.View | null;
private _sceneRefs: { [sceneIndex: string]: RX.View } = {};
// Save a public reference to the parent navigator if one was given in props.
navigatorReference: Navigator;
// [Bug:506870] Move web navigator to RX animated API
// Spring system and spring used for transitions.
springSystem: SpringSystem;
spring: Spring;
// Cache the dimensions of the navigator so scenes can transition with that size in mind.
private _dimensions: RX.Types.Dimensions;
// Receives initial props and sets initial state for Navigator
constructor(initialProps?: NavigatorProps) {
super(initialProps);
// Default navigator state
this.state = {
sceneConfigStack: [],
routeStack: [],
presentedIndex: 0,
transitionFromIndex: undefined,
transitionQueue: [],
};
}
UNSAFE_componentWillMount() {
this.springSystem = new rebound.SpringSystem();
this.spring = this.springSystem.createSpring();
this.spring.setRestSpeedThreshold(0.05);
this.spring.setCurrentValue(0).setAtRest();
this.spring.addListener({
onSpringUpdate: () => {
this._handleSpringUpdate();
},
onSpringAtRest: () => {
this._completeTransition();
},
});
}
componentDidMount() {
this._updateDimensionsCache();
this._handleSpringUpdate();
}
componentDidUpdate() {
this._updateDimensionsCache();
}
render() {
const newRenderedSceneMap: { [routeId: number]: JSX.Element } = {};
let scenes: JSX.Element[];
if (this.state.routeStack.length > 0) {
scenes = this.state.routeStack.map((route, index) => {
let renderedScene: JSX.Element;
if (this._renderedSceneMap[route.routeId] &&
index !== this.state.presentedIndex) {
renderedScene = this._renderedSceneMap[route.routeId];
} else {
renderedScene = this._renderNavigatorScene(route, index);
}
newRenderedSceneMap[route.routeId] = renderedScene;
return renderedScene;
});
} else {
scenes = [];
}
this._renderedSceneMap = _.clone(newRenderedSceneMap);
return (
{ scenes }
);
}
// Public Navigator Helper methods. These methods modify Navigator state, which kicks of
// re-renders for the Navigator
jumpTo(route: Types.NavigatorRoute): void {
const destIndex = this.state.routeStack.indexOf(route);
this._jumpN(destIndex - this.state.presentedIndex);
}
jumpForward(): void {
this._jumpN(1);
}
jumpBack(): void {
this._jumpN(-1);
}
push(route: Types.NavigatorRoute): void {
this._invariant(!!route, 'Must supply route to push');
const activeLength = this.state.presentedIndex + 1;
const activeStack = this.state.routeStack.slice(0, activeLength);
const activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength);
const nextStack = activeStack.concat([route]);
const destIndex = nextStack.length - 1;
const nextAnimationConfigStack: NavigatorSceneConfig [] = activeAnimationConfigStack.concat([
this._getSceneConfigFromRoute(route),
]);
this.setState({
routeStack: nextStack,
sceneConfigStack: nextAnimationConfigStack,
}, () => {
this._enableScene(destIndex);
this._transitionTo(destIndex);
});
}
immediatelyResetRouteStack(nextRouteStack: Types.NavigatorRoute[]): void {
const destIndex = nextRouteStack.length - 1;
this.setState({
// Build a sceneConfigStack
sceneConfigStack: _.map(nextRouteStack, route => this._getSceneConfigFromRoute(route)),
routeStack: nextRouteStack,
presentedIndex: destIndex,
transitionFromIndex: undefined,
transitionQueue: [],
}, () => {
this._handleSpringUpdate();
if (destIndex >= 0) {
this._enableScene(destIndex, true);
}
});
}
pop(): void {
if (this.state.transitionQueue.length) {
// This is the workaround to prevent user from firing multiple `pop()` calls that may pop the routes beyond
// the limit. Because `this.state.presentedIndex` does not update until the transition starts, we can't
// reliably use `this.state.presentedIndex` to know whether we can safely keep popping the routes or not at
// this moment.
return;
}
if (this.state.presentedIndex > 0) {
this._popN(1);
}
}
// This method replaces the route at the given index of the stack and pops to that index.
replaceAtIndex(route: Types.NavigatorRoute, index: number): void {
this._invariant(!!route, 'Must supply route to replace');
if (index < 0) {
index += this.state.routeStack.length;
}
if (this.state.routeStack.length <= index) {
return;
}
const nextRouteStack = this.state.routeStack.slice(0, index + 1);
const nextAnimationModeStack = this.state.sceneConfigStack.slice(0, index + 1);
nextRouteStack[index] = route;
nextAnimationModeStack[index] = this._getSceneConfigFromRoute(route);
this.setState({
routeStack: nextRouteStack,
sceneConfigStack: nextAnimationModeStack,
});
}
replace(route: Types.NavigatorRoute): void {
this.replaceAtIndex(route, this.state.presentedIndex);
}
replacePrevious(route: Types.NavigatorRoute): void {
this.replaceAtIndex(route, this.state.presentedIndex - 1);
}
popToTop(): void {
this.popToRoute(this.state.routeStack[0]);
}
popToRoute(route: Types.NavigatorRoute): void {
const indexOfRoute = this.state.routeStack.indexOf(route);
this._invariant(indexOfRoute !== -1, 'Calling popToRoute for a route that doesn\'t exist!');
const numToPop = this.state.presentedIndex - indexOfRoute;
this._popN(numToPop);
}
replacePreviousAndPop(route: Types.NavigatorRoute): void {
if (this.state.routeStack.length < 2) {
return;
}
this.replacePrevious(route);
this.pop();
}
getCurrentRoutes(): Types.NavigatorRoute[] {
// Clone before returning to avoid caller mutating the stack.
return this.state.routeStack.slice();
}
private _onMountContainer = (comp: RX.View | null) => {
this._containerRef = comp;
};
private _updateDimensionsCache() {
if (this._containerRef) {
const transitioner = ReactDOM.findDOMNode(this._containerRef) as HTMLElement | null;
if (transitioner) {
this._dimensions = {
width: transitioner.offsetWidth,
height: transitioner.offsetHeight,
};
}
}
}
// Helper method to extract Navigator's Scene config from the route
private _getSceneConfigFromRoute(route: Types.NavigatorRoute): NavigatorSceneConfig {
// route exists? query the factory to generate a scene configuration
if (route) {
return NavigatorSceneConfigFactory.createConfig(route.sceneConfigType);
}
return undefined;
}
// Render a scene for the navigator
private _renderNavigatorScene(route: Types.NavigatorRoute, index: number): JSX.Element {
const styles: RX.Types.ViewStyleRuleSet[] = [_styles.baseScene, _styles.sceneStyle, _styles.defaultSceneStyle];
if (index !== this.state.presentedIndex) {
// update styles
styles.push(_styles.disabledScene);
}
// Wraps the callback passed as a prop to Navigator to render the scene
return (
this._onMountScene(comp, index) }
style={ styles }
>
{ this.props.renderScene(route) }
);
}
private _onMountScene(comp: RX.View | null, sceneIndex: number) {
const sceneId = 'scene_' + sceneIndex;
if (!comp) {
delete this._sceneRefs[sceneId];
} else {
this._sceneRefs[sceneId] = comp;
}
}
// Push a scene below the others so they don't block touches sent to the presented scenes.
private _disableScene(sceneIndex: number) {
const sceneId = 'scene_' + sceneIndex;
if (this._sceneRefs[sceneId]) {
this._setNativeStyles(this._sceneRefs[sceneId], {
opacity: 0,
zIndex: -10,
});
}
}
// Add styles on the scene - At this time, the scene should be mounted and sitting in the
// DOM. We are just adding giving styles to this current scene.
private _enableScene(sceneIndex: number, force = false) {
const sceneStyle = Styles.combine([_styles.baseScene, _styles.sceneStyle, _styles.defaultSceneStyle]) as any;
// Then restore the top value for this scene.
const enabledSceneNativeProps = {
style: {
top: sceneStyle.top,
bottom: sceneStyle.bottom,
opacity: 1,
zIndex: 0,
transform: '',
},
};
if (!force && sceneIndex !== this.state.transitionFromIndex &&
sceneIndex !== this.state.presentedIndex) {
// If we are not in a transition from this index, make sure opacity is 0 to prevent the enabled scene from
// flashing over the presented scene.
(enabledSceneNativeProps.style as any).opacity = 0;
}
const sceneId = 'scene_' + sceneIndex;
if (this._sceneRefs[sceneId]) {
this._setNativeStyles(this._sceneRefs[sceneId], enabledSceneNativeProps.style);
}
}
private _transitionTo(destIndex: number, velocity?: number, jumpSpringTo?: number, cb?: TransitionToCallback) {
// If we're already presenting this index, bail here.
if (destIndex === this.state.presentedIndex) {
return;
}
// If we're already transitioning to another index, queue this one.
if (this.state.transitionFromIndex !== undefined) {
const newTransitionQueue = _.cloneDeep(this.state.transitionQueue);
newTransitionQueue.push({
destIndex: destIndex,
velocity: velocity,
transitionFinished: cb,
});
// set new transition queue
this.setState ({ transitionQueue: newTransitionQueue });
return;
}
// Set new state values.
this.setState({
transitionFromIndex: this.state.presentedIndex,
presentedIndex: destIndex,
transitionFinished: cb,
});
// Grab the scene config from the route we're leaving.
const sceneConfig = this.state.sceneConfigStack[this.state.transitionFromIndex] ||
this.state.sceneConfigStack[this.state.presentedIndex];
this._invariant(!!sceneConfig, 'Cannot configure scene at index ' + this.state.transitionFromIndex);
// Set the spring in motion. Updates will trigger _handleSpringUpdate.
if (jumpSpringTo !== undefined) {
this.spring.setCurrentValue(jumpSpringTo);
}
this.spring.setOvershootClampingEnabled(true);
this.spring.getSpringConfig().friction = sceneConfig.springFriction;
this.spring.getSpringConfig().tension = sceneConfig.springTension;
this.spring.setVelocity(velocity || sceneConfig.defaultTransitionVelocity);
this.spring.setEndValue(1);
if (this.props.transitionStarted) {
this.props.transitionStarted();
}
}
private _completeTransition() {
const newState: NavigatorState = {};
this.setState({
transitionFromIndex: undefined,
});
this.spring.setCurrentValue(0).setAtRest();
this._hideScenes();
// Do we have pending transitions? trigger transitions then
if (this.state.transitionQueue.length) {
const newTransitionQueue = _.cloneDeep(this.state.transitionQueue);
const queuedTransition = newTransitionQueue.shift();
// add styles on the scene we are about to transition to
this._enableScene(queuedTransition.destIndex);
this._transitionTo(
queuedTransition.destIndex,
queuedTransition.velocity,
undefined,
queuedTransition.transitionFinished,
);
if (this.state.transitionFinished) {
this.state.transitionFinished();
newState.transitionFinished = undefined;
}
newState.transitionQueue = newTransitionQueue;
// New setState
this.setState(newState);
} else {
if (this.props.transitionCompleted) {
this.props.transitionCompleted();
}
if (this.state.transitionFinished) {
this.state.transitionFinished();
}
}
}
private _hideScenes() {
for (let i = 0; i < this.state.routeStack.length; i++) {
if (i === this.state.presentedIndex ||
i === this.state.transitionFromIndex) {
continue;
}
this._disableScene(i);
}
}
// This happens for each frame of either a gesture or a transition. If both are happening, we only set values for
// the transition and the gesture will catch up later.
private _handleSpringUpdate() {
// Prioritize handling transition in progress over a gesture:
if (this.state.transitionFromIndex !== undefined) {
this._transitionBetween(
this.state.transitionFromIndex,
this.state.presentedIndex,
this.spring.getCurrentValue(),
);
}
}
private _transitionSceneStyle(fromIndex: number, toIndex: number, progress: number, index: number) {
const sceneId = 'scene_' + index;
const viewAtIndex = this._sceneRefs[sceneId];
if (viewAtIndex === undefined) {
return;
}
// Use toIndex animation when we move forwards. Use fromIndex when we move back.
const sceneConfigIndex = fromIndex < toIndex ? toIndex : fromIndex;
let sceneConfig = this.state.sceneConfigStack[sceneConfigIndex];
// This happens for overswiping when there is no scene at toIndex.
if (!sceneConfig) {
sceneConfig = this.state.sceneConfigStack[sceneConfigIndex - 1];
}
const styleToUse: RX.Types.ViewStyleRuleSet = {};
const useFn = index < fromIndex || index < toIndex ?
sceneConfig.animationInterpolators.out :
sceneConfig.animationInterpolators.into;
const directionAdjustedProgress = fromIndex < toIndex ? progress : 1 - progress;
const didChange = useFn(styleToUse, this._dimensions, directionAdjustedProgress);
if (didChange) {
this._setNativeStyles(viewAtIndex, styleToUse);
}
}
private _transitionBetween(fromIndex: number, toIndex: number, progress: number) {
this._transitionSceneStyle(fromIndex, toIndex, progress, fromIndex);
this._transitionSceneStyle(fromIndex, toIndex, progress, toIndex);
}
private _getDestIndexWithinBounds(n: number) {
const currentIndex = this.state.presentedIndex;
const destIndex = currentIndex + n;
this._invariant(destIndex >= 0, 'Cannot jump before the first route.');
const maxIndex = this.state.routeStack.length - 1;
this._invariant(maxIndex >= destIndex, 'Cannot jump past the last route.');
return destIndex;
}
private _jumpN(n: number) {
const destIndex = this._getDestIndexWithinBounds(n);
this._invariant(destIndex !== -1, 'Cannot jump to route that is not in the route stack');
this._enableScene(destIndex);
this._transitionTo(destIndex);
}
private _popN(n: number) {
if (n === 0) {
return;
}
this._invariant(this.state.presentedIndex - n >= 0, 'Cannot pop below zero');
const popIndex = this.state.presentedIndex - n;
this._enableScene(popIndex);
this._transitionTo(
popIndex,
undefined, // default velocity
undefined, // no spring jumping
() => this._cleanScenesPastIndex(popIndex),
);
}
private _cleanScenesPastIndex(index: number) {
const newStackLength = index + 1;
// Remove any unneeded rendered routes.
if (newStackLength < this.state.routeStack.length) {
this.setState({
sceneConfigStack: this.state.sceneConfigStack.slice(0, newStackLength),
routeStack: this.state.routeStack.slice(0, newStackLength),
});
}
}
// Get routeId for the incoming route
private _getRouteID(route: Types.NavigatorRoute): number {
return route.routeId;
}
// Define an inconstiant method like React.Navigator
private _invariant(test: boolean, failureMessage: string) {
if (!test) {
throw failureMessage;
}
}
// Manually override the styles in the DOM for the given component. This method is a hacky equivalent of React Native's
// setNativeProps.
private _setNativeStyles(component: React.ReactInstance, currentStyles: any) {
// Grab the actual element from the DOM.
const element = ReactDOM.findDOMNode(component) as HTMLElement|null;
if (element) {
const flatStyles: RX.Types.ViewStyleRuleSet = Array.isArray(currentStyles) ? _.flatten(currentStyles) : currentStyles;
// Modify styles
_.assign(element.style, flatStyles);
}
}
}
export default NavigatorImpl;
export const Navigator = NavigatorImpl;
export const NavigatorDelegateSelector: DelegateSelector = undefined;
export { Types };
================================================
FILE: extensions/navigation/src/web/NavigatorSceneConfigFactory.tsx
================================================
/**
* NavigatorSceneConfigFactory.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* NavigatorSceneConfigFactory creates an 'object' of type NavigatorSceneConfig,
* which is consumed by the Navigator. This object contains properties to execute
* spring animation for transition between scenes. NavigatorSceneConfigFactory and
* NavigatorSceneConfig are both exported.
*/
import * as RX from 'reactxp';
import * as _ from '../common/lodashMini';
import * as Types from '../common/Types';
// Interpolator type, which accepts a combination of these types and returns a interpolated/calculated result
// Interpolator wrapper, which is given as a callback method to Navigator to call the animation interpolator
export type Interpolator = (progress: number, dimension?: RX.Types.Dimensions) => number;
export type InterpolatorWrapper = (previousStyleSet: RX.Types.ViewStyleRuleSet,
dimensions: RX.Types.Dimensions, progress: number) => boolean;
// Interface to define the transition styles for multiple views
export interface TransitionStyle {
translateX?: (progress: number, dimension: RX.Types.Dimensions) => string;
translateY?: (progress: number, dimension: RX.Types.Dimensions) => string;
translateZ?: (progress: number, dimension: RX.Types.Dimensions) => string;
// Note: Weird type: Either a 'function type' or a 'number'
opacity?: ((progress: number) => number) | number;
rotateX?: ((progress: number) => number) | number;
rotateY?: ((progress: number) => number) | number;
rotateZ?: ((progress: number) => number) | number;
scaleX?: (progress: number) => number;
scaleY?: (progress: number) => number;
scaleZ?: (progress: number) => number;
}
// Defined style interpolators for each transition type
class SceneConfigStyles {
static fadeToTheLeft: TransitionStyle = {
translateX: (t, dimensions) =>(t * -dimensions.width * 0.3) + 'px',
opacity: 1,
};
static fadeToTheRight: TransitionStyle = {
translateX: (t, dimensions) => (t * dimensions.width * 0.3) + 'px',
opacity: 1,
};
static fadeIn: TransitionStyle = {
opacity: t => t,
};
static fadeOut: TransitionStyle = {
opacity: t => 1 - t,
};
static fadeOutToTop: TransitionStyle = {
opacity: t => 1 - t,
translateY: (t, dimensions) => (t * -0.1 * dimensions.height) + 'px',
};
static toTheLeft: TransitionStyle = {
translateX: (t, dimensions) => (t * -dimensions.width) + 'px',
};
static toTheUp: TransitionStyle = {
translateY: (t, dimensions) => (t * -dimensions.height) + 'px',
};
static toTheDown: TransitionStyle = {
translateY: (t, dimensions) => (t * dimensions.height) + 'px',
};
static fromTheRight: TransitionStyle = {
opacity: 1,
translateX: (t, dimensions) => (dimensions.width - (t * dimensions.width)) + 'px',
};
static fromTheLeft: TransitionStyle = {
opacity: 1,
translateX: (t, dimensions) => (-dimensions.width + (t * dimensions.width)) + 'px',
};
static fromTheDown: TransitionStyle = {
translateY: (t, dimensions) => (dimensions.height - t * dimensions.height) + 'px',
};
static fromTheUp: TransitionStyle = {
opacity: 1,
translateY: (t, dimensions) => (-dimensions.height + t * dimensions.height) + 'px',
};
static fromTheFront: TransitionStyle = {
opacity: 1,
translateY: (t, dimensions) => (dimensions.height - t * dimensions.height) + 'px',
};
static toTheBack: TransitionStyle = {
scaleX: t => (1 - (t * 0.05)),
scaleY: t => (1 - (t * 0.05)),
opacity: 1,
};
// CSS requires all transforms to be combined into one transform property. bundleCompoundStyles searches a style
// definition for separate transforms and melts it down to a "transform" property.
static bundleCompoundStyles(styles: { [name: string]: string | number }): any {
const transforms: { [name: string]: string | number } = { };
const remaining: { [name: string]: string | number } = { };
for (const name in styles) {
if (styles.hasOwnProperty(name)) {
switch (name) {
case 'translateX':
case 'translateY':
case 'translateZ':
case 'scaleX':
case 'scaleY':
case 'scaleZ':
case 'rotateX':
case 'rotateY':
case 'rotateZ':
transforms[name] = styles[name];
break;
default:
remaining[name] = styles[name];
break;
}
}
}
// Add transforms into remaining object
if (!_.isEmpty(transforms)) {
remaining.transform = _.map(transforms, (val, key) => key + '(' + val + ')').join(' ');
}
return remaining;
}
}
// Navigator config class. Navigator works on the instances of this class
export class NavigatorSceneConfig {
// Rebound spring parameters when transitioning FROM this scene
springFriction = 26;
springTension = 200;
// Velocity to start at when transitioning without gesture
defaultTransitionVelocity = 1.5;
// Returns an object of functions that return a function
animationInterpolators: {
into: InterpolatorWrapper;
out: InterpolatorWrapper;
};
constructor(intoStyle: TransitionStyle, outStyle: TransitionStyle) {
// Into, Out interpolators are required to do a scene transition
this.animationInterpolators = {
into: this._styleInterpolator(intoStyle),
out: this._styleInterpolator(outStyle),
};
}
// Private method that hangs as a callback on animationInterpolator object
// It calculates new styles and updates the previousStyles object sent to decide
// if the animation triggered or not in the component that calls it
private _styleInterpolator(styles: TransitionStyle): InterpolatorWrapper {
return (previousStyleSet: RX.Types.ViewStyleRuleSet, dimensions: RX.Types.Dimensions, progress: number): boolean => {
// Calls the interpolator method for each type and calculates
const newStyleSet = SceneConfigStyles.bundleCompoundStyles(
_.mapValues(styles, (interpolator: Interpolator | number) => (
_.isNumber(interpolator) ? interpolator : interpolator(progress, dimensions)
)) as any);
// Check if anything has changed since last frame.
if (_.isEqual(previousStyleSet, newStyleSet)) {
return false;
}
// Copy the new props into the previous object.
for (const prop in newStyleSet) {
if (newStyleSet.hasOwnProperty(prop)) {
_.assign(previousStyleSet, {[prop]: _.get(newStyleSet, prop)});
}
}
return true;
};
}
}
// Factory class to create Navigator scene configurations for each type of transition between routes
export class NavigatorSceneConfigFactory {
// Helper method that creates a new Animation config for a scene
static createConfig(configType: Types.NavigatorSceneConfigType): NavigatorSceneConfig {
switch (configType) {
case Types.NavigatorSceneConfigType.FloatFromRight:
return new NavigatorSceneConfig(SceneConfigStyles.fromTheRight, SceneConfigStyles.fadeToTheLeft);
case Types.NavigatorSceneConfigType.FloatFromLeft:
return new NavigatorSceneConfig(SceneConfigStyles.fromTheLeft, SceneConfigStyles.fadeToTheRight);
case Types.NavigatorSceneConfigType.FloatFromBottom:
return new NavigatorSceneConfig(SceneConfigStyles.fromTheFront, SceneConfigStyles.toTheBack);
case Types.NavigatorSceneConfigType.Fade:
return new NavigatorSceneConfig(SceneConfigStyles.fadeIn, SceneConfigStyles.fadeOut);
case Types.NavigatorSceneConfigType.FadeWithSlide:
return new NavigatorSceneConfig(SceneConfigStyles.fadeIn, SceneConfigStyles.fadeOutToTop);
default:
// Float from Right
return new NavigatorSceneConfig(SceneConfigStyles.fromTheLeft, SceneConfigStyles.fadeToTheRight);
}
}
}
================================================
FILE: extensions/navigation/tsconfig.json
================================================
{
"compilerOptions": {
"experimentalDecorators": true,
"noImplicitAny": true,
"skipLibCheck": true,
"declaration": true,
"noResolve": false,
"module": "commonjs",
"target": "es5",
"outDir": "dist",
"jsx": "react",
"lib": ["es5", "dom"]
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}
================================================
FILE: extensions/netinfo/.eslintrc.json
================================================
{
"parserOptions": {
"project": "./tsconfig.json"
},
"extends": ["skype"],
"rules": {},
"overrides": [
{
"files": [
"*.tsx"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off"
}
},
{
"files": [
"PluginBaseChecker.ts"
],
"rules": {
"@typescript-eslint/no-unused-vars": "off"
}
}
]
}
================================================
FILE: extensions/netinfo/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
package-lock.json
# Optional npm cache directory
.npm
# Build artifacts
**/dist
# Miscellaneous user files
*.user
.vscode
.DS_STORE
================================================
FILE: extensions/netinfo/.npmignore
================================================
/node_modules
/src/.vs
/src/bin
/src/obj
*.user
================================================
FILE: extensions/netinfo/README.md
================================================
# reactxp-netinfo
This module provides cross-platform support for detecting network ionformation within the [ReactXP](https://microsoft.github.io/reactxp/) library. This used to be a part of ReactXP core, but was extracted to be a standalone module inline with React Native `Lean Core` initiative. This exists as a standalone module to prevent users of ReactXP from having to link native modules when getting started.
## Getting Started
This module relies on [@react-native-community/netinfo](https://www.npmjs.com/packages/@react-native-community/netinfo) and will need to be linked into the react-native project.
This can be done by following the linking instructions in the React Native documentation or by running
```react-native link @react-native-community/netinfo```
## Documentation
For detailed documentation, look [here](https://microsoft.github.io/reactxp/docs/extensions/netinfo.html).
### Prerequisites
* [ReactXP](https://github.com/microsoft/reactxp/)
## Contributing
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
You must sign a Contribution License Agreement (CLA) before your PR will be merged. This a one-time requirement for Microsoft projects in GitHub. You can read more about [Contribution License Agreements (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) on Wikipedia. You can sign the Microsoft Contribution License Agreement by visiting https://cla.microsoft.com/. Use your GitHub account to login.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
================================================
FILE: extensions/netinfo/index.android.js
================================================
'use strict';
module.exports = require('./dist/android/PluginBase.js');
================================================
FILE: extensions/netinfo/index.ios.js
================================================
'use strict';
module.exports = require('./dist/ios/PluginBase.js');
================================================
FILE: extensions/netinfo/index.js
================================================
'use strict';
// Export web by default. Other platforms have custom index.[platform].js files
module.exports = require('./dist/web/PluginBase.js');
================================================
FILE: extensions/netinfo/index.macos.js
================================================
'use strict';
module.exports = require('./dist/macos/PluginBase.js');
================================================
FILE: extensions/netinfo/index.windows.js
================================================
'use strict';
module.exports = require('./dist/windows/PluginBase.js');
================================================
FILE: extensions/netinfo/package.json
================================================
{
"name": "reactxp-netinfo",
"version": "2.0.0",
"description": "Plugin for ReactXP that provides information about network connectivity",
"author": "ReactXP Team ",
"license": "MIT",
"scripts": {
"build": "tsc",
"lint": "eslint --ext .ts,.tsx src",
"lint:fix": "npm run lint -- --fix"
},
"dependencies": {
"@react-native-community/netinfo": "^4.6.1"
},
"peerDependencies": {
"reactxp": "^2.0.0",
"react-native": ">=0.57",
"react-native-windows": "^0.57.1"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "2.15.0",
"eslint": "6.8.0",
"eslint-config-skype": "1.5.0",
"eslint-plugin-import": "2.19.1",
"reactxp": "^2.0.0",
"typescript": "3.7.4"
},
"types": "dist/web/PluginBase.d.ts"
}
================================================
FILE: extensions/netinfo/src/android/PluginBase.tsx
================================================
/*
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the Android implementation of the plugin.
*/
import * as Types from '../common/Types';
import NetInfo from '../native-common/NetInfo';
export { NetInfo as default, Types };
================================================
FILE: extensions/netinfo/src/common/Interfaces.ts
================================================
/*
* Interfaces.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Interface definition for cross-platform ReactXP plugin for gathering network/connectivity
* info. This was extracted from the reactxp core
*/
import SubscribableEvent from 'subscribableevent';
import * as Types from './Types';
export abstract class NetInfo {
abstract isConnected(): Promise;
abstract getType(): Promise;
connectivityChangedEvent = new SubscribableEvent<(isConnected: boolean) => void>();
}
export interface PluginInterface {
Types: typeof Types;
default: NetInfo;
}
================================================
FILE: extensions/netinfo/src/common/PluginBaseChecker.ts
================================================
/*
* PluginBaseChecker.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Type check all the pluginbase exports against the desired interface.
*/
import * as AndroidPlugin from '../android/PluginBase';
import * as iOSPlugin from '../ios/PluginBase';
import * as macOSPlugin from '../macos/PluginBase';
import * as WebPlugin from '../web/PluginBase';
import * as WindowsPlugin from '../windows/PluginBase';
import * as Interfaces from './Interfaces';
const _typeCheckerAndroid: Interfaces.PluginInterface = AndroidPlugin;
const _typeCheckeriOS: Interfaces.PluginInterface = iOSPlugin;
const _typeCheckermacOS: Interfaces.PluginInterface = macOSPlugin;
const _typeCheckerWeb: Interfaces.PluginInterface = WebPlugin;
const _typeCheckerWindows: Interfaces.PluginInterface = WindowsPlugin;
================================================
FILE: extensions/netinfo/src/common/Types.ts
================================================
/*
* Types.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Type definitions to support the plugin.
*/
export enum DeviceNetworkType {
Unknown,
None,
Wifi,
Mobile2G,
Mobile3G,
Mobile4G
}
================================================
FILE: extensions/netinfo/src/ios/PluginBase.tsx
================================================
/*
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the iOS implementation of the plugin.
*/
import * as Types from '../common/Types';
import NetInfo from '../native-common/NetInfo';
export { NetInfo as default, Types };
================================================
FILE: extensions/netinfo/src/macos/PluginBase.tsx
================================================
/*
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the Mac implementation of the plugin.
*/
import * as Types from '../common/Types';
import NetInfo from '../native-common/NetInfo';
export { NetInfo as default, Types };
================================================
FILE: extensions/netinfo/src/native-common/NetInfo.tsx
================================================
/**
* NetInfo.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Native implementation of network information APIs.
*/
import * as RNNetInfo from '@react-native-community/netinfo';
import * as Types from '../common/Types';
import * as Interfaces from '../common/Interfaces';
export class NetInfo extends Interfaces.NetInfo {
constructor() {
super();
const onEventOccurredHandler = (state: RNNetInfo.NetInfoState) => {
this.connectivityChangedEvent.fire(state.isConnected);
};
RNNetInfo.addEventListener(onEventOccurredHandler);
}
isConnected(): Promise {
return RNNetInfo.fetch()
.then((state: RNNetInfo.NetInfoState) => state.isConnected)
.catch(() => Promise.reject('NetInfo.isConnected.fetch() failed'));
}
getType(): Promise {
return RNNetInfo.fetch().then((state: RNNetInfo.NetInfoState) => NetInfo._getNetworkTypeFromConnectionInfo(state));
}
private static _getNetworkTypeFromConnectionInfo(state: RNNetInfo.NetInfoState): Types.DeviceNetworkType {
if (state.type === RNNetInfo.NetInfoStateType.cellular) {
if (state.details === null || state.details.cellularGeneration === null) {
return Types.DeviceNetworkType.Unknown;
} else if (state.details.cellularGeneration === '2g') {
return Types.DeviceNetworkType.Mobile2G;
} else if (state.details.cellularGeneration === '3g') {
return Types.DeviceNetworkType.Mobile3G;
} else if (state.details.cellularGeneration === '4g') {
return Types.DeviceNetworkType.Mobile4G;
}
} else if (state.type === RNNetInfo.NetInfoStateType.wifi ) {
return Types.DeviceNetworkType.Wifi;
} else if (state.type === RNNetInfo.NetInfoStateType.ethernet ) {
return Types.DeviceNetworkType.Wifi;
} else if (state.type === RNNetInfo.NetInfoStateType.wimax ) {
return Types.DeviceNetworkType.Wifi;
} else if (state.type === RNNetInfo.NetInfoStateType.none) {
return Types.DeviceNetworkType.None;
}
return Types.DeviceNetworkType.Unknown;
}
}
export default new NetInfo();
================================================
FILE: extensions/netinfo/src/web/NetInfo.tsx
================================================
/**
* Video.tsx
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Web-specific implementation of the cross-platform Video abstraction.
*/
import * as Types from '../common/Types';
import * as Interfaces from '../common/Interfaces';
export class NetInfo extends Interfaces.NetInfo {
constructor() {
super();
const onEventOccuredHandler = () => {
this.connectivityChangedEvent.fire(navigator.onLine);
};
// Avoid accessing window if it's not defined (for test environment).
if (typeof(window) !== 'undefined') {
window.addEventListener('online', onEventOccuredHandler);
window.addEventListener('offline', onEventOccuredHandler);
}
}
isConnected(): Promise {
return Promise.resolve(navigator.onLine);
}
getType(): Promise {
return Promise.resolve(Types.DeviceNetworkType.Unknown);
}
}
export default new NetInfo();
================================================
FILE: extensions/netinfo/src/web/PluginBase.ts
================================================
/*
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the Web implementation of the plugin.
*/
import * as Types from '../common/Types';
import NetInfo from './NetInfo';
export { NetInfo as default, Types };
================================================
FILE: extensions/netinfo/src/windows/PluginBase.ts
================================================
/*
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the Windows implementation of the plugin.
*/
import * as Types from '../common/Types';
import NetInfo from '../native-common/NetInfo';
export { NetInfo as default, Types };
================================================
FILE: extensions/netinfo/tsconfig.json
================================================
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"strictPropertyInitialization": true,
"experimentalDecorators": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"noImplicitThis": true,
"noImplicitAny": true,
"skipLibCheck": true,
"declaration": true,
"noResolve": false,
"target": "es5",
"module": "commonjs",
"outDir": "dist",
"jsx": "react",
"lib": ["es2015.promise", "es5", "DOM"]
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}
================================================
FILE: extensions/video/.eslintrc.json
================================================
{
"parserOptions": {
"project": "./tsconfig.json"
},
"extends": ["skype", "skype/react"],
"rules": {},
"overrides": [
{
"files": [
"*.tsx"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off"
}
},
{
"files": [
"PluginBaseChecker.ts"
],
"rules": {
"@typescript-eslint/no-unused-vars": "off"
}
}
]
}
================================================
FILE: extensions/video/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
package-lock.json
# Optional npm cache directory
.npm
# Build artifacts
**/dist
# Miscellaneous user files
*.user
.vscode
.DS_STORE
================================================
FILE: extensions/video/.npmignore
================================================
/node_modules
/src/.vs
/src/bin
/src/obj
*.user
================================================
FILE: extensions/video/README.md
================================================
# reactxp-video
This module provides cross-platform support for video playback within the [ReactXP](https://microsoft.github.io/reactxp/) library.
## Documentation
For detailed documentation, look [here](https://microsoft.github.io/reactxp/docs/extensions/video.html).
### Prerequisites
* [ReactXP](https://github.com/microsoft/reactxp/)
## Contributing
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
You must sign a Contribution License Agreement (CLA) before your PR will be merged. This a one-time requirement for Microsoft projects in GitHub. You can read more about [Contribution License Agreements (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) on Wikipedia. You can sign the Microsoft Contribution License Agreement by visiting https://cla.microsoft.com/. Use your GitHub account to login.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
================================================
FILE: extensions/video/index.android.js
================================================
'use strict';
module.exports = require('./dist/android/PluginBase.js');
================================================
FILE: extensions/video/index.ios.js
================================================
'use strict';
module.exports = require('./dist/ios/PluginBase.js');
================================================
FILE: extensions/video/index.js
================================================
'use strict';
// Export web by default. Other platforms have custom index.[platform].js files
module.exports = require('./dist/web/PluginBase.js');
================================================
FILE: extensions/video/index.macos.js
================================================
'use strict';
module.exports = require('./dist/macos/PluginBase.js');
================================================
FILE: extensions/video/index.windows.js
================================================
'use strict';
module.exports = require('./dist/windows/PluginBase.js');
================================================
FILE: extensions/video/package.json
================================================
{
"name": "reactxp-video",
"version": "2.0.0",
"description": "Plugin for ReactXP that provides a video player component for all platforms",
"author": "ReactXP Team ",
"license": "MIT",
"scripts": {
"build": "tsc",
"lint": "eslint --ext .ts,.tsx src",
"lint:fix": "npm run lint -- --fix"
},
"dependencies": {
"lodash": "^4.17.15",
"react-native-video": "^5.0.2"
},
"peerDependencies": {
"react": "^16.3.0",
"reactxp": "^2.0.0",
"react-dom": "^16.3.0",
"react-native": ">=0.57",
"react-native-windows": "^0.57.1"
},
"devDependencies": {
"@types/lodash": "^4.14.149",
"@types/react-dom": "^16.9.4",
"@types/react-native": "^0.60.23",
"@typescript-eslint/eslint-plugin": "2.15.0",
"eslint": "6.8.0",
"eslint-config-skype": "1.5.0",
"eslint-plugin-import": "2.19.1",
"eslint-plugin-react": "7.17.0",
"react": "^16.3.0",
"reactxp": "^2.0.0",
"typescript": "3.7.4"
},
"types": "dist/web/PluginBase.d.ts"
}
================================================
FILE: extensions/video/src/android/PluginBase.tsx
================================================
/*
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the Android implementation of the plugin.
*/
import * as Types from '../common/Types';
import Video from '../native-common/Video';
export { Video as default, Types };
================================================
FILE: extensions/video/src/common/Interfaces.ts
================================================
/*
* Interfaces.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Interface definition for cross-platform ReactXP plugin for
* display videos and controlling video playback.
*/
import * as RX from 'reactxp';
import * as Types from './Types';
export abstract class Video extends RX.Component {
abstract seek(position: number): void;
abstract play(): void;
abstract pause(): void;
abstract mute(muted: boolean): void;
}
export interface PluginInterface {
Types: typeof Types;
default: typeof Video;
}
================================================
FILE: extensions/video/src/common/PluginBaseChecker.ts
================================================
/*
* PluginBaseChecker.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Type check all the pluginbase exports against the desired interface.
*/
import * as AndroidPlugin from '../android/PluginBase';
import * as iOSPlugin from '../ios/PluginBase';
import * as macOSPlugin from '../macos/PluginBase';
import * as WebPlugin from '../web/PluginBase';
import * as WindowsPlugin from '../windows/PluginBase';
import * as Interfaces from './Interfaces';
const _typeCheckerAndroid: Interfaces.PluginInterface = AndroidPlugin;
const _typeCheckeriOS: Interfaces.PluginInterface = iOSPlugin;
const _typeCheckermacOS: Interfaces.PluginInterface = macOSPlugin;
const _typeCheckerWeb: Interfaces.PluginInterface = WebPlugin;
const _typeCheckerWindows: Interfaces.PluginInterface = WindowsPlugin;
================================================
FILE: extensions/video/src/common/Types.ts
================================================
/*
* Types.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Type definitions to support the plugin.
*/
import * as React from 'react';
import { Types as RXTypes } from 'reactxp';
// Video interfaces from react-native-video
export interface VideoProgress {
currentTime: number;
playableDuration: number;
atValue?: number;
target?: number;
atTimeScale?: number;
}
export interface VideoInfo {
duration?: number;
naturalSize?: {
width: number;
height: number;
};
}
export interface VideoProps extends RXTypes.CommonStyledProps {
source: string | number;
accessibilityLabel?: string;
showControls?: boolean;
preload?: 'auto'|'metadata'|'none';
resizeMode?: 'contain'|'cover'|'stretch';
loop?: boolean;
authToken?: string;
shouldRedirectForAndroidHLS?: boolean;
onBuffer?: () => void;
onCanPlay?: () => void;
onCanPlayThrough?: () => void;
onEnded?: () => void;
onError?: () => void;
onLoadStart?: () => void;
onLoadedData?: (info: VideoInfo) => void;
onProgress?: (progress: VideoProgress) => void;
}
export abstract class Video extends React.Component {}
================================================
FILE: extensions/video/src/ios/PluginBase.tsx
================================================
/**
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the iOS implementation of the plugin.
*/
import * as Types from '../common/Types';
import Video from '../native-common/Video';
export { Video as default, Types };
================================================
FILE: extensions/video/src/macos/PluginBase.tsx
================================================
/**
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the MacOS implementation of the plugin.
*/
import * as Types from '../common/Types';
import Video from '../native-common/Video';
export { Video as default, Types };
================================================
FILE: extensions/video/src/native-common/Video.tsx
================================================
/**
* Video.tsx
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* RN-specific implementation of the cross-platform Video abstraction.
*/
import * as React from 'react';
import * as RX from 'reactxp';
import { default as RNVideo, VideoInfo, VideoBufferInfo } from 'react-native-video';
import * as Types from '../common/Types';
export interface VideoState {
isPlaying?: boolean;
isMuted?: boolean;
duration?: number;
}
class Video extends RX.Component {
private _mountedComponent: RNVideo|null = null;
constructor(props: Types.VideoProps) {
super(props);
this.state = {
isPlaying: false,
isMuted: false,
};
}
render() {
const source = typeof this.props.source === 'number'
? this.props.source
: { uri: this.props.source };
return (
);
}
private _onMount = (component: any) => {
this._mountedComponent = component as RNVideo;
};
private _onError = () => {
if (!this._mountedComponent) {
return;
}
if (this.props.onError) {
this.props.onError();
}
};
private _onLoad = () => {
if (!this._mountedComponent) {
return;
}
// The native control calls _onLoad only if you are
// not displaying the native play controls.
if (!this.props.showControls && this.props.onCanPlay) {
this.props.onCanPlay();
}
};
private _onLoadData = (loadInfo: VideoInfo) => {
if (!this._mountedComponent) {
return;
}
if (this.props.onLoadedData) {
this.props.onLoadedData(loadInfo);
}
// The native control calls _onLoad only if you are
// not displaying the native play controls.
if (this.props.showControls && this.props.onCanPlay) {
this.props.onCanPlay();
}
this.setState({ duration: loadInfo.duration });
};
private _onBuffer = (bufferInfo: VideoBufferInfo) => {
if (!this._mountedComponent) {
return;
}
if (bufferInfo.isBuffering) {
if (this.props.onBuffer) {
this.props.onBuffer();
}
} else {
if (this.props.onCanPlayThrough) {
this.props.onCanPlayThrough();
}
}
};
private _onEnd = () => {
if (!this._mountedComponent) {
return;
}
if (!this.props.loop) {
// Stop it so it doesn't auto-start.
this.setState({ isPlaying: false });
// Seek video to start.
this.seek(0);
}
if (this.props.onEnded) {
this.props.onEnded();
}
};
seek(position: number) {
if (this._mountedComponent) {
this._mountedComponent.seek(position);
}
}
seekPercent(percentage: number) {
if (this._mountedComponent) {
if (this.state.duration) {
this._mountedComponent.seek(this.state.duration * percentage);
}
}
}
play() {
this.setState({ isPlaying: true });
}
pause() {
this.setState({ isPlaying: false });
}
stop() {
this.pause();
this.seek(0);
}
mute(muted: boolean) {
this.setState({ isMuted: muted });
}
}
export default Video;
================================================
FILE: extensions/video/src/typings/react-native-video.d.ts
================================================
/**
* react-native-video.d.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Type definition for the React Native implementation
* of video player:
* https://github.com/brentvatne/react-native-video
*/
declare module 'react-native-video' {
import * as React from 'react';
import * as RN from 'react-native';
interface VideoProgress {
atValue: number;
target: number;
playableDuration: number;
atTimeScale: number;
currentTime: number;
}
interface VideoInfo {
duration?: number;
naturalSize?: {
width: number;
height: number;
};
}
interface VideoBufferInfo {
isBuffering: boolean;
}
interface VideoProps extends RN.ComponentPropsBase {
source: {
uri: string;
type?: string;
// Some versions of react-native-video may not support this option.
authToken?: string;
} | number;
resizeMode?: string;
poster?: string;
repeat?: boolean;
paused?: boolean;
muted?: boolean;
volume?: number; // 0 is muted, 1 is normal
rate?: number; // 0 is paused, 1 is normal
playInBackground?: boolean;
playWhenInactive?: boolean;
ignoreSilentSwitch?: 'ignore'|'obey';
disableFocus?: boolean;
controls?: boolean;
currentTime?: number;
progressUpdateInterval?: number;
useTextureView?: boolean;
onLoadStart?: () => void;
onLoad?: (info: VideoInfo) => void;
onBuffer?: (bufferInfo: VideoBufferInfo) => void;
onError?: () => void;
onProgress?: (progress: VideoProgress) => void;
onSeek?: () => void;
onEnd?: () => void;
onFullscreenPlayerWillPresent?: () => void;
onFullscreenPlayerDidPresent?: () => void;
onFullscreenPlayerWillDismiss?: () => void;
onFullscreenPlayerDidDismiss?: () => void;
onReadyForDisplay?: () => void;
onPlaybackStalled?: () => void;
onPlaybackResume?: () => void;
onPlaybackRateChange?: () => void;
onAudioFocusChanged?: () => void;
onAudioBecomingNoisy?: () => void;
style?: any;
}
export default class Video extends React.Component {
seek(position: number): void;
}
}
================================================
FILE: extensions/video/src/web/PluginBase.ts
================================================
/**
* PluginBase.ts
* Author: David de Regt
* Copyright: Microsoft 2016
*
* Base export for the Web implementation of the plugin.
*/
import * as Types from '../common/Types';
import Video from './Video';
export { Video as default, Types };
================================================
FILE: extensions/video/src/web/Video.tsx
================================================
/**
* Video.tsx
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Web-specific implementation of the cross-platform Video abstraction.
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as RX from 'reactxp';
import extend = require('lodash/extend');
import * as Types from '../common/Types';
class Video extends RX.Component {
componentDidMount() {
// We need to manually install the onEnded handler because. React doesn't support this.
const videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement | null;
if (videoDOM) {
videoDOM.onended = () => {
if (this.props.onEnded) {
this.props.onEnded();
}
};
}
}
componentWillUnmount() {
const videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement | null;
if (videoDOM) {
// Prevent Chrome based browsers to leak video elements
videoDOM.src = '';
}
}
render() {
let combinedStyles = extend(RX.Styles.combine(this.props.style), {
display: 'flex',
});
if (this.props.resizeMode === 'cover') {
combinedStyles = extend(combinedStyles, {
position: 'absolute',
minWidth: '100%',
minHeight: '100%',
width: 'auto',
height: 'auto',
top: '50%',
left: '50%',
WebkitTransform: 'translate(-50%,-50%)',
MozTransform: 'translate(-50%,-50%)',
msTransform: 'translate(-50%,-50%)',
transform: 'translate(-50%,-50%)',
});
} else if (this.props.resizeMode === 'contain') {
combinedStyles = extend(combinedStyles, {
width: '100%',
height: '100%',
});
} else {
combinedStyles = extend(combinedStyles, {
width: 'auto',
height: 'auto',
maxWidth: '100%',
maxHeight: '100%',
});
}
// The HTML version of video doesn't support numeric-based source
// references, only string-based URIs.
const source = typeof this.props.source === 'string' ? this.props.source : '';
return (
);
}
seek(position: number) {
const videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement | null;
if (videoDOM) {
videoDOM.currentTime = position;
}
}
seekPercent(percentage: number) {
const videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement | null;
if (videoDOM) {
videoDOM.currentTime = percentage * videoDOM.duration;
}
}
play() {
const videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement | null;
if (videoDOM) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
videoDOM.play();
}
}
pause() {
const videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement | null;
if (videoDOM) {
videoDOM.pause();
}
}
stop() {
const videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement | null;
if (videoDOM) {
videoDOM.pause();
videoDOM.currentTime = 0;
}
}
mute(muted: boolean) {
const videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement | null;
if (videoDOM) {
videoDOM.muted = muted;
}
}
private _onLoadedData = () => {
if (this.props.onLoadedData) {
const videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement | null;
if (videoDOM) {
const loadInfo: Types.VideoInfo = {
duration: videoDOM.duration,
naturalSize: {
width: videoDOM.videoWidth,
height: videoDOM.videoHeight,
},
};
this.props.onLoadedData(loadInfo);
}
}
};
private _onTimeUpdate = () => {
if (this.props.onProgress) {
const videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement | null;
if (videoDOM) {
this.props.onProgress({
currentTime: videoDOM.currentTime,
playableDuration: videoDOM.duration,
});
}
}
};
}
export default Video;
================================================
FILE: extensions/video/src/windows/PluginBase.ts
================================================
/**
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the web implementation of the plugin.
*/
import * as Types from '../common/Types';
import Video from './Video';
export { Video as default, Types };
================================================
FILE: extensions/video/src/windows/Video.tsx
================================================
/**
* Video.tsx
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Windows-specific implementation of the cross-platform Video abstraction.
*/
import * as React from 'react';
import * as RN from 'react-native';
import * as RX from 'reactxp';
import extend = require('lodash/extend');
import * as Types from '../common/Types';
export interface VideoState {
isPlaying?: boolean;
duration?: number;
}
class Video extends RX.Component {
// TODO(uwp): #694149 Not implemented
render() {
const combinedStyles = extend(RX.Styles.combine(this.props.style), {
backgroundColor: 'red',
});
return (
Video
);
}
seek(position: number) {
// TODO(uwp): #694149 Not implemented
}
seekPercent(percentage: number) {
// TODO(uwp): #694149 Not implemented
}
play() {
// TODO(uwp): #694149 Not implemented
}
pause() {
// TODO(uwp): #694149 Not implemented
}
stop() {
this.pause();
this.seek(0);
}
mute(muted: boolean) {
// TODO(uwp): #694149 Not implemented
}
}
export default Video;
================================================
FILE: extensions/video/tsconfig.json
================================================
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"strictPropertyInitialization": true,
"experimentalDecorators": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"noImplicitThis": true,
"noImplicitAny": true,
"skipLibCheck": true,
"declaration": true,
"noResolve": false,
"target": "es5",
"module": "commonjs",
"outDir": "dist",
"jsx": "react",
"lib": ["es2015.promise", "es2018.promise", "es5", "dom"]
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}
================================================
FILE: extensions/virtuallistview/.eslintrc.json
================================================
{
"parserOptions": {
"project": "./tsconfig.json"
},
"extends": ["skype", "skype/react"],
"rules": {},
"overrides": [
{
"files": [
"*.tsx"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off"
}
}
]
}
================================================
FILE: extensions/virtuallistview/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Optional npm cache directory
.npm
# Build artifacts
/dist
# Miscellaneous user files
*.user
.vscode
.DS_STORE
package-lock.json
================================================
FILE: extensions/virtuallistview/.npmignore
================================================
/node_modules
*.user
================================================
FILE: extensions/virtuallistview/LICENSE
================================================
ReactXP
Copyright (c) Microsoft Corporation
All rights reserved.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the Software), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies
or substantial portions of the Software.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
================================================
FILE: extensions/virtuallistview/README.md
================================================
# ReactXP VirtualListView
VirtualListView is a flexible, generalized list view built on [ReactXP](https://microsoft.github.io/reactxp/).
[Detailed documentation](https://microsoft.github.io/reactxp/docs/extensions/virtuallistview.html) can be found on the ReactXP site.
### Prerequisites
* [ReactXP](https://microsoft.github.io/reactxp/)
## Contributing
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
You must sign a Contribution License Agreement (CLA) before your PR will be merged. This a one-time requirement for Microsoft projects in GitHub. You can read more about [Contribution License Agreements (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) on Wikipedia. You can sign the Microsoft Contribution License Agreement by visiting https://cla.microsoft.com/. Use your GitHub account to login.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
================================================
FILE: extensions/virtuallistview/index.android.js
================================================
'use strict';
module.exports = require('./dist/VirtualListView.js');
================================================
FILE: extensions/virtuallistview/index.ios.js
================================================
'use strict';
module.exports = require('./dist/VirtualListView.js');
================================================
FILE: extensions/virtuallistview/index.js
================================================
'use strict';
module.exports = require('./dist/VirtualListView.js');
================================================
FILE: extensions/virtuallistview/index.macos.js
================================================
'use strict';
module.exports = require('./dist/VirtualListView.js');
================================================
FILE: extensions/virtuallistview/index.windows.js
================================================
'use strict';
module.exports = require('./dist/VirtualListView.js');
================================================
FILE: extensions/virtuallistview/package.json
================================================
{
"name": "reactxp-virtuallistview",
"version": "2.1.0",
"description": "General-purpose virtualized list view built on the cross-platform ReactXP library",
"author": "ReactXP Team ",
"license": "MIT",
"scripts": {
"build": "tsc",
"lint": "eslint --ext .ts,.tsx src",
"lint:fix": "npm run lint -- --fix"
},
"dependencies": {
"lodash": "^4.17.15"
},
"peerDependencies": {
"react": "^16.3",
"reactxp": "^2.0.0",
"react-dom": "^16.3",
"react-native": ">=0.57",
"react-native-windows": "^0.57.1"
},
"devDependencies": {
"@types/lodash": "^4.14.149",
"@types/react-dom": "^16.9.4",
"@typescript-eslint/eslint-plugin": "2.15.0",
"eslint": "6.8.0",
"eslint-config-skype": "1.5.0",
"eslint-plugin-import": "2.19.1",
"eslint-plugin-react": "7.17.0",
"react": "^16.3.0",
"reactxp": "^2.0.0",
"typescript": "3.7.4"
},
"homepage": "https://microsoft.github.io/reactxp/docs/extensions/virtuallistview.html",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/reactxp/"
},
"bugs": {
"url": "https://github.com/microsoft/reactxp/issues"
},
"types": "dist/VirtualListView.d.ts"
}
================================================
FILE: extensions/virtuallistview/src/VirtualListCell.tsx
================================================
/**
* VirtualListCell.tsx
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* This helper class is used in conjunction with VirtualListView. It represents the
* container for a single list item.
*/
import { createRef } from 'react';
import * as RX from 'reactxp';
import assert from './assert';
export interface VirtualListCellInfo {
key: string;
disableTouchOpacityAnimation?: boolean;
}
export interface VirtualListCellRenderDetails {
item: T;
selected: boolean;
focused: boolean;
}
export interface VirtualListCellProps extends RX.CommonProps {
// All callbacks should be prebound to optimize performance.
onLayout?: (itemKey: string, height: number) => void;
onAnimateStartStop?: (itemKey: string, start: boolean) => void;
onItemFocused?: (item: ItemInfo | undefined) => void;
onItemSelected?: (item: ItemInfo) => void;
renderItem: (details: VirtualListCellRenderDetails) => JSX.Element | JSX.Element[];
onKeyPress: (ev: RX.Types.KeyboardEvent) => void;
// Props that do not impact render (position is set by animated style).
itemKey: string | undefined;
left: number;
top: number;
width: number;
isVisible: boolean;
useNativeDriver?: boolean;
showOverflow?: boolean;
// We need to disable animation and the native animation driver in screen reader mode.
isScreenReaderModeEnabled?: boolean;
// Props that impact render, should be validated in shouldComponentUpdate.
isActive: boolean;
isFocused: boolean;
isSelected: boolean;
tabIndex?: number;
shouldUpdate: boolean;
item: ItemInfo | undefined;
}
interface StaticRendererProps extends RX.CommonProps {
shouldUpdate: boolean;
isFocused: boolean;
isSelected: boolean;
item: ItemInfo | undefined;
renderItem: (details: VirtualListCellRenderDetails) => JSX.Element | JSX.Element[];
}
const _styles = {
cellView: RX.Styles.createViewStyle({
position: 'absolute',
}),
overflowVisible: RX.Styles.createViewStyle({
overflow: 'visible',
}),
overflowHidden: RX.Styles.createViewStyle({
overflow: 'hidden',
}),
};
const _isNativeMacOS = RX.Platform.getType() === 'macos';
const _skypeEaseInAnimationCurve = RX.Animated.Easing.CubicBezier(1, 0, 0.78, 1);
const _skypeEaseOutAnimationCurve = RX.Animated.Easing.CubicBezier(0.33, 0, 0, 1);
const _keyCodeEnter = 13;
const _keyCodeSpace = 32;
const _keyCodeReturn = 3;
export class VirtualListCell extends RX.Component, RX.Stateless> {
// Helper class used to render child elements. If we know that none of the children changed - we would like to skip
// the render completely, to improve performance.
// eslint-disable-next-line @typescript-eslint/member-naming
private static StaticRenderer = class
extends RX.Component, RX.Stateless> {
constructor(props: StaticRendererProps) {
super(props);
}
shouldComponentUpdate(nextProps: StaticRendererProps): boolean {
return nextProps.shouldUpdate ||
this.props.isFocused !== nextProps.isFocused ||
this.props.isSelected !== nextProps.isSelected;
}
render() {
// If we don't have an item to render, return null here
if (!this.props.item) {
return null;
}
return (
{ this.props.renderItem({
item: this.props.item,
selected: this.props.isSelected,
focused: this.props.isFocused,
}) }
);
}
};
private static _hiddenTopValue = -32768;
private _isVisible = false;
private _top: number = VirtualListCell._hiddenTopValue;
private _calculatedHeight = 0;
private _topValue: RX.Animated.Value;
private _leftValue: RX.Animated.Value;
private _widthValue: RX.Animated.Value;
private _ref = createRef();
// we need to split style for position and width because we use native driver for position,
// but native driver doesnt support width
private _animatedStylePosition: RX.Types.AnimatedViewStyleRuleSet;
private _animatedStyleWidth: RX.Types.AnimatedViewStyleRuleSet;
private _topAnimation: RX.Types.Animated.CompositeAnimation | undefined;
private _itemKey: string | undefined;
constructor(props: VirtualListCellProps) {
super(props);
this._isVisible = props.isVisible;
this._top = props.top;
this._itemKey = props.itemKey;
const topValue = this._isVisible ? this._top : VirtualListCell._hiddenTopValue;
this._topValue = RX.Animated.createValue(topValue);
const leftValue = props.left || 0;
this._leftValue = RX.Animated.createValue(leftValue);
if (!props.isScreenReaderModeEnabled && !_isNativeMacOS) {
// On native platforms, we'll stick with translate[X|Y] because it has a performance advantage.
this._animatedStylePosition = RX.Styles.createAnimatedViewStyle({
transform: [{
translateY: this._topValue,
}],
left: this._leftValue,
});
} else {
// We need to work around an IE-specific bug. It doesn't properly handle
// translateY in this case. In particular, if separate translations are used
// within the item itself, it doesn't handle that combination.
this._animatedStylePosition = RX.Styles.createAnimatedViewStyle({
top: this._topValue,
left: this._leftValue,
});
}
this._widthValue = RX.Animated.createValue(props.width || 0);
this._animatedStyleWidth = RX.Styles.createAnimatedViewStyle({
width: this._widthValue,
});
}
UNSAFE_componentWillReceiveProps(nextProps: VirtualListCellProps) {
// If it's inactive, it had better be invisible.
assert(nextProps.isActive || !nextProps.isVisible);
assert(nextProps.useNativeDriver === this.props.useNativeDriver);
// All callbacks should be prebound to optimize performance.
assert(this.props.onLayout === nextProps.onLayout, 'onLayout callback changed');
assert(this.props.onItemSelected === nextProps.onItemSelected, 'onItemSelected callback changed');
assert(this.props.onItemFocused === nextProps.onItemFocused, 'onItemFocused callback changed');
assert(this.props.onAnimateStartStop === nextProps.onAnimateStartStop, 'onAnimateStartStop callback changed');
assert(this.props.renderItem === nextProps.renderItem, 'renderItem callback changed');
// We assume this prop doesn't change for perf reasons. Callers should modify
// the key to force an unmount/remount if these need to change.
assert(this.props.isScreenReaderModeEnabled === nextProps.isScreenReaderModeEnabled);
this.setItemKey(nextProps.itemKey);
if (this.props.left !== nextProps.left) {
this._leftValue.setValue(nextProps.left);
}
if (this.props.width !== nextProps.width) {
this._widthValue.setValue(nextProps.width);
}
if (this.props.itemKey !== nextProps.itemKey) {
this.setVisibility(nextProps.isVisible);
this.setTop(nextProps.top);
}
}
shouldComponentUpdate(nextProps: VirtualListCellProps): boolean {
// No need to update inactive (recycled) cells.
if (!nextProps.isActive) {
return false;
}
// Check if props important for rendering changed.
if (this.props.tabIndex !== nextProps.tabIndex ||
this.props.itemKey !== nextProps.itemKey ||
this.props.isFocused !== nextProps.isFocused ||
this.props.isSelected !== nextProps.isSelected) {
return true;
}
return nextProps.shouldUpdate;
}
componentDidUpdate(prevProps: VirtualListCellProps) {
// We need to simulate a layout event here because recycled cells may not
// generate a layout event if the cell contents haven't changed.
if (this.props.onLayout && this.props.isActive && this._calculatedHeight && this._itemKey) {
this.props.onLayout(this._itemKey, this._calculatedHeight);
}
}
componentWillUnmount() {
// Stop any pending animation.
if (this._topAnimation) {
this._topAnimation.stop();
}
}
setVisibility(isVisible: boolean) {
if (isVisible !== this._isVisible) {
this._isVisible = isVisible;
if (this._topAnimation) {
this._topAnimation.stop();
}
this._topValue.setValue(this._isVisible ? this._top : VirtualListCell._hiddenTopValue);
}
}
isVisible() {
return this._isVisible;
}
setTop(top: number, animate = false, animationDelay = 0, animationOvershoot = 0) {
if (top !== this._top) {
this._top = top;
if (this._isVisible) {
let isReplacingPendingAnimation = false;
// Stop any pending animation.
if (this._topAnimation) {
const animationToCancel = this._topAnimation;
// The call to stop() will invoke the stop callback. If we are
// going to replace a pending animation, we'll make it look like
// a continuous animation rather than calling the callback multiple
// times. If we're not replacing the animation with another animation,
// allow the onAnimateStartStop to proceed.
if (animate) {
this._topAnimation = undefined;
}
animationToCancel.stop();
isReplacingPendingAnimation = true;
}
if (animate) {
if (animationOvershoot !== 0) {
this._topAnimation = RX.Animated.sequence([
RX.Animated.timing(this._topValue, {
toValue: top + animationOvershoot,
duration: 200,
delay: animationDelay,
easing: _skypeEaseInAnimationCurve,
}),
RX.Animated.timing(this._topValue, {
toValue: top,
duration: 400,
easing: _skypeEaseOutAnimationCurve,
}),
]);
} else {
this._topAnimation = RX.Animated.timing(this._topValue, {
toValue: top,
duration: 200,
delay: animationDelay,
easing: RX.Animated.Easing.InOut(),
});
}
if (!isReplacingPendingAnimation && this.props.onAnimateStartStop && this._itemKey) {
this.props.onAnimateStartStop(this._itemKey, true);
}
this._topAnimation.start(() => {
// Has the animation been canceled?
if (this._topAnimation) {
this._topAnimation = undefined;
if (this.props.onAnimateStartStop && this._itemKey) {
this.props.onAnimateStartStop(this._itemKey, false);
}
}
});
} else {
this._topValue.setValue(top);
}
}
}
}
cancelPendingAnimation() {
if (this._topAnimation) {
this._topAnimation.stop();
}
}
setItemKey(key: string | undefined) {
this._itemKey = key;
}
getTop() {
return this._top;
}
focus() {
if (this._ref.current && this.props.tabIndex) {
const virtualCellComponent = this._ref.current;
virtualCellComponent.focus();
}
}
render() {
const overflow = this.props.showOverflow ? _styles.overflowVisible : _styles.overflowHidden;
return (
);
}
private _onKeyPress = (e: RX.Types.KeyboardEvent) => {
const isSelectItemKeyPress = e.keyCode === _keyCodeEnter ||
e.keyCode === _keyCodeSpace ||
e.keyCode === _keyCodeReturn;
if (isSelectItemKeyPress && this.props.onItemSelected && this.props.item) {
this.props.onItemSelected(this.props.item);
e.stopPropagation();
}
if (this.props.onKeyPress) {
this.props.onKeyPress(e);
}
};
private _onFocus = (e: RX.Types.FocusEvent) => {
if (this.props.onItemFocused) {
this.props.onItemFocused(this.props.item);
}
};
private _onPress = (e: RX.Types.SyntheticEvent) => {
if (this.props.onItemSelected && this.props.item) {
this.props.onItemSelected(this.props.item);
e.stopPropagation();
}
};
private _onBlur = (e: RX.Types.FocusEvent) => {
if (this.props.onItemFocused) {
this.props.onItemFocused(undefined);
}
};
private _onLayout = (layoutInfo: RX.Types.ViewOnLayoutEvent) => {
if (this.props.onLayout && this.props.isActive && this._itemKey) {
this._calculatedHeight = layoutInfo.height;
this.props.onLayout(this._itemKey, layoutInfo.height);
}
};
}
================================================
FILE: extensions/virtuallistview/src/VirtualListView.tsx
================================================
/**
* VirtualListView.tsx
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* A cross-platform virtualized list view supporting variable-height items and
* methods to navigate to specific items by index.
*
* Misc notes to help understand the flow:
* 1. There are only a few ways to enter calculation flows:
* * _updateStateFromProps: We got new props
* * _onLayoutContainer: Our outer container rendered and/or changed size
* * _onLayoutItem: An item rendered and/or changed changed size
* * _onScroll: The user scrolled the container
* Everything else is a helper function for these four entry points.
* 2. We largely ignore the React lifecycle here. We completely eschew state in favor of forceUpdate when
* we know that we need to call render(). We cheat and use the animation code to move items and make
* them opaque/invisible at the right time outside of the render cycle.
* 3. Items are rendered in containers called "cells". Cells are allocated on demand and given their own keys.
* When an item is no longer within the view port (e.g. in response to the the user scrolling), the corresponding
* cell is recycled to avoid unmounting and mounting. These recycled cells are rendered in a position that is
* not visible to the user. When a new cell is needed, we consult the recycled cell list to find one that matches
* the specified "template" of the new item. Callers should set the template field in a way that all similar items
* share the same template. This will minimize the amount of work that React needs to be done to reuse the recycled
* cell.
* 3. The intended render flow is as follows:
* * Start filling hidden items from top down
* * Wait for items to be measured (or if heights are known, then bypass this step)
* * Set the translation of all items such that they appear in view at the same time without new items popping
* into existence afterward.
* 4. We address the issue of unexpected item heights tracking _heightAboveRenderAdjustment. When this is
* non-zero, it means that our initial guess for one or more items was wrong, so the _containerHeight is
* currently incorrect. Correcting this is an expensive and potentially disruptive action because it
* involves setting the container height, repositioning all visible cells and setting the scroll
* position all in the same frame if possible.
*/
import * as _ from 'lodash';
import { createRef, RefObject } from 'react';
import * as RX from 'reactxp';
import assert from './assert';
import { VirtualListCell, VirtualListCellInfo, VirtualListCellRenderDetails } from './VirtualListCell';
// Specifies information about each item to be displayed in the list.
export interface VirtualListViewItemInfo extends VirtualListCellInfo {
// Specifies the known height of the item or a best guess if the height isn't known.
height: number;
// Specifies that the height is not known and needs to be measured dynamically.
// This has a big perf overhead because it requires a double layout (once offscreen
// to measure the item). It also disables cell recycling. Wherever possible, it
// should be avoided, especially for perf-critical views.
measureHeight?: boolean;
// Specify the same "template" string for items that are rendered
// with identical or similar view hierarchies. When a template is specified,
// the list view attempts to recycle cells whose templates match. When an item
// scrolls off the screen and others appear on screen, the contents of the
// cell are simply updated rather than torn down and rebuilt.
template?: string;
// Is the item navigable by keyboard or through accessibility mechanisms?
isNavigable?: boolean;
}
export interface VirtualListViewCellRenderDetails extends VirtualListCellRenderDetails {
}
export interface VirtualListViewProps
extends RX.CommonStyledProps> {
testId?: string;
// Ordered list of descriptors for items to display in the list.
itemList: ItemInfo[];
// Callback for rendering item when it becomes visible within view port.
renderItem: (renderDetails: VirtualListCellRenderDetails) => JSX.Element | JSX.Element[];
onItemSelected?: (item: ItemInfo) => void;
onItemFocused?: (item: ItemInfo | undefined) => void;
initialSelectedKey?: string;
// Optional padding around the scrolling content within the list.
padding?: number;
// If true, allows each item to overflow its visible cell boundaries; by default,
// item contents are clipped to cell boundaries.
showOverflow?: boolean;
// Should the list animate additions, removals and moves within the list?
animateChanges?: boolean;
// By default, VirtualListView re-renders every item during the render. Setting
// this flag to true allows the list view to re-render only items from itemList
// whose descriptor has changed, thus avoiding unnecessary rendering. It uses
// _.isEqual to perform this check. In this mode, renderItem should not depend
// on any external state, only on VirtualListViewItemInfo, to render item.
skipRenderIfItemUnchanged?: boolean;
// Pass-through properties for scroll view.
keyboardDismissMode?: 'none' | 'interactive' | 'on-drag';
keyboardShouldPersistTaps?: boolean;
disableScrolling?: boolean;
scrollsToTop?: boolean; // iOS only, scroll to top when user taps on status bar
disableBouncing?: boolean; // iOS only, bounce override
scrollIndicatorInsets?: {
top: number;
left: number;
bottom: number;
right: number;
}; // iOS only
scrollEventThrottle?: number;
onScroll?: (scrollTop: number, scrollLeft: number) => void;
onLayout?: (e: RX.Types.ViewOnLayoutEvent) => void;
scrollXAnimatedValue?: RX.Types.AnimatedValue;
scrollYAnimatedValue?: RX.Types.AnimatedValue;
// Use this if you want to vertically offset the focused item when using keyboard nav
keyboardFocusScrollOffset?: number;
// Logging callback to debug issues related to the VirtualListView.
logInfo?: (textToLog: string) => void;
}
export interface VirtualListViewState {
lastFocusedItemKey?: string;
isFocused?: boolean;
selectedItemKey?: string;
}
export interface VirtualCellInfo {
cellRef: RefObject>;
virtualKey: string;
itemTemplate?: string;
isHeightConstant: boolean;
height: number;
cachedItemKey: string;
top: number;
isVisible: boolean;
shouldUpdate: boolean;
}
enum FocusDirection {
Up = -1,
Down = 1
}
const _styles = {
scrollContainer: RX.Styles.createScrollViewStyle({
flex: 1,
position: 'relative',
flexDirection: 'column',
}),
staticContainer: RX.Styles.createViewStyle({
flex: 1,
flexDirection: 'column',
}),
};
const _isNativeAndroid = RX.Platform.getType() === 'android';
const _isNativeIOS = RX.Platform.getType() === 'ios';
const _isWeb = RX.Platform.getType() === 'web';
// How many items with unknown heights will we allow? A larger value will fill the view more
// quickly but will result in a bunch of long-running work that can cause frame skips during
// animations.
const _maxSimultaneousMeasures = 16;
// Recycled cells remain mounted to reduce the allocations and deallocations.
// This value controls how many we maintain before culling.
const _maxRecycledCells = 50;
const _maxRecycledCellsForAccessibility = 0;
const _virtualKeyPrefix = 'vc_';
const _accessibilityVirtualKeyPrefix = 'ac_';
// Key codes used on web/RN (keycodes for arrows are different between web and native, unfortunately)
// (a resolution for https://github.com/Microsoft/reactxp/issues/419 will make this look better, hopefuly)
const _keyCodeUpArrow = _isWeb ? 38 : 19;
const _keyCodeDownArrow = _isWeb ? 40 : 20;
export class VirtualListView
extends RX.Component, VirtualListViewState> {
private _lastScrollTop = 0;
private _layoutHeight = 0;
private _layoutWidth = 0;
// Cache the width for rendered items for reuse/optimization
private _contentWidth = -1;
private _isMounted = false;
// Controls the full height of the scrolling view, independent of the view port height
private _containerHeight = 0;
private _containerHeightValue = RX.Animated.createValue(this._containerHeight);
private _containerAnimatedStyle = RX.Styles.createAnimatedViewStyle({
height: this._containerHeightValue,
});
// A dictionary of items that maps item keys to item indexes.
private _itemMap = new Map();
private _scrollViewRef = createRef();
// When we need to actually re-render, mark this until it's resolved
private _isRenderDirty = false;
// Number of pending item animations. We defer some actions while animations are pending.
private _pendingAnimations = new Set();
// We attempt to guess the size of items before we render them, but if we're wrong, we need to accumulate the guess
// error so that we can correct it later.
private _heightAboveRenderAdjustment = 0;
// Cache the heights of blocks of the list
private _heightAboveRenderBlock = 0;
private _heightOfRenderBlock = 0;
private _heightBelowRenderBlock = 0;
// Count the number of items above, in, and below the render block
private _itemsAboveRenderBlock = 0;
private _itemsInRenderBlock = 0;
private _itemsBelowRenderBlock = 0;
// Items that we're waiting on a measure from
private _pendingMeasurements = new Set();
// We first render items to fill the visible screen, and then render past it in another render pass.
private _isInitialFillComplete = false;
// Save a height cache of things that are no longer being rendered because we may scroll them off screen and still
// want to know what their height is to calculate the size.
private _heightCache = new Map();
// Next cell key. We keep incrementing this value so we always generate unique keys.
private static _nextCellKey = 1;
// Cells that contain visible items.
private _activeCells = new Map>();
// Cells that were previously allocated but no longer contain items that are visible.
// They are kept around and reused to avoid exceess allocations.
private _recycledCells: VirtualCellInfo[] = [];
// List of cells that are rendered
private _navigatableItemsRendered: {
key: string;
// eslint-disable-next-line @typescript-eslint/camelcase
vc_key: string;
}[] = [];
private _pendingFocusDirection: FocusDirection | undefined;
// Recycled cells remain mounted to reduce the allocations and deallocations.
// This value controls how many we maintain before culling.
private _maxRecycledCells = _maxRecycledCells;
private _isScreenReaderEnabled = false;
// Fraction of screen height that we render above and below the visible screen.
private _renderOverdrawFactor = 0.5;
private _minOverdrawAmount = 512;
private _maxOverdrawAmount = 4096;
// These must be at least as big as the numbers above to avoid feedback loops.
private _cullFraction = 1.0;
private _minCullAmount = this._minOverdrawAmount * 2;
constructor(props: VirtualListViewProps) {
super(props);
this._updateStateFromProps(props, true);
this.state = {
lastFocusedItemKey: _.some(props.itemList, item => item.key === props.initialSelectedKey) ?
props.initialSelectedKey :
undefined,
selectedItemKey: _.some(props.itemList, item => item.key === props.initialSelectedKey) ?
props.initialSelectedKey :
undefined,
};
}
UNSAFE_componentWillReceiveProps(nextProps: VirtualListViewProps): void {
if (!_.isEqual(this.props, nextProps)) {
this._updateStateFromProps(nextProps, false);
}
}
UNSAFE_componentWillUpdate(nextProps: VirtualListViewProps, nextState: VirtualListViewState) {
const updatedState: Partial = {};
let updateState = false;
if (nextState.lastFocusedItemKey && !_.some(nextProps.itemList, item => item.key === nextState.lastFocusedItemKey)) {
updateState = true;
updatedState.lastFocusedItemKey = undefined;
}
if (nextState.selectedItemKey && !_.some(nextProps.itemList, item => item.key === nextState.selectedItemKey)) {
updateState = true;
updatedState.selectedItemKey = undefined;
}
if (updateState) {
this.setState(updatedState);
}
}
private _setupForAccessibility() {
if (this.props.logInfo) {
this.props.logInfo('Screen reader enabled.');
}
this._isScreenReaderEnabled = true;
if (_isNativeIOS || _isNativeAndroid) {
// Clear recycled cells and turn off recycling.
if (this._recycledCells.length > 0) {
this._recycledCells = [];
this._isRenderDirty = true;
}
this._maxRecycledCells = _maxRecycledCellsForAccessibility;
}
}
private _tearDownForAccessibility() {
if (this.props.logInfo) {
this.props.logInfo('Screen reader disabled.');
}
this._isScreenReaderEnabled = false;
if (_isNativeIOS || _isNativeAndroid) {
// Enable recycling.
this._maxRecycledCells = _maxRecycledCells;
}
}
private _isAndroidScreenReaderEnabled() {
return this._isScreenReaderEnabled && _isNativeAndroid;
}
private _updateStateFromProps(props: VirtualListViewProps, initialBuild: boolean) {
if (props.logInfo) {
props.logInfo('Rebuilding VirtualListView State - initial: ' + initialBuild +
', items: ' + props.itemList.length);
}
if (initialBuild && props.skipRenderIfItemUnchanged) {
// When we are using smart rerender we can make overdraw much larger
this._renderOverdrawFactor = 5;
this._minOverdrawAmount = 2048;
this._maxOverdrawAmount = 4096;
this._cullFraction = 6;
this._minCullAmount = 3072;
}
if (initialBuild || !_.isEqual(this.props.itemList, props.itemList)) {
this._handleItemListChange(props);
this._calcNewRenderedItemState(props);
}
this._renderIfDirty(props);
}
private _handleItemListChange(props: VirtualListViewProps) {
// Build a new item map.
const newItemMap = new Map();
let itemIndex = -1;
for (const item of props.itemList) {
itemIndex++;
// Make sure there are no duplicate keys.
if (newItemMap.has(item.key)) {
assert(false, 'Found a duplicate key: ' + item.key);
if (props.logInfo) {
props.logInfo('Item with key ' + item.key + ' is duplicated at positions ' + itemIndex +
' and ' + newItemMap.get(item.key)!);
}
}
newItemMap.set(item.key, itemIndex);
if (this.props && this.props.itemList) {
const cell = this._activeCells.get(item.key);
if (cell) {
const oldItemIndex = this._itemMap.get(item.key);
if (oldItemIndex === undefined) {
cell.shouldUpdate = true;
} else {
const oldItem = this.props.itemList[oldItemIndex];
if (this.props.skipRenderIfItemUnchanged || !_.isEqual(oldItem, item)) {
cell.shouldUpdate = true;
}
}
}
}
}
// Stop tracking the heights of deleted items.
const oldItems = (this.props && this.props.itemList) ? this.props.itemList : [];
itemIndex = -1;
for (const item of oldItems) {
itemIndex++;
if (!newItemMap.has(item.key)) {
// If we're deleting an item that's above the current render block,
// update the adjustment so we avoid an unnecessary scroll.
// Update focused item if it's the one removed, if we're unable to, reset focus
if (item.key === this.state.lastFocusedItemKey) {
if (!this._focusSubsequentItem(FocusDirection.Down, false, false) &&
!this._focusSubsequentItem(FocusDirection.Up, false, false)) {
this.setState({ lastFocusedItemKey: undefined });
}
}
if (itemIndex < this._itemsAboveRenderBlock) {
this._heightAboveRenderAdjustment += this._getHeightOfItem(oldItems[itemIndex]);
}
this._heightCache.delete(item.key);
this._pendingMeasurements.delete(item.key);
// Recycle any deleted active cells up front so they can be recycled below.
if (this._activeCells.has(item.key)) {
this._recycleCell(item.key);
}
}
}
const overdrawAmount = this._calcOverdrawAmount();
const renderBlockTopLimit = this._lastScrollTop - overdrawAmount;
const renderBlockBottomLimit = this._lastScrollTop + this._layoutHeight + overdrawAmount;
let yPosition = this._heightAboveRenderAdjustment;
let lookingForStartOfRenderBlock = true;
this._itemsAboveRenderBlock = 0;
this._itemsInRenderBlock = 0;
// Determine the new bounds of the render block.
itemIndex = -1;
for (const item of props.itemList) {
itemIndex++;
const itemHeight = this._getHeightOfItem(item);
yPosition += itemHeight;
if (yPosition <= renderBlockTopLimit) {
if (this._activeCells.has(item.key)) {
this._recycleCell(item.key);
}
} else {
if (lookingForStartOfRenderBlock) {
this._itemsAboveRenderBlock = itemIndex;
lookingForStartOfRenderBlock = false;
}
if (yPosition - itemHeight < renderBlockBottomLimit) {
// We're within the render block.
this._itemsInRenderBlock++;
if (this._activeCells.has(item.key)) {
this._setCellTopAndVisibility(item.key, this._shouldShowItem(item, props),
yPosition - itemHeight, !!props.animateChanges);
} else {
this._allocateCell(item.key, item.template, itemIndex, !item.measureHeight, item.height,
yPosition - itemHeight, this._shouldShowItem(item, props));
if (!this._isItemHeightKnown(item)) {
this._pendingMeasurements.add(item.key);
}
}
} else {
// We're past the render block.
if (this._activeCells.has(item.key)) {
this._recycleCell(item.key);
}
}
}
}
// Replace the item map with the updated version.
this._itemMap = newItemMap;
this._itemsBelowRenderBlock = props.itemList.length - this._itemsAboveRenderBlock -
this._itemsInRenderBlock;
this._heightAboveRenderBlock = this._calcHeightOfItems(props, 0, this._itemsAboveRenderBlock - 1);
this._heightOfRenderBlock = this._calcHeightOfItems(props, this._itemsAboveRenderBlock,
this._itemsAboveRenderBlock + this._itemsInRenderBlock - 1);
this._heightBelowRenderBlock = this._calcHeightOfItems(props,
this._itemsAboveRenderBlock + this._itemsInRenderBlock, props.itemList.length - 1);
// Pre-populate the container height with known values early - if there are dynamically sized items in the list, this will be
// corrected during the onLayout phase
if (this._containerHeight === 0) {
this._containerHeight = this._heightAboveRenderBlock + this._heightOfRenderBlock + this._heightBelowRenderBlock;
this._containerHeightValue.setValue(this._containerHeight);
}
}
private _calcOverdrawAmount() {
return this._isInitialFillComplete ?
Math.min(Math.max(this._layoutHeight * this._renderOverdrawFactor, this._minOverdrawAmount), this._maxOverdrawAmount) :
0;
}
private _onLayoutContainer = (e: RX.Types.ViewOnLayoutEvent) => {
if (!this._isMounted) {
return;
}
let layoutWidth = e.width;
if (this.props.padding) {
layoutWidth -= this.props.padding;
}
const layoutHeight = e.height;
if (layoutWidth !== this._layoutWidth) {
if (this.props.logInfo) {
this.props.logInfo('New layout width: ' + layoutWidth);
}
this._layoutWidth = layoutWidth;
this._resizeAllItems(this.props);
}
if (layoutHeight !== this._layoutHeight) {
if (this.props.logInfo) {
this.props.logInfo('New layout height: ' + layoutHeight);
}
this._layoutHeight = layoutHeight;
this._calcNewRenderedItemState(this.props);
this._renderIfDirty(this.props);
// See if we have accumulated enough error to require an adjustment.
this._reconcileCorrections(this.props);
}
if (this.props.onLayout) {
this.props.onLayout(e);
}
};
private _onLayoutItem = (itemKey: string, newHeight: number) => {
if (!this._isMounted) {
return;
}
const itemIndex = this._itemMap.get(itemKey);
// Because this event is async on some platforms, the index may have changed or
// the item could have been removed by the time the event arrives.
if (itemIndex === undefined) {
return;
}
const item = this.props.itemList[itemIndex];
const oldHeight = this._getHeightOfItem(item);
if (!item.measureHeight) {
// Trust constant-height items, even if the layout tells us otherwise.
// We shouldn't even get this callback, since we don't specify an onLayout in this case.
if (this.props.logInfo) {
this.props.logInfo('Item ' + itemKey + ' listed as known height (' + oldHeight +
'), but got an itemOnLayout anyway? (Reported Height: ' + newHeight + ')');
}
return;
}
this._heightCache.set(itemKey, newHeight);
if (itemIndex < this._itemsAboveRenderBlock || itemIndex >= this._itemsAboveRenderBlock + this._itemsInRenderBlock) {
// Getting a response for a culled item (no longer in tracked render block), so track the height but don't update anything.
return;
}
let needsRecalc = false;
if (oldHeight !== newHeight) {
if (this.props.logInfo) {
this.props.logInfo('onLayout: Item Height Changed: ' + itemKey + ' - Old: ' + oldHeight + ', New: ' + newHeight);
}
this._heightOfRenderBlock += (newHeight - oldHeight);
if (this._isInitialFillComplete) {
// See if there are any visible items before this one.
let foundVisibleItemBefore = false;
for (let i = this._itemsAboveRenderBlock; i < this._itemsAboveRenderBlock + this._itemsInRenderBlock; i++) {
if (this._isCellVisible(this.props.itemList[i].key)) {
foundVisibleItemBefore = true;
break;
}
if (i === itemIndex) {
break;
}
}
if (!foundVisibleItemBefore) {
// It's in a safe block above the known-height render area.
if (this.props.logInfo) {
this.props.logInfo('Added delta to fake space offset: ' + (oldHeight - newHeight) + ' -> ' +
(this._heightAboveRenderAdjustment + (oldHeight - newHeight)));
}
this._heightAboveRenderAdjustment += (oldHeight - newHeight);
}
}
needsRecalc = true;
}
this._pendingMeasurements.delete(itemKey);
needsRecalc = needsRecalc || this._pendingMeasurements.size === 0;
if (needsRecalc) {
this._calcNewRenderedItemState(this.props);
this._renderIfDirty(this.props);
}
// See if we have accumulated enough error to require an adjustment.
this._reconcileCorrections(this.props);
};
private _onAnimateStartStopItem = (itemKey: string, animateStart: boolean) => {
if (this._isMounted) {
const hasAnimation = this._pendingAnimations.has(itemKey);
if (animateStart) {
assert(!hasAnimation, 'unexpected animation start');
this._pendingAnimations.add(itemKey);
} else {
assert(hasAnimation, 'unexpected animation complete');
this._pendingAnimations.delete(itemKey);
// We defer this because there are cases where we can cancel animations
// because we've received new props. We don't want to re-enter the
// routines with the old props, so we'll defer and wait for this.props
// to be updated.
_.defer(() => {
if (this._isMounted) {
if (this._pendingAnimations.size === 0 && this._isMounted) {
// Perform deferred actions now that all animations are complete.
this._reconcileCorrections(this.props);
}
}
});
}
}
};
private _onScroll = (scrollTop: number, scrollLeft: number) => {
if (this._lastScrollTop === scrollTop) {
// Already know about it!
if (this.props.logInfo) {
this.props.logInfo('Got Known Scroll: ' + scrollTop);
}
return;
}
this._lastScrollTop = scrollTop;
// We scrolled, so update item state.
this._calcNewRenderedItemState(this.props);
this._renderIfDirty(this.props);
// See if we have accumulated enough error to require an adjustment.
this._reconcileCorrections(this.props);
if (this.props.onScroll) {
this.props.onScroll(scrollTop, scrollLeft);
}
};
// Some things to keep in mind during this function:
// * Item heights are all in a fixed state from the beginning to the end of the function. The total
// container height will never change through the course of the function. We're only deciding what
// to bother rendering/culling and where to place items within the container.
// * We're going to, in order: cull unnecessary items, add new items, and position them within the container.
private _calcNewRenderedItemState(props: VirtualListViewProps): void {
if (this._layoutHeight === 0) {
// Wait until we get a height before bothering.
return;
}
if (props.itemList.length === 0) {
// Can't possibly be rendering anything.
return;
}
if (this._pendingMeasurements.size > 0) {
// Don't bother if we're still measuring things. Wait for the last batch to end.
return;
}
// What's the top/bottom line that we'll cull items that are wholly outside of?
const cullMargin = Math.max(this._layoutHeight * this._cullFraction, this._minCullAmount);
const topCullLine = this._lastScrollTop - cullMargin;
const bottomCullLine = this._lastScrollTop + this._layoutHeight + cullMargin;
// Do we need to cut anything out of the top because we've scrolled away from it?
while (this._itemsInRenderBlock > 0) {
const itemIndex = this._itemsAboveRenderBlock;
const item = props.itemList[itemIndex];
if (!this._isItemHeightKnown(item)) {
break;
}
const itemHeight = this._getHeightOfItem(item);
if (this._heightAboveRenderAdjustment + this._heightAboveRenderBlock + itemHeight >= topCullLine) {
// We're rendering up to the top render line, so don't need to nuke any more.
break;
}
this._itemsInRenderBlock--;
this._heightOfRenderBlock -= itemHeight;
this._itemsAboveRenderBlock++;
this._heightAboveRenderBlock += itemHeight;
this._recycleCell(item.key);
if (props.logInfo) {
props.logInfo('Culled Item From Top: ' + item.key);
}
}
// Do we need to cut anything out of the bottom because we've scrolled away from it?
while (this._itemsInRenderBlock > 0) {
const itemIndex = this._itemsAboveRenderBlock + this._itemsInRenderBlock - 1;
const item = props.itemList[itemIndex];
if (!this._isItemHeightKnown(item)) {
break;
}
const itemHeight = this._getHeightOfItem(item);
if (this._heightAboveRenderAdjustment + this._heightAboveRenderBlock + this._heightOfRenderBlock
- itemHeight <= bottomCullLine) {
break;
}
this._itemsInRenderBlock--;
this._heightOfRenderBlock -= itemHeight;
this._itemsBelowRenderBlock++;
this._heightBelowRenderBlock += itemHeight;
this._recycleCell(item.key);
if (props.logInfo) {
props.logInfo('Culled Item From Bottom: ' + item.key);
}
}
// Determine what the line is that we're rendering up to. If we haven't yet filled a screen,
// first get the screen full before over-rendering.
const overdrawAmount = this._calcOverdrawAmount();
let renderMargin = this._isInitialFillComplete ? overdrawAmount : 0;
let renderBlockTopLimit = this._lastScrollTop - renderMargin;
let renderBlockBottomLimit = this._lastScrollTop + this._layoutHeight + renderMargin;
if (this._itemsInRenderBlock === 0) {
let yPosition = this._heightAboveRenderAdjustment;
this._itemsAboveRenderBlock = 0;
// Find the first item that's in the render block and add it.
for (let i = 0; i < props.itemList.length; i++) {
const item = props.itemList[i];
const itemHeight = this._getHeightOfItem(item);
yPosition += itemHeight;
if (yPosition > renderBlockTopLimit) {
this._itemsAboveRenderBlock = i;
this._itemsInRenderBlock = 1;
this._allocateCell(item.key, item.template, i, !item.measureHeight, item.height,
yPosition - itemHeight, this._shouldShowItem(item, props));
if (!this._isItemHeightKnown(item)) {
this._pendingMeasurements.add(item.key);
}
break;
}
}
this._itemsBelowRenderBlock = props.itemList.length - this._itemsAboveRenderBlock - this._itemsInRenderBlock;
this._heightAboveRenderBlock = this._calcHeightOfItems(props, 0, this._itemsAboveRenderBlock - 1);
this._heightOfRenderBlock = this._calcHeightOfItems(props, this._itemsAboveRenderBlock,
this._itemsAboveRenderBlock + this._itemsInRenderBlock - 1);
this._heightBelowRenderBlock = this._calcHeightOfItems(props,
this._itemsAboveRenderBlock + this._itemsInRenderBlock, props.itemList.length - 1);
}
// What is the whole height of the scroll region? We need this both for calculating bottom
// offsets as well as for making the view render to the proper height since we're using
// position: absolute for all placements.
const itemBlockHeight = this._heightAboveRenderAdjustment + this._heightAboveRenderBlock +
this._heightOfRenderBlock + this._heightBelowRenderBlock;
const containerHeight = Math.max(itemBlockHeight, this._layoutHeight);
// Render the actual items now!
let yPosition = this._heightAboveRenderBlock + this._heightAboveRenderAdjustment;
let topOfRenderBlockY = yPosition;
// Start by checking heights/visibility of everything in the render block before we add to it.
for (let i = 0; i < this._itemsInRenderBlock; i++) {
const itemIndex = this._itemsAboveRenderBlock + i;
const item = props.itemList[itemIndex];
this._setCellTopAndVisibility(item.key, this._shouldShowItem(item, props),
yPosition, !!this.props.animateChanges);
const height = this._getHeightOfItem(item);
yPosition += height;
}
let bottomOfRenderBlockY = yPosition;
// See if the container height needs adjusting.
if (containerHeight !== this._containerHeight) {
if (props.logInfo) {
props.logInfo('Container Height Change: ' + this._containerHeight + ' to ' + containerHeight);
}
this._containerHeight = containerHeight;
this._containerHeightValue.setValue(containerHeight);
}
// Reuse an item-builder.
const buildItem = (itemIndex: number, above: boolean) => {
const item = props.itemList[itemIndex];
const isHeightKnown = this._isItemHeightKnown(item);
const itemHeight = this._getHeightOfItem(item);
assert(itemHeight > 0, 'list items should always have non-zero height');
this._itemsInRenderBlock++;
this._heightOfRenderBlock += itemHeight;
let yPlacement: number;
if (above) {
this._itemsAboveRenderBlock--;
this._heightAboveRenderBlock -= itemHeight;
topOfRenderBlockY -= itemHeight;
yPlacement = topOfRenderBlockY;
} else {
this._itemsBelowRenderBlock--;
this._heightBelowRenderBlock -= itemHeight;
yPlacement = bottomOfRenderBlockY;
bottomOfRenderBlockY += itemHeight;
}
if (!isHeightKnown) {
this._pendingMeasurements.add(item.key);
}
this._allocateCell(item.key, item.template, itemIndex, !item.measureHeight, item.height,
yPlacement, this._shouldShowItem(item, props));
if (props.logInfo) {
props.logInfo('New Item On ' + (above ? 'Top' : 'Bottom') + ': ' + item.key);
}
};
// Try to add items to the bottom of the current render block.
while (this._pendingMeasurements.size < _maxSimultaneousMeasures) {
// Stop if we go beyond the bottom render limit.
if (this._itemsBelowRenderBlock <= 0 ||
this._heightAboveRenderAdjustment + this._heightAboveRenderBlock +
this._heightOfRenderBlock >= renderBlockBottomLimit) {
break;
}
buildItem(this._itemsAboveRenderBlock + this._itemsInRenderBlock, false);
}
// Try to add an item to the top of the current render block.
while (this._pendingMeasurements.size < _maxSimultaneousMeasures) {
if (this._itemsAboveRenderBlock <= 0 ||
this._heightAboveRenderAdjustment + this._heightAboveRenderBlock <= renderBlockTopLimit) {
break;
}
buildItem(this._itemsAboveRenderBlock - 1, true);
}
// See if we've filled the screen and rendered it, and we're not waiting on any measurements.
if (!this._isInitialFillComplete && !this._isRenderDirty && this._pendingMeasurements.size === 0) {
// Time for overrender. Recalc render lines.
renderMargin = overdrawAmount;
renderBlockTopLimit = this._lastScrollTop - renderMargin;
renderBlockBottomLimit = this._lastScrollTop + this._layoutHeight + renderMargin;
this._popInvisibleIntoView(props);
// Render pass again!
this._componentDidRender();
}
if (props.logInfo) {
props.logInfo('CalcNewRenderedItemState: O:' + this._heightAboveRenderAdjustment +
' + A:' + this._heightAboveRenderBlock + ' + R:' + this._heightOfRenderBlock + ' + B:' +
this._heightBelowRenderBlock + ' = ' + itemBlockHeight + ', FilledViewable: ' + this._isInitialFillComplete);
}
}
private _reconcileCorrections(props: VirtualListViewProps) {
// If there are pending animations, don't adjust because it will disrupt
// the animations. When all animations are complete, we will get called back.
if (this._pendingAnimations.size > 0) {
return;
}
// Calculate the max amount of error we want to accumulate before we adjust
// the content height size. We don't want to do this too often because it's
// expensive, but we also don't want to let the error get too great because
// the scroll bar thumb will not accurately reflect the scroll position.
let maxFakeSpaceOffset = 0; // Math.max(this._layoutHeight / 2, 256);
// If the user has scrolled all the way to the boundary of the rendered area,
// we can't afford any error.
if (this._lastScrollTop === 0 || this._lastScrollTop < this._heightAboveRenderAdjustment) {
maxFakeSpaceOffset = 0;
}
// Did the error amount exceed our limit?
if (Math.abs(this._heightAboveRenderAdjustment) > maxFakeSpaceOffset) {
if (props.logInfo) {
props.logInfo('Removing _heightAboveRenderAdjustment');
}
// We need to adjust the content height, the positions of the rendered items
// and the scroll position as atomically as possible.
const newContainerHeight = this._containerHeight - this._heightAboveRenderAdjustment;
if (props.logInfo) {
props.logInfo('Container Height Change: ' + this._containerHeight + ' to ' + newContainerHeight);
}
this._containerHeight = newContainerHeight;
this._containerHeightValue.setValue(newContainerHeight);
for (let i = this._itemsAboveRenderBlock; i < this._itemsAboveRenderBlock + this._itemsInRenderBlock; i++) {
const item = props.itemList[i];
const cell = this._activeCells.get(item.key)!;
this._setCellTopAndVisibility(item.key, cell.isVisible,
cell.top - this._heightAboveRenderAdjustment, false);
}
// Clear the adjustment.
this._heightAboveRenderAdjustment = 0;
}
}
private _popInvisibleIntoView(props: VirtualListViewProps) {
if (props.logInfo) {
props.logInfo('Popping invisible items into view');
}
this._isInitialFillComplete = true;
// Update styles now to snap everything into view.
for (let i = 0; i < this._itemsInRenderBlock; i++) {
const itemIndex = this._itemsAboveRenderBlock + i;
const item = props.itemList[itemIndex];
const cellInfo = this._activeCells.get(item.key)!;
this._setCellTopAndVisibility(item.key, this._shouldShowItem(item, props),
cellInfo.top, false);
}
}
private _resizeAllItems(props: VirtualListViewProps) {
if (this._layoutWidth > 0 && this._layoutWidth !== this._contentWidth) {
this._contentWidth = this._layoutWidth;
this.forceUpdate();
}
}
private _renderIfDirty(props: VirtualListViewProps): void {
if (this._isRenderDirty) {
if (this._isMounted) {
this.forceUpdate();
}
}
}
// Cell Management Methods
private _allocateCell(itemKey: string, itemTemplate: string | undefined, itemIndex: number, isHeightConstant: boolean,
height: number, top: number, isVisible: boolean): VirtualCellInfo {
let newCell = this._activeCells.get(itemKey);
if (!newCell) {
// If there's a specified template, see if we can find an existing
// recycled cell that we can reuse with the same template.
if (itemTemplate && isHeightConstant) {
// See if we can find an exact match both in terms of template and previous key.
// This has the greatest chance of rendering the same as previously.
let bestOptionIndex = _.findIndex(this._recycledCells, cell => cell.itemTemplate === itemTemplate &&
cell.cachedItemKey === itemKey && cell.height === height);
// We couldn't find an exact match. Try to find one with the same template.
if (bestOptionIndex < 0) {
bestOptionIndex = _.findIndex(this._recycledCells, cell => cell.itemTemplate === itemTemplate);
}
if (bestOptionIndex >= 0) {
newCell = this._recycledCells[bestOptionIndex];
this._recycledCells.splice(bestOptionIndex, 1);
}
}
}
if (newCell) {
// We found an existing cell. Repurpose it.
newCell.isVisible = isVisible;
newCell.top = top;
newCell.shouldUpdate = true;
assert(newCell.isHeightConstant === isHeightConstant, 'isHeightConstant assumed to not change');
assert(newCell.itemTemplate === itemTemplate, 'itemTemplate assumed to not change');
const mountedCell = newCell.cellRef.current;
if (mountedCell) {
mountedCell.setVisibility(isVisible);
mountedCell.setTop(top);
mountedCell.setItemKey(itemKey);
}
} else {
// We didn't find a recycled cell that we could use. Allocate a new one.
newCell = {
cellRef: createRef>(),
virtualKey: _virtualKeyPrefix + VirtualListView._nextCellKey,
itemTemplate: itemTemplate,
isHeightConstant: isHeightConstant,
height: height,
cachedItemKey: itemKey,
top: top,
isVisible: isVisible,
shouldUpdate: true,
};
VirtualListView._nextCellKey += 1;
}
this._isRenderDirty = true;
this._activeCells.set(itemKey, newCell);
return newCell;
}
private _recycleCell(itemKey: string) {
const virtualCellInfo = this._activeCells.get(itemKey);
if (virtualCellInfo) {
if (this._maxRecycledCells > 0) {
this._setCellTopAndVisibility(itemKey, false, virtualCellInfo.top, false);
// Is there a "template" hint associated with this cell? If so,
// we may be able to reuse it later.
if (virtualCellInfo.itemTemplate && virtualCellInfo.isHeightConstant) {
this._recycledCells.push(virtualCellInfo);
if (this._recycledCells.length > this._maxRecycledCells) {
// Delete the oldest recycled cell.
this._recycledCells.splice(0, 1);
this._isRenderDirty = true;
}
} else {
// Re-render to force the cell to be unmounted.
this._isRenderDirty = true;
}
}
this._activeCells.delete(itemKey);
}
}
private _setCellTopAndVisibility(itemKey: string, isVisibile: boolean, top: number,
animateIfPreviouslyVisible: boolean) {
const cellInfo = this._activeCells.get(itemKey);
if (!cellInfo) {
assert(false, 'Missing cell');
return;
}
// Disable animation for Android when screen reader is on.
// This is needed to make sure screen reader order is correct.
const animate = animateIfPreviouslyVisible && cellInfo.isVisible && !this._isAndroidScreenReaderEnabled();
cellInfo.isVisible = isVisibile;
cellInfo.top = top;
// Set the "live" values as well.
const cell = cellInfo.cellRef.current;
if (cell) {
cell.setVisibility(isVisibile);
cell.setTop(top, animate);
}
}
private _isCellVisible(itemKey: string): boolean {
const cellInfo = this._activeCells.get(itemKey);
return (!!cellInfo && cellInfo.isVisible);
}
scrollToTop = (animated = true, top = 0) => {
const scrollView = this._scrollViewRef.current;
if (scrollView) {
scrollView.setScrollTop(top, animated);
}
};
render() {
const itemsRendered: JSX.Element[] = [];
this._navigatableItemsRendered = [];
if (this.props.logInfo) {
this.props.logInfo('Rendering ' + this._itemsInRenderBlock + ' Items...');
}
// Build a list of all the cells we're going to render. This includes all of the active
// cells plus any recycled (offscreen) cells.
let cellList: {
cellInfo: VirtualCellInfo;
item: ItemInfo | undefined;
itemIndex: number | undefined;
}[] = [];
for (let i = 0; i < this._itemsInRenderBlock; i++) {
const itemIndex = this._itemsAboveRenderBlock + i;
const item = this.props.itemList[itemIndex];
const virtualCellInfo = this._activeCells.get(item.key)!;
assert(virtualCellInfo, 'Active Cell not found for key ' + item.key + ', index=' + i);
cellList.push({
cellInfo: virtualCellInfo,
item: item,
itemIndex: itemIndex,
});
if (item.isNavigable) {
this._navigatableItemsRendered.push({ key: item.key, vc_key: virtualCellInfo.virtualKey });
}
}
for (const virtualCellInfo of this._recycledCells) {
assert(virtualCellInfo, 'Recycled Cells array contains a null/undefined object');
cellList.push({
cellInfo: virtualCellInfo,
item: undefined,
itemIndex: undefined,
});
}
// Sort the list of cells by virtual key so the order doesn't change. Otherwise
// the underlying render engine (the browser or React Native) treat it as a DOM
// change, and perf suffers.
cellList = cellList.sort((a, b) => a.cellInfo.virtualKey < b.cellInfo.virtualKey ? 1 : -1);
let focusIndex: number | undefined;
if (this.state.lastFocusedItemKey === undefined) {
const itemToFocus = _.minBy(cellList, cell => {
if (!cell.item || !cell.item.isNavigable) {
return Number.MAX_VALUE;
}
return cell.itemIndex;
});
if (itemToFocus) {
focusIndex = itemToFocus.itemIndex;
}
}
for (const cell of cellList) {
let tabIndexValue = -1;
let isFocused = false;
let isSelected = false;
if (cell.item) {
if (cell.item.isNavigable) {
if (cell.itemIndex === focusIndex) {
tabIndexValue = 0;
} else {
tabIndexValue = cell.item.key === this.state.lastFocusedItemKey ? 0 : -1;
}
if (cell.item.key === this.state.selectedItemKey) {
isSelected = true;
}
}
if (cell.item.key === this.state.lastFocusedItemKey) {
isFocused = true;
}
}
// We disable transform in Android because it creates problem for screen reader order.
// We update the keys in order to make sure we re-render cells, as once we enable native animation for a view.
// We can't disable it.
itemsRendered.push(
,
);
cell.cellInfo.shouldUpdate = false;
}
if (this.props.logInfo) {
// [NOTE: For debugging] This shows the order in which virtual cells are laid out.
const domOrder = _.map(cellList, c => {
const itemKey = c.item ? c.item.key : null;
const itemIndex = c.item ? c.itemIndex : null;
return 'vKey: ' + c.cellInfo.virtualKey + ' iKey: ' + itemKey + ' iIdx: ' + itemIndex;
}).join('\n');
this.props.logInfo(domOrder);
this.props.logInfo('Item Render Complete');
}
const scrollViewStyle = [_styles.scrollContainer];
let staticContainerStyle: (RX.Types.ViewStyleRuleSet | RX.Types.AnimatedViewStyleRuleSet)[] = [_styles.staticContainer];
if (this.props.style) {
if (Array.isArray(this.props.style)) {
staticContainerStyle = staticContainerStyle.concat(this.props.style as RX.Types.ViewStyleRuleSet[]);
} else {
staticContainerStyle.push(this.props.style as RX.Types.ViewStyleRuleSet);
}
}
staticContainerStyle.push(this._containerAnimatedStyle);
return (
~30 events per second max
style={ scrollViewStyle }
bounces={ !this.props.disableBouncing }
onKeyPress={ this._onKeyDown }
scrollEnabled={ !this.props.disableScrolling }
scrollIndicatorInsets={ this.props.scrollIndicatorInsets }
>
{ itemsRendered }
);
}
private _onItemFocused = (itemInfo?: ItemInfo) => {
if (itemInfo) {
this.setState({
lastFocusedItemKey: itemInfo.key,
isFocused: true,
});
} else {
this.setState({ isFocused: false });
}
if (this.props.onItemFocused) {
this.props.onItemFocused(itemInfo);
}
};
// Sets selection & focus to specified key
selectItemKey(key: string, scrollToItem = true) {
// Set focus and selection
this.setState({
lastFocusedItemKey: key,
selectedItemKey: key,
});
if (scrollToItem) {
this._scrollToItemKey(key);
}
}
private _onItemSelected = (itemInfo?: ItemInfo) => {
if (itemInfo) {
this.selectItemKey(itemInfo.key, false);
if (this.props.onItemSelected) {
this.props.onItemSelected(itemInfo);
}
}
};
private _onKeyDown = (e: RX.Types.KeyboardEvent) => {
if (!this._scrollViewRef.current ||
(e.keyCode !== _keyCodeUpArrow && e.keyCode !== _keyCodeDownArrow)) {
return;
}
// Is it an "up arrow" key?
if (e.keyCode === _keyCodeUpArrow) {
this._focusSubsequentItem(FocusDirection.Up, true);
e.preventDefault();
// Is it a "down arrow" key?
} else if (e.keyCode === _keyCodeDownArrow) {
this._focusSubsequentItem(FocusDirection.Down, true);
e.preventDefault();
}
};
private _scrollToItemKey(key: string): void {
let indexToSelect: number | undefined;
_.each(this.props.itemList, (item, idx) => {
if (item.key === key) {
indexToSelect = idx;
return true;
}
});
if (indexToSelect !== undefined) {
this._scrollToItemIndex(indexToSelect);
}
}
private _scrollToItemIndex(index: number): void {
this.scrollToTop(false, this._calcHeightOfItems(this.props, 0, index - 1) - (this.props.keyboardFocusScrollOffset || 0));
}
// Returns true if successfully found/focused, false if not found/focused
private _focusSubsequentItem(direction: FocusDirection, viaKeyboard: boolean, retry = true): boolean {
let index: number | undefined = _.findIndex(this._navigatableItemsRendered, item => item.key === this.state.lastFocusedItemKey);
if (index !== -1 && index + direction > -1 && index + direction < this._navigatableItemsRendered.length) {
const newFocusKey = this._navigatableItemsRendered[index + direction].key;
const cellForFocus = this._activeCells.get(newFocusKey);
if (cellForFocus && cellForFocus.cellRef.current) {
const newElementForFocus = cellForFocus.cellRef.current;
newElementForFocus.focus();
if (viaKeyboard && newElementForFocus.props.itemKey) {
this._scrollToItemKey(newElementForFocus.props.itemKey);
}
}
return true;
}
if (index === -1 && retry && this.state.lastFocusedItemKey !== undefined) {
index = this._itemMap.get(this.state.lastFocusedItemKey);
if (index === undefined) {
assert(false, 'Something went wrong in finding last focused item');
return false;
}
const height = index === 0 ? 0 : this._calcHeightOfItems(this.props, 0, index - 1);
this.scrollToTop(false, height);
this._pendingFocusDirection = direction;
return true;
}
return false;
}
private _screenReaderStateChanged = (isEnabled: boolean) => {
if (isEnabled) {
this._setupForAccessibility();
if (_isNativeAndroid) {
// We need to re-render virtual cells.
this._isRenderDirty = true;
}
this._renderIfDirty(this.props);
} else {
this._tearDownForAccessibility();
}
};
componentDidMount() {
RX.Accessibility.screenReaderChangedEvent.subscribe(this._screenReaderStateChanged);
if (RX.Accessibility.isScreenReaderEnabled()) {
this._setupForAccessibility();
}
this._isMounted = true;
this._componentDidRender();
// If an initial selection key was provided, ensure that we scroll to the item
if (this.props.initialSelectedKey) {
this._scrollToItemKey(this.props.initialSelectedKey);
}
}
componentWillUnmount() {
this._isMounted = false;
RX.Accessibility.screenReaderChangedEvent.unsubscribe(this._screenReaderStateChanged);
}
componentDidUpdate(prevProps: VirtualListViewProps) {
this._componentDidRender();
}
protected _componentDidRender() {
if (this.props.logInfo) {
this.props.logInfo('Component Did Render');
}
this._isRenderDirty = false;
// If we don't defer this, we can end up overflowing the stack
// because one render immediately causes another render to be started.
_.defer(() => {
if (this._isMounted) {
this._calcNewRenderedItemState(this.props);
this._renderIfDirty(this.props);
this._reconcileCorrections(this.props);
this._setFocusIfNeeded();
}
});
}
// If there was a pending focus setting before we re-rendered, set the same.
private _setFocusIfNeeded() {
if (this._pendingFocusDirection) {
this._focusSubsequentItem(this._pendingFocusDirection, false, false /* do not retry if this fails */);
this._pendingFocusDirection = undefined;
}
}
// Local helper functions for item information
private _shouldShowItem(item: VirtualListViewItemInfo, props: VirtualListViewProps): boolean {
const isMeasuring = !this._isItemHeightKnown(item);
const shouldHide = isMeasuring || !this._isInitialFillComplete;
return !shouldHide;
}
private _calcHeightOfItems(props: VirtualListViewProps, startIndex: number, endIndex: number) {
let count = 0;
for (let i = startIndex; i <= endIndex; i++) {
count += this._getHeightOfItem(props.itemList[i]);
}
return count;
}
private _isItemHeightKnown(item: VirtualListViewItemInfo) {
return !item.measureHeight || this._heightCache.has(item.key);
}
private _getHeightOfItem(item: VirtualListViewItemInfo | undefined) {
if (!item) {
return 0;
}
// See if the item height was passed as "known"
if (!item.measureHeight) {
return item.height;
}
// See if we have it cached
const cachedHeight = this._heightCache.get(item.key);
if (cachedHeight !== undefined) {
return cachedHeight;
}
// Nope -- use guess given to us
return item.height;
}
}
================================================
FILE: extensions/virtuallistview/src/assert.ts
================================================
/**
* assert
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
*/
const assert = (cond: any, message?: string | undefined): void => {
if (!cond) {
throw new Error(message || 'Assertion Failed');
}
};
export default assert;
================================================
FILE: extensions/virtuallistview/tsconfig.json
================================================
{
"compilerOptions": {
"declaration": true,
"noResolve": false,
"importHelpers": true,
"downlevelIteration": false,
"jsx": "react",
"reactNamespace": "RX",
"module": "commonjs",
"target": "es5",
"outDir": "dist/",
"skipLibCheck": true,
"strict": true,
"lib": [
"es5",
"dom",
"es2015.collection",
"es2015.iterable"
],
"types" : ["lodash", "react", "react-dom"]
},
"include": [
"src/**/*"
],
"exclude": [
"dist",
"node_modules"
]
}
================================================
FILE: extensions/webview/.eslintrc.json
================================================
{
"parserOptions": {
"project": "./tsconfig.json"
},
"extends": ["skype", "skype/react"],
"rules": {},
"overrides": [
{
"files": [
"*.tsx"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off"
}
},
{
"files": [
"PluginBaseChecker.ts"
],
"rules": {
"@typescript-eslint/no-unused-vars": "off"
}
}
]
}
================================================
FILE: extensions/webview/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
package-lock.json
# Optional npm cache directory
.npm
# Build artifacts
**/dist
# Miscellaneous user files
*.user
.vscode
.DS_STORE
================================================
FILE: extensions/webview/.npmignore
================================================
/node_modules
/src/.vs
/src/bin
/src/obj
*.user
================================================
FILE: extensions/webview/README.md
================================================
# reactxp-webview
This module provides a cross-platform control that allows the display of an independent web page within the [ReactXP](https://microsoft.github.io/reactxp/) library. This used to be a part of ReactXP core, but was extracted to be a standalone module inline with React Native `Lean Core` initiative. This exists as a standalone module to prevent users of ReactXP from having to link native modules when getting started.
## Getting Started
This module relies on [react-native-webview](https://www.npmjs.com/package/react-native-webview) and will need to be added into the react native project. Follow the instructions in their [Getting Started](https://github.com/react-native-webview/react-native-webview/blob/HEAD/docs/Getting-Started.md) guide to properly add the package to the project.
## Documentation
For detailed documentation, look [here](https://microsoft.github.io/reactxp/docs/extensions/webview.html).
### Prerequisites
* [ReactXP](https://github.com/microsoft/reactxp/)
## Contributing
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
You must sign a Contribution License Agreement (CLA) before your PR will be merged. This a one-time requirement for Microsoft projects in GitHub. You can read more about [Contribution License Agreements (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) on Wikipedia. You can sign the Microsoft Contribution License Agreement by visiting https://cla.microsoft.com/. Use your GitHub account to login.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
================================================
FILE: extensions/webview/index.android.js
================================================
'use strict';
module.exports = require('./dist/android/PluginBase.js');
================================================
FILE: extensions/webview/index.ios.js
================================================
'use strict';
module.exports = require('./dist/ios/PluginBase.js');
================================================
FILE: extensions/webview/index.js
================================================
'use strict';
// Export web by default. Other platforms have custom index.[platform].js files
module.exports = require('./dist/web/PluginBase.js');
================================================
FILE: extensions/webview/index.macos.js
================================================
'use strict';
module.exports = require('./dist/macos/PluginBase.js');
================================================
FILE: extensions/webview/index.windows.js
================================================
'use strict';
module.exports = require('./dist/windows/PluginBase.js');
================================================
FILE: extensions/webview/package.json
================================================
{
"name": "reactxp-webview",
"version": "2.0.0",
"description": "Plugin for ReactXP that provides a control that allows the display of an independent web page",
"author": "ReactXP Team ",
"license": "MIT",
"scripts": {
"build": "tsc",
"lint": "eslint --ext .ts,.tsx src",
"lint:fix": "npm run lint -- --fix"
},
"dependencies": {
"react-native-webview": "^7.5.2"
},
"peerDependencies": {
"react": "^16.3.0",
"reactxp": "^2.0.0",
"react-dom": "^16.3",
"react-native": ">=0.57",
"react-native-windows": "^0.57.1"
},
"devDependencies": {
"@types/react-native": "0.60.30",
"@typescript-eslint/eslint-plugin": "2.15.0",
"eslint": "6.8.0",
"eslint-config-skype": "1.5.0",
"eslint-plugin-import": "2.19.1",
"eslint-plugin-react": "7.17.0",
"react": "^16.3.0",
"reactxp": "^2.0.0",
"typescript": "3.7.4"
},
"types": "dist/web/PluginBase.d.ts"
}
================================================
FILE: extensions/webview/src/android/PluginBase.tsx
================================================
/*
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the Android implementation of the plugin.
*/
import * as Types from '../common/Types';
import WebView from '../native-common/WebView';
export { WebView as default, Types };
================================================
FILE: extensions/webview/src/common/Interfaces.ts
================================================
/*
* Interfaces.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Interface definition for cross-platform ReactXP plugin for a control that allows the
* display of an independent web page. This was extracted from the reactxp core
*/
import * as Types from './Types';
export interface PluginInterface {
Types: typeof Types;
default: typeof Types.WebView;
}
================================================
FILE: extensions/webview/src/common/PluginBaseChecker.ts
================================================
/*
* PluginBaseChecker.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Type check all the pluginbase exports against the desired interface.
*/
import * as AndroidPlugin from '../android/PluginBase';
import * as iOSPlugin from '../ios/PluginBase';
import * as macOSPlugin from '../macos/PluginBase';
import * as WebPlugin from '../web/PluginBase';
import * as WindowsPlugin from '../windows/PluginBase';
import * as Interfaces from './Interfaces';
const _typeCheckerAndroid: Interfaces.PluginInterface = AndroidPlugin;
const _typeCheckeriOS: Interfaces.PluginInterface = iOSPlugin;
const _typeCheckermacOS: Interfaces.PluginInterface = macOSPlugin;
const _typeCheckerWeb: Interfaces.PluginInterface = WebPlugin;
const _typeCheckerWindows: Interfaces.PluginInterface = WindowsPlugin;
================================================
FILE: extensions/webview/src/common/Types.ts
================================================
/*
* Types.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Type definitions to support the plugin.
*/
import { Component as ReactComponent } from 'react';
import { Types as RXTypes } from 'reactxp';
export interface WebViewNavigationState {
canGoBack: boolean;
canGoForward: boolean;
loading: boolean;
url: string;
title: string;
readonly navigationType: 'click' | 'formsubmit' | 'backforward' | 'reload' | 'formresubmit' | 'other';
}
export interface WebViewErrorState extends WebViewNavigationState {
description: string;
domain: string;
code: string;
}
export enum WebViewSandboxMode {
None = 0,
AllowForms = 1 << 0,
AllowModals = 1 << 1,
AllowOrientationLock = 1 << 2,
AllowPointerLock = 1 << 3,
AllowPopups = 1 << 4,
AllowPopupsToEscapeSandbox = 1 << 5,
AllowPresentation = 1 << 6,
AllowSameOrigin = 1 << 7,
AllowScripts = 1 << 8,
AllowTopNavigation = 1 << 9,
AllowMixedContentAlways = 1 << 10,
AllowMixedContentCompatibilityMode = 1 << 11
}
export interface WebViewSource {
html: string;
baseUrl?: string;
}
export interface WebViewShouldStartLoadEvent {
url: string;
}
export interface WebViewNavigationEvent extends RXTypes.SyntheticEvent {
nativeEvent: WebViewNavigationState;
}
export interface WebViewErrorEvent extends RXTypes.SyntheticEvent {
nativeEvent: WebViewErrorState;
}
export interface WebViewMessageEvent extends RXTypes.SyntheticEvent {
data: string;
origin: string;
}
export interface WebViewProps extends RXTypes.CommonStyledProps {
url?: string;
source?: WebViewSource;
headers?: Headers;
onLoad?: (e: RXTypes.SyntheticEvent) => void;
onNavigationStateChange?: (navigationState: WebViewNavigationState) => void;
scalesPageToFit?: boolean;
injectedJavaScript?: string;
javaScriptEnabled?: boolean;
mediaPlaybackRequiresUserAction?: boolean;
allowsInlineMediaPlayback?: boolean;
// Native only
startInLoadingState?: boolean;
domStorageEnabled?: boolean;
onShouldStartLoadWithRequest?: (shouldStartLoadEvent: WebViewShouldStartLoadEvent) => boolean;
onLoadStart?: (e: RXTypes.SyntheticEvent) => void;
onError?: (e: RXTypes.SyntheticEvent) => void;
onMessage?: (e: WebViewMessageEvent) => void;
// Web only; overrides javaScriptEnabled if used
sandbox?: WebViewSandboxMode;
}
export abstract class WebView extends ReactComponent {
abstract postMessage(message: string, targetOrigin?: string): void;
abstract reload(): void;
abstract goBack(): void;
abstract goForward(): void;
}
================================================
FILE: extensions/webview/src/ios/PluginBase.tsx
================================================
/*
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the iOS implementation of the plugin.
*/
import * as Types from '../common/Types';
import WebView from '../native-common/WebView';
export { WebView as default, Types };
================================================
FILE: extensions/webview/src/macos/PluginBase.tsx
================================================
/*
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the Mac implementation of the plugin.
*/
import * as Types from '../common/Types';
import WebView from '../native-common/WebView';
export { WebView as default, Types };
================================================
FILE: extensions/webview/src/native-common/WebView.tsx
================================================
/**
* WebView.tsx
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* A control that allows the display of an independent web page.
*/
import * as React from 'react';
import * as RN from 'react-native';
import { WebView as RNWebView } from 'react-native-webview';
import {
WebViewMessageEvent as RNWebViewMessageEvent,
WebViewSourceHtml as RNWebViewSourceHtml,
WebViewSourceUri as RNWebViewSourceUri,
} from 'react-native-webview/lib/WebViewTypes';
import * as RX from 'reactxp';
import * as Types from '../common/Types';
const _styles = {
webViewDefault: RX.Styles.createViewStyle({
flex: 1,
alignSelf: 'stretch',
}),
};
type MixedContentMode = 'never' | 'always' | 'compatibility' | undefined;
export class WebView extends React.Component implements Types.WebView {
private _mountedComponent: RNWebView | undefined;
render() {
const styles = [_styles.webViewDefault, this.props.style] as RN.StyleProp;
const source = this._buildSource();
const injectedJavascript = this._buildInjectedJavascript();
return (
);
}
private _sandboxToMixedContentMode(sandbox?: Types.WebViewSandboxMode): MixedContentMode {
if (sandbox) {
if (sandbox & Types.WebViewSandboxMode.AllowMixedContentAlways) {
return 'always';
}
if (sandbox & Types.WebViewSandboxMode.AllowMixedContentCompatibilityMode) {
return 'compatibility';
}
}
return 'never';
}
protected _onMount = (component: RNWebView | null) => {
this._mountedComponent = component || undefined;
};
protected _onMessage = (e: RNWebViewMessageEvent) => {
if (this.props.onMessage) {
const event: Types.WebViewMessageEvent = {
defaultPrevented: e.defaultPrevented,
nativeEvent: e.nativeEvent,
cancelable: e.cancelable,
timeStamp: e.timeStamp,
bubbles: e.bubbles,
origin: '*',
data: e.nativeEvent.data,
stopPropagation: () => e.stopPropagation(),
preventDefault: () => e.preventDefault(),
};
this.props.onMessage(event);
}
};
private _buildSource(): RNWebViewSourceUri | RNWebViewSourceHtml | undefined {
const { headers, source, url } = this.props;
if (url) {
return { headers, uri: url };
}
if (source) {
return source;
}
return undefined;
}
private _buildInjectedJavascript(): string {
// Keep compatibility with old code that uses window.postMessage. For more information,
// see https://github.com/react-native-community/react-native-webview/releases/tag/v5.0.0
let injectedJavascript = `
// WebView -> Native
(function() {
if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
window.postMessage = function(data) {
window.ReactNativeWebView.postMessage(data);
};
}
})();
// Native -> WebView
(function() {
window.postMessageFromReactXP = function(message) {
var event;
try {
event = new MessageEvent('message', { data: message });
} catch (e) {
event = document.createEvent('MessageEvent');
event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);
}
document.dispatchEvent(event);
};
})();`;
if (this.props.injectedJavaScript !== undefined) {
injectedJavascript += this.props.injectedJavaScript;
}
// End the injectedJavascript with 'true;' or else you'll sometimes get silent failures
injectedJavascript += ';true;';
return injectedJavascript;
}
postMessage(message: string, targetOrigin = '*') {
if (this._mountedComponent) {
this._mountedComponent.injectJavaScript(`window.postMessageFromReactXP('${message}');`);
}
}
reload() {
if (this._mountedComponent) {
this._mountedComponent.reload();
}
}
goBack() {
if (this._mountedComponent) {
this._mountedComponent.goBack();
}
}
goForward() {
if (this._mountedComponent) {
this._mountedComponent.goForward();
}
}
}
export default WebView;
================================================
FILE: extensions/webview/src/web/PluginBase.ts
================================================
/*
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the Web implementation of the plugin.
*/
import * as Types from '../common/Types';
import WebView from './WebView';
export { WebView as default, Types };
================================================
FILE: extensions/webview/src/web/WebView.tsx
================================================
/**
* WebView.tsx
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* A control that allows the display of an independent web page.
*/
import * as React from 'react';
import * as RX from 'reactxp';
import * as Types from '../common/Types';
const _styles = {
webViewDefault: RX.Styles.createViewStyle({
flex: 1,
alignSelf: 'stretch',
borderStyle: 'none',
}),
webViewContainer: RX.Styles.createViewStyle({
flexDirection: 'column',
flex: 1,
alignSelf: 'stretch',
}),
};
export interface WebViewState {
postComplete?: boolean;
webFormIdentifier?: string;
webFrameIdentifier?: string;
}
interface WebViewMessageEventInternal extends Types.WebViewMessageEvent {
__propagationStopped: boolean;
}
export class WebView extends React.Component implements Types.WebView {
private static _webFrameNumber = 1;
private static _onMessageReceived: RX.Types.SubscribableEvent<(e: WebViewMessageEventInternal) => void>;
private static _messageListenerInstalled = false;
private _mountedComponent: HTMLIFrameElement | null = null;
private _onMessageReceivedToken: RX.Types.SubscriptionToken | undefined;
constructor(props: Types.WebViewProps) {
super(props);
this.state = {
postComplete: false,
webFormIdentifier: `form${WebView._webFrameNumber}`,
webFrameIdentifier: `frame${WebView._webFrameNumber}`,
};
WebView._webFrameNumber++;
}
componentDidMount() {
this._postRender();
const customContents = this._getCustomHtml(this.props);
if (customContents) {
this._setContents(customContents);
}
}
componentDidUpdate(prevProps: Types.WebViewProps, prevState: WebViewState) {
this._postRender();
const oldCustomContents = this._getCustomHtml(prevProps);
const newCustomContents = this._getCustomHtml(this.props);
if (newCustomContents) {
if (oldCustomContents !== newCustomContents) {
this._setContents(newCustomContents);
}
}
}
componentWillUnmount() {
if (this._onMessageReceivedToken) {
this._onMessageReceivedToken.unsubscribe();
this._onMessageReceivedToken = undefined;
}
}
private _getCustomHtml(props: Types.WebViewProps): string | undefined {
if (props.url || !props.source) {
return undefined;
}
return props.source.html;
}
private _setContents(html: string) {
const iframeDOM = this._mountedComponent;
if (iframeDOM && iframeDOM.contentWindow) {
try {
// Some older browsers don't support this, so
// be prepared to catch an exception.
(iframeDOM as any).srcdoc = html;
} catch {
// Swallow exceptions
}
}
}
private _installMessageListener() {
// Don't install global message listener twice.
if (!WebView._messageListenerInstalled) {
// Set up the global event.
WebView._onMessageReceived = new RX.Types.SubscribableEvent<(e: WebViewMessageEventInternal) => void>(true);
window.addEventListener('message', (e: MessageEvent) => {
const event: WebViewMessageEventInternal = {
data: e.data,
origin: e.origin,
nativeEvent: e,
bubbles: e.bubbles,
cancelable: e.cancelable,
defaultPrevented: e.defaultPrevented,
__propagationStopped: false,
stopPropagation: () => {
e.stopPropagation();
event.__propagationStopped = true;
},
preventDefault: () => {
e.preventDefault();
},
timeStamp: e.timeStamp,
};
WebView._onMessageReceived.fire(event);
});
WebView._messageListenerInstalled = true;
}
// Subscribe to the global event if we haven't already done so.
if (!this._onMessageReceivedToken) {
this._onMessageReceivedToken = WebView._onMessageReceived.subscribe(e => {
if (this.props.onMessage) {
this.props.onMessage(e);
// Stop the event from propagating further.
return e.__propagationStopped;
}
return false;
});
}
}
private _postRender() {
// If the caller wants to receive posted messages
// from the web view, we need to install a global
// message handler.
if (this.props.onMessage) {
this._installMessageListener();
}
if (!this.state.postComplete) {
this.setState({
postComplete: true,
});
}
}
render() {
const styles = RX.Styles.combine([_styles.webViewDefault, this.props.style]) as React.CSSProperties;
const sandbox = this.props.sandbox !== undefined
? this.props.sandbox
: (this.props.javaScriptEnabled ? Types.WebViewSandboxMode.AllowScripts : Types.WebViewSandboxMode.None);
// width 100% is needed for Edge - it doesn't grow iframe. Resize needs to be done with wrapper
return (
);
}
protected _onMount = (component: HTMLIFrameElement | null) => {
this._mountedComponent = component;
};
private _onLoad = (e: RX.Types.SyntheticEvent) => {
if (this.props.onLoad) {
this.props.onLoad(e);
}
};
private _sandboxToStringValue = (sandbox: Types.WebViewSandboxMode) => {
const values: string[] = [];
if (sandbox & Types.WebViewSandboxMode.AllowForms) {
values.push('allow-forms');
}
if (sandbox & Types.WebViewSandboxMode.AllowModals) {
values.push('allow-modals');
}
if (sandbox & Types.WebViewSandboxMode.AllowOrientationLock) {
values.push('allow-orientation-lock');
}
if (sandbox & Types.WebViewSandboxMode.AllowPointerLock) {
values.push('allow-pointer-lock');
}
if (sandbox & Types.WebViewSandboxMode.AllowPopups) {
values.push('allow-popups');
}
if (sandbox & Types.WebViewSandboxMode.AllowPopupsToEscapeSandbox) {
values.push('allow-popups-to-escape-sandbox');
}
if (sandbox & Types.WebViewSandboxMode.AllowPresentation) {
values.push('allow-presentation');
}
if (sandbox & Types.WebViewSandboxMode.AllowSameOrigin) {
values.push('allow-same-origin');
}
if (sandbox & Types.WebViewSandboxMode.AllowScripts) {
values.push('allow-scripts');
}
if (sandbox & Types.WebViewSandboxMode.AllowTopNavigation) {
values.push('allow-top-navigation');
}
return values.join(' ');
};
postMessage(message: string, targetOrigin = '*') {
const iframeDOM = this._mountedComponent;
if (iframeDOM && iframeDOM.contentWindow) {
iframeDOM.contentWindow.postMessage(message, targetOrigin);
}
}
reload() {
const iframeDOM = this._mountedComponent;
if (iframeDOM && iframeDOM.contentWindow) {
iframeDOM.contentWindow.location.reload(true);
}
}
goBack() {
const iframeDOM = this._mountedComponent;
if (iframeDOM && iframeDOM.contentWindow) {
iframeDOM.contentWindow.history.back();
}
}
goForward() {
const iframeDOM = this._mountedComponent;
if (iframeDOM && iframeDOM.contentWindow) {
iframeDOM.contentWindow.history.forward();
}
}
}
export default WebView;
================================================
FILE: extensions/webview/src/windows/PluginBase.ts
================================================
/*
* PluginBase.ts
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT license.
*
* Base export for the Windows implementation of the plugin.
*/
import * as Types from '../common/Types';
import WebView from '../native-common/WebView';
export { WebView as default, Types };
================================================
FILE: extensions/webview/tsconfig.json
================================================
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"strictPropertyInitialization": true,
"experimentalDecorators": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"noImplicitThis": true,
"noImplicitAny": true,
"skipLibCheck": true,
"declaration": true,
"noResolve": false,
"target": "es5",
"module": "commonjs",
"outDir": "dist",
"jsx": "react"
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}
================================================
FILE: index.android.js
================================================
'use strict';
module.exports = require('./dist/android/ReactXP.js');
================================================
FILE: index.ios.js
================================================
'use strict';
module.exports = require('./dist/ios/ReactXP.js');
================================================
FILE: index.js
================================================
'use strict';
// Export web by default. Other platforms have custom index.[platform].js files
module.exports = require('./dist/web/ReactXP.js');
================================================
FILE: index.macos.js
================================================
'use strict';
module.exports = require('./dist/macos/ReactXP.js');
================================================
FILE: index.windows.js
================================================
'use strict';
module.exports = require('./dist/windows/ReactXP.js');
================================================
FILE: package.json
================================================
{
"name": "reactxp",
"version": "2.0.0",
"description": "Cross-platform abstraction layer for writing React-based applications a single time that work identically across web, React Native, and Electron distribution",
"author": "ReactXP Team ",
"license": "MIT",
"scripts": {
"build": "tsc",
"watch": "tsc --watch",
"lint": "eslint --ext .ts,.tsx src",
"lint:fix": "npm run lint -- --fix"
},
"dependencies": {
"lodash": "^4.17.15",
"prop-types": "^15.7.2",
"rebound": "^0.1.0",
"subscribableevent": "^1.0.1"
},
"peerDependencies": {
"react": "^16.3.0",
"react-dom": "^16.3.0",
"react-native": ">=0.57",
"react-native-windows": "^0.57.1"
},
"devDependencies": {
"@types/lodash": "4.14.149",
"@types/react": "16.9.15",
"@types/react-dom": "16.9.4",
"@types/react-native": "0.60.23",
"@typescript-eslint/eslint-plugin": "2.15.0",
"eslint": "6.8.0",
"eslint-config-skype": "1.5.0",
"eslint-plugin-import": "2.19.1",
"eslint-plugin-react": "7.17.0",
"react": "^16.3.0",
"tslint": "5.20.1",
"tsutils": "3.17.1",
"typescript": "3.7.4"
},
"homepage": "https://microsoft.github.io/reactxp/",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/reactxp/"
},
"bugs": {
"url": "https://github.com/microsoft/reactxp/issues"
},
"types": "dist/ReactXP.d.ts"
}
================================================
FILE: samples/ImageList/.eslintrc
================================================
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"warnOnUnsupportedTypeScriptVersion": false,
"ecmaFeatures": { "jsx": true },
"ecmaVersion": 2018,
"sourceType": "module",
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint", "reactxp", "react", "jest"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:reactxp/recommended",
"plugin:react/recommended",
"plugin:jest/recommended"
],
"env": {
"browser": true,
"node": true,
"es6": true
},
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"indent": "off",
"@typescript-eslint/indent": ["error", 4],
"react/jsx-curly-brace-presence": [2, { "props": "always" }],
"max-len": ["error", 140]
},
"globals": {
"__DEV__": true
},
"settings": {
"react": { "version": "latest" }
}
}
================================================
FILE: samples/ImageList/.gitignore
================================================
# OSX
#
.DS_Store
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
# BUCK
#
buck-out/
\.buckd/
*.keystore
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/
*/fastlane/report.xml
*/fastlane/Preview.html
*/fastlane/screenshots
# Bundle artifact
#
*.jsbundle
# Web dist
#
dist-*
================================================
FILE: samples/ImageList/README.md
================================================
# RXPImageList
This sample demonstrates a ReactXP app that fetches information from a REST service (Giphy) and displays a virtualized list of images. The app works on React Native (iOS, Android, Windows) and web.
The commands in the instructions below assume you are in the root of this repo.
### Building
- From the RXPImageList directory, run `npm install`. This fetches the dependencies.
### To run your app on Web:
```shell
npm run start:web
```
### To build Web production version of your app:
```shell
npm run build:web
```
### To run your app on iOS:
```shell
npm run start:ios
```
##### - or -
open ios/RXPImageList.xcodeproj project in Xcode
press the Run button
### To run your app on Android:
Have an Android emulator running (quickest way to get started), or a device connected
```shell
npm run start:android
```
##### - or -
open android/ project in Android Studio
press the Run button
### To run your app on Windows:
```shell
npm run start:windows
```
##### - or -
open windows/RXPImageList.sln project in Visual Studio
press the Run button
================================================
FILE: samples/ImageList/android/app/BUCK
================================================
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
lib_deps = []
create_aar_targets(glob(["libs/*.aar"]))
create_jar_targets(glob(["libs/*.jar"]))
android_library(
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = "build_config",
package = "com.rxpimagelist",
)
android_resource(
name = "res",
package = "com.rxpimagelist",
res = "src/main/res",
)
android_binary(
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)
================================================
FILE: samples/ImageList/android/app/build.gradle
================================================
apply plugin: "com.android.application"
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
entryFile: "index.js"
]
apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId "com.rxpimagelist"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ["armeabi-v7a":1, "x86":2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation "com.facebook.react:react-native:+" // From node_modules
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}
================================================
FILE: samples/ImageList/android/app/build_defs.bzl
================================================
"""Helper definitions to glob .aar and .jar targets"""
def create_aar_targets(aarfiles):
for aarfile in aarfiles:
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
lib_deps.append(":" + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
def create_jar_targets(jarfiles):
for jarfile in jarfiles:
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
lib_deps.append(":" + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)
================================================
FILE: samples/ImageList/android/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: samples/ImageList/android/app/src/debug/AndroidManifest.xml
================================================
================================================
FILE: samples/ImageList/android/app/src/debug/res/xml/react_native_config.xml
================================================
localhost
10.0.2.2
10.0.3.2
================================================
FILE: samples/ImageList/android/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: samples/ImageList/android/app/src/main/assets/.gitignore
================================================
================================================
FILE: samples/ImageList/android/app/src/main/java/com/rxpimagelist/MainActivity.java
================================================
package com.rxpimagelist;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "RXApp";
}
}
================================================
FILE: samples/ImageList/android/app/src/main/java/com/rxpimagelist/MainApplication.java
================================================
package com.rxpimagelist;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List getPackages() {
return Arrays.asList(
new MainReactPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
================================================
FILE: samples/ImageList/android/app/src/main/res/values/strings.xml
================================================
RXPImageList
================================================
FILE: samples/ImageList/android/app/src/main/res/values/styles.xml
================================================
================================================
FILE: samples/ImageList/android/build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "28.0.3"
minSdkVersion = 16
compileSdkVersion = 28
targetSdkVersion = 28
supportLibVersion = "28.0.0"
}
repositories {
google()
jcenter()
}
dependencies {
classpath("com.android.tools.build:gradle:3.4.0")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
google()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
}
================================================
FILE: samples/ImageList/android/gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: samples/ImageList/android/gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: samples/ImageList/android/gradlew
================================================
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file 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.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: samples/ImageList/android/gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: samples/ImageList/android/keystores/BUCK
================================================
keystore(
name = "debug",
properties = "debug.keystore.properties",
store = "debug.keystore",
visibility = [
"PUBLIC",
],
)
================================================
FILE: samples/ImageList/android/keystores/debug.keystore.properties
================================================
key.store=debug.keystore
key.alias=androiddebugkey
key.store.password=android
key.alias.password=android
================================================
FILE: samples/ImageList/android/settings.gradle
================================================
rootProject.name = 'RXPImageList'
include ':app'
================================================
FILE: samples/ImageList/babel.config.js
================================================
module.exports = function(api) {
api.cache.forever();
const presets = [
['module:metro-react-native-babel-preset'],
];
const plugins = [
['@babel/proposal-decorators', { legacy: true }],
];
if (process.env.platform === 'web') {
return {
presets: ['@babel/env', ...presets],
plugins,
}
}
return { presets, plugins };
};
================================================
FILE: samples/ImageList/index.js
================================================
import './src/index';
================================================
FILE: samples/ImageList/ios/RXPImageList/AppDelegate.h
================================================
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import
#import
@interface AppDelegate : UIResponder
@property (nonatomic, strong) UIWindow *window;
@end
================================================
FILE: samples/ImageList/ios/RXPImageList/AppDelegate.m
================================================
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "AppDelegate.h"
#import
#import
#import
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"RXApp"
initialProperties:nil];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
@end
================================================
FILE: samples/ImageList/ios/RXPImageList/Base.lproj/LaunchScreen.xib
================================================
================================================
FILE: samples/ImageList/ios/RXPImageList/Images.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: samples/ImageList/ios/RXPImageList/Images.xcassets/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: samples/ImageList/ios/RXPImageList/Info.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleDisplayName
RXPImageList
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
APPL
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1
LSRequiresIPhoneOS
NSLocationWhenInUseUsageDescription
UILaunchStoryboardName
LaunchScreen
UIRequiredDeviceCapabilities
armv7
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UIViewControllerBasedStatusBarAppearance
NSLocationWhenInUseUsageDescription
NSAppTransportSecurity
NSAllowsArbitraryLoads
NSExceptionDomains
localhost
NSExceptionAllowsInsecureHTTPLoads
================================================
FILE: samples/ImageList/ios/RXPImageList/main.m
================================================
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
================================================
FILE: samples/ImageList/ios/RXPImageList-tvOS/Info.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
APPL
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1
LSRequiresIPhoneOS
UILaunchStoryboardName
LaunchScreen
UIRequiredDeviceCapabilities
armv7
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UIViewControllerBasedStatusBarAppearance
NSLocationWhenInUseUsageDescription
NSAppTransportSecurity
NSExceptionDomains
localhost
NSExceptionAllowsInsecureHTTPLoads
================================================
FILE: samples/ImageList/ios/RXPImageList-tvOSTests/Info.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
BNDL
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1
================================================
FILE: samples/ImageList/ios/RXPImageList.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
00E356F31AD99517003FC87E /* RXPImageListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* RXPImageListTests.m */; };
11D1A2F320CAFA9E000508D9 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */; };
2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */; };
2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */; };
2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */; };
2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */; };
2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */; };
2D16E6881FA4F8E400B85C8A /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D16E6891FA4F8E400B85C8A /* libReact.a */; };
2DCD954D1E0B4F2C00145EB5 /* RXPImageListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* RXPImageListTests.m */; };
2DF0FFEE2056DD460020B375 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.a */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */; };
ED297163215061F000B7C4FE /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED297162215061F000B7C4FE /* JavaScriptCore.framework */; };
ED2971652150620600B7C4FE /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED2971642150620600B7C4FE /* JavaScriptCore.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTActionSheet;
};
00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTGeolocation;
};
00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
remoteInfo = RCTImage;
};
00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B511DB1A9E6C8500147676;
remoteInfo = RCTNetwork;
};
00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 832C81801AAF6DEF007FA2F7;
remoteInfo = RCTVibration;
};
00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
remoteInfo = RXPImageList;
};
139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTSettings;
};
139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3C86DF461ADF2C930047B81A;
remoteInfo = RCTWebSocket;
};
146834031AC3E56700842450 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
remoteInfo = React;
};
2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7;
remoteInfo = "RXPImageList-tvOS";
};
2D16E6711FA4F8DC00B85C8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = ADD01A681E09402E00F6D226;
remoteInfo = "RCTBlob-tvOS";
};
2D16E6831FA4F8DC00B85C8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3DBE0D001F3B181A0099AA32;
remoteInfo = fishhook;
};
2D16E6851FA4F8DC00B85C8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32;
remoteInfo = "fishhook-tvOS";
};
2DF0FFDE2056DD460020B375 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = EBF21BDC1FC498900052F4D5;
remoteInfo = jsinspector;
};
2DF0FFE02056DD460020B375 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = EBF21BFA1FC4989A0052F4D5;
remoteInfo = "jsinspector-tvOS";
};
2DF0FFE22056DD460020B375 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7;
remoteInfo = "third-party";
};
2DF0FFE42056DD460020B375 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D383D3C1EBD27B6005632C8;
remoteInfo = "third-party-tvOS";
};
2DF0FFE62056DD460020B375 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 139D7E881E25C6D100323FB7;
remoteInfo = "double-conversion";
};
2DF0FFE82056DD460020B375 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D383D621EBD27B9005632C8;
remoteInfo = "double-conversion-tvOS";
};
2DF0FFEA2056DD460020B375 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 9936F3131F5F2E4B0010BF04;
remoteInfo = privatedata;
};
2DF0FFEC2056DD460020B375 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04;
remoteInfo = "privatedata-tvOS";
};
3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A283A1D9B042B00D4039D;
remoteInfo = "RCTImage-tvOS";
};
3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28471D9B043800D4039D;
remoteInfo = "RCTLinking-tvOS";
};
3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28541D9B044C00D4039D;
remoteInfo = "RCTNetwork-tvOS";
};
3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28611D9B046600D4039D;
remoteInfo = "RCTSettings-tvOS";
};
3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A287B1D9B048500D4039D;
remoteInfo = "RCTText-tvOS";
};
3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28881D9B049200D4039D;
remoteInfo = "RCTWebSocket-tvOS";
};
3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28131D9B038B00D4039D;
remoteInfo = "React-tvOS";
};
3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D3C059A1DE3340900C268FA;
remoteInfo = yoga;
};
3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D3C06751DE3340C00C268FA;
remoteInfo = "yoga-tvOS";
};
3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4;
remoteInfo = cxxreact;
};
3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4;
remoteInfo = "cxxreact-tvOS";
};
3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4;
remoteInfo = jschelpers;
};
3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4;
remoteInfo = "jschelpers-tvOS";
};
5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTAnimation;
};
5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28201D9B03D100D4039D;
remoteInfo = "RCTAnimation-tvOS";
};
78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTLinking;
};
832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B5119B1A9E6C1200147676;
remoteInfo = RCTText;
};
ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 358F4ED71D1E81A9004DF814;
remoteInfo = RCTBlob;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; };
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; };
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; };
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; };
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; };
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; };
00E356EE1AD99517003FC87E /* RXPImageListTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RXPImageListTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
00E356F21AD99517003FC87E /* RXPImageListTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RXPImageListTests.m; sourceTree = ""; };
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; };
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; };
13B07F961A680F5B00A75B9A /* RXPImageList.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RXPImageList.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = RXPImageList/AppDelegate.h; sourceTree = ""; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = RXPImageList/AppDelegate.m; sourceTree = ""; };
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = RXPImageList/Images.xcassets; sourceTree = ""; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RXPImageList/Info.plist; sourceTree = ""; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = RXPImageList/main.m; sourceTree = ""; };
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; };
2D02E47B1E0B4A5D006451C7 /* RXPImageList-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RXPImageList-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
2D02E4901E0B4A5D006451C7 /* RXPImageList-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RXPImageList-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
2D16E6891FA4F8E400B85C8A /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; };
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; };
ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = "../node_modules/react-native/Libraries/Blob/RCTBlob.xcodeproj"; sourceTree = ""; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
00E356EB1AD99517003FC87E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
ED297163215061F000B7C4FE /* JavaScriptCore.framework in Frameworks */,
ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */,
11D1A2F320CAFA9E000508D9 /* libRCTAnimation.a in Frameworks */,
146834051AC3E58100842450 /* libReact.a in Frameworks */,
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */,
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */,
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */,
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */,
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E4781E0B4A5D006451C7 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
ED2971652150620600B7C4FE /* JavaScriptCore.framework in Frameworks */,
2D16E6881FA4F8E400B85C8A /* libReact.a in Frameworks */,
2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */,
2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */,
2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */,
2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */,
2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */,
2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */,
2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E48D1E0B4A5D006451C7 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2DF0FFEE2056DD460020B375 /* libReact.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
00C302A81ABCB8CE00DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */,
);
name = Products;
sourceTree = "";
};
00C302B61ABCB90400DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */,
);
name = Products;
sourceTree = "";
};
00C302BC1ABCB91800DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302C01ABCB91800DB3ED1 /* libRCTImage.a */,
3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */,
);
name = Products;
sourceTree = "";
};
00C302D41ABCB9D200DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */,
3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */,
);
name = Products;
sourceTree = "";
};
00C302E01ABCB9EE00DB3ED1 /* Products */ = {
isa = PBXGroup;
children = (
00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */,
);
name = Products;
sourceTree = "";
};
00E356EF1AD99517003FC87E /* RXPImageListTests */ = {
isa = PBXGroup;
children = (
00E356F21AD99517003FC87E /* RXPImageListTests.m */,
00E356F01AD99517003FC87E /* Supporting Files */,
);
path = RXPImageListTests;
sourceTree = "";
};
00E356F01AD99517003FC87E /* Supporting Files */ = {
isa = PBXGroup;
children = (
00E356F11AD99517003FC87E /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "";
};
139105B71AF99BAD00B5F7CC /* Products */ = {
isa = PBXGroup;
children = (
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */,
3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */,
);
name = Products;
sourceTree = "";
};
139FDEE71B06529A00C62182 /* Products */ = {
isa = PBXGroup;
children = (
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */,
3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */,
2D16E6841FA4F8DC00B85C8A /* libfishhook.a */,
2D16E6861FA4F8DC00B85C8A /* libfishhook-tvOS.a */,
);
name = Products;
sourceTree = "";
};
13B07FAE1A68108700A75B9A /* RXPImageList */ = {
isa = PBXGroup;
children = (
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
);
name = RXPImageList;
sourceTree = "";
};
146834001AC3E56700842450 /* Products */ = {
isa = PBXGroup;
children = (
146834041AC3E56700842450 /* libReact.a */,
3DAD3EA31DF850E9000B6D8A /* libReact.a */,
3DAD3EA51DF850E9000B6D8A /* libyoga.a */,
3DAD3EA71DF850E9000B6D8A /* libyoga.a */,
3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */,
3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */,
3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */,
3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */,
2DF0FFDF2056DD460020B375 /* libjsinspector.a */,
2DF0FFE12056DD460020B375 /* libjsinspector-tvOS.a */,
2DF0FFE32056DD460020B375 /* libthird-party.a */,
2DF0FFE52056DD460020B375 /* libthird-party.a */,
2DF0FFE72056DD460020B375 /* libdouble-conversion.a */,
2DF0FFE92056DD460020B375 /* libdouble-conversion.a */,
2DF0FFEB2056DD460020B375 /* libprivatedata.a */,
2DF0FFED2056DD460020B375 /* libprivatedata-tvOS.a */,
);
name = Products;
sourceTree = "";
};
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
isa = PBXGroup;
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
2D16E6891FA4F8E400B85C8A /* libReact.a */,
);
name = Frameworks;
sourceTree = "";
};
5E91572E1DD0AC6500FF2AA8 /* Products */ = {
isa = PBXGroup;
children = (
5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */,
5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */,
);
name = Products;
sourceTree = "";
};
78C398B11ACF4ADC00677621 /* Products */ = {
isa = PBXGroup;
children = (
78C398B91ACF4ADC00677621 /* libRCTLinking.a */,
3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */,
);
name = Products;
sourceTree = "";
};
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */,
146833FF1AC3E56700842450 /* React.xcodeproj */,
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */,
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */,
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */,
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
);
name = Libraries;
sourceTree = "";
};
832341B11AAA6A8300B99B32 /* Products */ = {
isa = PBXGroup;
children = (
832341B51AAA6A8300B99B32 /* libRCTText.a */,
3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */,
);
name = Products;
sourceTree = "";
};
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
13B07FAE1A68108700A75B9A /* RXPImageList */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* RXPImageListTests */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
);
indentWidth = 2;
sourceTree = "";
tabWidth = 2;
usesTabs = 0;
};
83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* RXPImageList.app */,
00E356EE1AD99517003FC87E /* RXPImageListTests.xctest */,
2D02E47B1E0B4A5D006451C7 /* RXPImageList-tvOS.app */,
2D02E4901E0B4A5D006451C7 /* RXPImageList-tvOSTests.xctest */,
);
name = Products;
sourceTree = "";
};
ADBDB9201DFEBF0600ED6528 /* Products */ = {
isa = PBXGroup;
children = (
ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */,
2D16E6721FA4F8DC00B85C8A /* libRCTBlob-tvOS.a */,
);
name = Products;
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
00E356ED1AD99517003FC87E /* RXPImageListTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "RXPImageListTests" */;
buildPhases = (
00E356EA1AD99517003FC87E /* Sources */,
00E356EB1AD99517003FC87E /* Frameworks */,
00E356EC1AD99517003FC87E /* Resources */,
);
buildRules = (
);
dependencies = (
00E356F51AD99517003FC87E /* PBXTargetDependency */,
);
name = RXPImageListTests;
productName = RXPImageListTests;
productReference = 00E356EE1AD99517003FC87E /* RXPImageListTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
13B07F861A680F5B00A75B9A /* RXPImageList */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "RXPImageList" */;
buildPhases = (
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
);
buildRules = (
);
dependencies = (
);
name = RXPImageList;
productName = "Hello World";
productReference = 13B07F961A680F5B00A75B9A /* RXPImageList.app */;
productType = "com.apple.product-type.application";
};
2D02E47A1E0B4A5D006451C7 /* RXPImageList-tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "RXPImageList-tvOS" */;
buildPhases = (
2D02E4771E0B4A5D006451C7 /* Sources */,
2D02E4781E0B4A5D006451C7 /* Frameworks */,
2D02E4791E0B4A5D006451C7 /* Resources */,
2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */,
);
buildRules = (
);
dependencies = (
);
name = "RXPImageList-tvOS";
productName = "RXPImageList-tvOS";
productReference = 2D02E47B1E0B4A5D006451C7 /* RXPImageList-tvOS.app */;
productType = "com.apple.product-type.application";
};
2D02E48F1E0B4A5D006451C7 /* RXPImageList-tvOSTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "RXPImageList-tvOSTests" */;
buildPhases = (
2D02E48C1E0B4A5D006451C7 /* Sources */,
2D02E48D1E0B4A5D006451C7 /* Frameworks */,
2D02E48E1E0B4A5D006451C7 /* Resources */,
);
buildRules = (
);
dependencies = (
2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */,
);
name = "RXPImageList-tvOSTests";
productName = "RXPImageList-tvOSTests";
productReference = 2D02E4901E0B4A5D006451C7 /* RXPImageList-tvOSTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0940;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
00E356ED1AD99517003FC87E = {
CreatedOnToolsVersion = 6.2;
TestTargetID = 13B07F861A680F5B00A75B9A;
};
2D02E47A1E0B4A5D006451C7 = {
CreatedOnToolsVersion = 8.2.1;
ProvisioningStyle = Automatic;
};
2D02E48F1E0B4A5D006451C7 = {
CreatedOnToolsVersion = 8.2.1;
ProvisioningStyle = Automatic;
TestTargetID = 2D02E47A1E0B4A5D006451C7;
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "RXPImageList" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 83CBB9F61A601CBA00E9B192;
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */;
ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
},
{
ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */;
ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
},
{
ProductGroup = ADBDB9201DFEBF0600ED6528 /* Products */;
ProjectRef = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */;
},
{
ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */;
ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
},
{
ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */;
ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
},
{
ProductGroup = 78C398B11ACF4ADC00677621 /* Products */;
ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
},
{
ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */;
ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
},
{
ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */;
ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
},
{
ProductGroup = 832341B11AAA6A8300B99B32 /* Products */;
ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
},
{
ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */;
ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
},
{
ProductGroup = 139FDEE71B06529A00C62182 /* Products */;
ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
},
{
ProductGroup = 146834001AC3E56700842450 /* Products */;
ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
},
);
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* RXPImageList */,
00E356ED1AD99517003FC87E /* RXPImageListTests */,
2D02E47A1E0B4A5D006451C7 /* RXPImageList-tvOS */,
2D02E48F1E0B4A5D006451C7 /* RXPImageList-tvOSTests */,
);
};
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTActionSheet.a;
remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTGeolocation.a;
remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTImage.a;
remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTNetwork.a;
remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTVibration.a;
remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTSettings.a;
remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTWebSocket.a;
remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
146834041AC3E56700842450 /* libReact.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libReact.a;
remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2D16E6721FA4F8DC00B85C8A /* libRCTBlob-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTBlob-tvOS.a";
remoteRef = 2D16E6711FA4F8DC00B85C8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2D16E6841FA4F8DC00B85C8A /* libfishhook.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libfishhook.a;
remoteRef = 2D16E6831FA4F8DC00B85C8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2D16E6861FA4F8DC00B85C8A /* libfishhook-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libfishhook-tvOS.a";
remoteRef = 2D16E6851FA4F8DC00B85C8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2DF0FFDF2056DD460020B375 /* libjsinspector.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libjsinspector.a;
remoteRef = 2DF0FFDE2056DD460020B375 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2DF0FFE12056DD460020B375 /* libjsinspector-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libjsinspector-tvOS.a";
remoteRef = 2DF0FFE02056DD460020B375 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2DF0FFE32056DD460020B375 /* libthird-party.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libthird-party.a";
remoteRef = 2DF0FFE22056DD460020B375 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2DF0FFE52056DD460020B375 /* libthird-party.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libthird-party.a";
remoteRef = 2DF0FFE42056DD460020B375 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2DF0FFE72056DD460020B375 /* libdouble-conversion.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libdouble-conversion.a";
remoteRef = 2DF0FFE62056DD460020B375 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2DF0FFE92056DD460020B375 /* libdouble-conversion.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libdouble-conversion.a";
remoteRef = 2DF0FFE82056DD460020B375 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2DF0FFEB2056DD460020B375 /* libprivatedata.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libprivatedata.a;
remoteRef = 2DF0FFEA2056DD460020B375 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2DF0FFED2056DD460020B375 /* libprivatedata-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libprivatedata-tvOS.a";
remoteRef = 2DF0FFEC2056DD460020B375 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTImage-tvOS.a";
remoteRef = 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTLinking-tvOS.a";
remoteRef = 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTNetwork-tvOS.a";
remoteRef = 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTSettings-tvOS.a";
remoteRef = 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTText-tvOS.a";
remoteRef = 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRCTWebSocket-tvOS.a";
remoteRef = 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3EA31DF850E9000B6D8A /* libReact.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libReact.a;
remoteRef = 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3EA51DF850E9000B6D8A /* libyoga.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libyoga.a;
remoteRef = 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3EA71DF850E9000B6D8A /* libyoga.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libyoga.a;
remoteRef = 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libcxxreact.a;
remoteRef = 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libcxxreact.a;
remoteRef = 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libjschelpers.a;
remoteRef = 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libjschelpers.a;
remoteRef = 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTAnimation.a;
remoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTAnimation.a;
remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTLinking.a;
remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
832341B51AAA6A8300B99B32 /* libRCTText.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTText.a;
remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTBlob.a;
remoteRef = ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
00E356EC1AD99517003FC87E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F8E1A680F5B00A75B9A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E4791E0B4A5D006451C7 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E48E1E0B4A5D006451C7 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Bundle React Native code and images";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
};
2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Bundle React Native Code And Images";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
00E356EA1AD99517003FC87E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
00E356F31AD99517003FC87E /* RXPImageListTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F871A680F5B00A75B9A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E4771E0B4A5D006451C7 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */,
2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D02E48C1E0B4A5D006451C7 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2DCD954D1E0B4F2C00145EB5 /* RXPImageListTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 13B07F861A680F5B00A75B9A /* RXPImageList */;
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
};
2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 2D02E47A1E0B4A5D006451C7 /* RXPImageList-tvOS */;
targetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
13B07FB21A68108700A75B9A /* Base */,
);
name = LaunchScreen.xib;
path = RXPImageList;
sourceTree = "";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
00E356F61AD99517003FC87E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = RXPImageListTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.create.RX.app.RXPImageList";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RXPImageList.app/RXPImageList";
};
name = Debug;
};
00E356F71AD99517003FC87E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
COPY_PHASE_STRIP = NO;
INFOPLIST_FILE = RXPImageListTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.create.RX.app.RXPImageList";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RXPImageList.app/RXPImageList";
};
name = Release;
};
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = NO;
INFOPLIST_FILE = RXPImageList/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.create.RX.app.RXPImageList";
PRODUCT_NAME = RXPImageList;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 1;
INFOPLIST_FILE = RXPImageList/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.create.RX.app.RXPImageList";
PRODUCT_NAME = RXPImageList;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
2D02E4971E0B4A5E006451C7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = "RXPImageList-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.create.RX.app.RXPImageList-tvOS";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Debug;
};
2D02E4981E0B4A5E006451C7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = "RXPImageList-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.create.RX.app.RXPImageList-tvOS";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Release;
};
2D02E4991E0B4A5E006451C7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = "RXPImageList-tvOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.create.RX.app.RXPImageList-tvOSTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RXPImageList-tvOS.app/RXPImageList-tvOS";
TVOS_DEPLOYMENT_TARGET = 10.1;
};
name = Debug;
};
2D02E49A1E0B4A5E006451C7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = "RXPImageList-tvOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.create.RX.app.RXPImageList-tvOSTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RXPImageList-tvOS.app/RXPImageList-tvOS";
TVOS_DEPLOYMENT_TARGET = 10.1;
};
name = Release;
};
83CBBA201A601CBA00E9B192 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
83CBBA211A601CBA00E9B192 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "RXPImageListTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
00E356F61AD99517003FC87E /* Debug */,
00E356F71AD99517003FC87E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "RXPImageList" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13B07F941A680F5B00A75B9A /* Debug */,
13B07F951A680F5B00A75B9A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "RXPImageList-tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2D02E4971E0B4A5E006451C7 /* Debug */,
2D02E4981E0B4A5E006451C7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "RXPImageList-tvOSTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2D02E4991E0B4A5E006451C7 /* Debug */,
2D02E49A1E0B4A5E006451C7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "RXPImageList" */ = {
isa = XCConfigurationList;
buildConfigurations = (
83CBBA201A601CBA00E9B192 /* Debug */,
83CBBA211A601CBA00E9B192 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
}
================================================
FILE: samples/ImageList/ios/RXPImageList.xcodeproj/xcshareddata/xcschemes/RXPImageList-tvOS.xcscheme
================================================
================================================
FILE: samples/ImageList/ios/RXPImageList.xcodeproj/xcshareddata/xcschemes/RXPImageList.xcscheme
================================================
================================================
FILE: samples/ImageList/ios/RXPImageListTests/Info.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
BNDL
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1
================================================
FILE: samples/ImageList/ios/RXPImageListTests/RXPImageListTests.m
================================================
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import
#import
#import
#import
#define TIMEOUT_SECONDS 600
#define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
@interface RXPImageListTests : XCTestCase
@end
@implementation RXPImageListTests
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
{
if (test(view)) {
return YES;
}
for (UIView *subview in [view subviews]) {
if ([self findSubviewInView:subview matching:test]) {
return YES;
}
}
return NO;
}
- (void)testRendersWelcomeScreen
{
UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
BOOL foundElement = NO;
__block NSString *redboxError = nil;
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
if (level >= RCTLogLevelError) {
redboxError = message;
}
});
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
return YES;
}
return NO;
}];
}
RCTSetLogFunction(RCTDefaultLogFunction);
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
}
@end
================================================
FILE: samples/ImageList/jest/enzyme.config.js
================================================
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });
================================================
FILE: samples/ImageList/jest/jest.config.js
================================================
module.exports = {
rootDir: '../',
roots: ['/src'],
moduleFileExtensions: [
'ts', 'tsx', 'js', 'json', 'node',
],
moduleDirectories: ['node_modules'],
snapshotSerializers: [
'enzyme-to-json/serializer',
],
setupFiles: [
'/jest/enzyme.config.js',
],
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.((t|j)sx?)$',
};
================================================
FILE: samples/ImageList/metro.config.js
================================================
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: { experimentalImportSupport: false, inlineRequires: false },
}),
},
};
================================================
FILE: samples/ImageList/package.json
================================================
{
"name": "rxpimagelist",
"private": true,
"main": "index.js",
"version": "2.0.0",
"scripts": {
"rn-cli": "node scripts/react-native.js",
"start:android": "npm run rn-cli run-android",
"start:windows": "npm run rn-cli run-windows",
"start:ios": "npm run rn-cli run-ios",
"start:web": "cross-env platform=web webpack-dev-server --config=web/webpack/dev.js --progress --colors --mode=development",
"start:rn-dev-server": "npm run rn-cli start --reset-cache",
"build:web": "cross-env platform=web webpack --config=web/webpack/prod.js --progress --colors --mode=production",
"test": "jest -c jest/jest.config.js",
"test:watch": "npm run test --watch",
"test:debug": "node --inspect-brk node_modules/.bin/jest -c jest/jest.config.js --runInBand",
"build:types": "tsc --emitDeclarationOnly",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -w",
"lint": "eslint --config .eslintrc --ext .ts,.tsx src"
},
"devDependencies": {
"@babel/core": "7.7.4",
"@babel/plugin-proposal-decorators": "7.7.4",
"@babel/preset-env": "7.7.4",
"@react-native-community/cli": "9.3.2",
"@types/enzyme": "3.10.3",
"@types/jest": "24.0.23",
"@typescript-eslint/eslint-plugin": "2.9.0",
"@typescript-eslint/parser": "2.9.0",
"babel-loader": "8.3.0",
"compression-webpack-plugin": "3.0.0",
"cross-env": "6.0.3",
"enzyme": "3.10.0",
"enzyme-adapter-react-16": "1.15.1",
"enzyme-to-json": "3.4.3",
"eslint": "6.7.1",
"eslint-loader": "4.0.2",
"eslint-plugin-jest": "23.1.0",
"eslint-plugin-react": "7.17.0",
"eslint-plugin-reactxp": "0.1.7",
"fork-ts-checker-webpack-plugin": "3.1.1",
"html-webpack-plugin": "5.5.0",
"jest": "29.1.2",
"metro-react-native-babel-preset": "0.57.0",
"rnpm-plugin-windows": "0.3.8",
"typescript": "3.7.2",
"webpack": "5.75.0",
"webpack-cli": "5.0.1",
"webpack-dev-server": "3.9.0",
"webpack-merge": "4.2.2"
},
"dependencies": {
"react": "16.12.0",
"react-dom": "16.12.0",
"react-native": "0.70.6",
"react-native-windows": "0.59.0-rc.3",
"reactxp": "^2.0.0",
"reactxp-virtuallistview": "^2.1.0",
"resub": "^1.2.2",
"simplerestclients": "^0.2.11",
"synctasks": "^0.3.3"
}
}
================================================
FILE: samples/ImageList/scripts/react-native.js
================================================
require('@react-native-community/cli').run();
================================================
FILE: samples/ImageList/src/App.tsx
================================================
/**
* Main entry point for sample image fetching app.
*/
import * as React from 'react';
import * as RX from 'reactxp';
import { DEBUG, DEV } from './config';
import RootView from './views/RootView';
class App {
init() {
RX.App.initialize(DEBUG, DEV);
RX.UserInterface.setMainView(this._renderRootView());
}
private _renderRootView() {
return (
);
}
}
export default new App();
================================================
FILE: samples/ImageList/src/config.ts
================================================
declare const __DEV__: boolean;
export const DEBUG = __DEV__;
export const DEV = __DEV__;
================================================
FILE: samples/ImageList/src/controls/SearchField.tsx
================================================
/**
* ImageListPanel.tsx
*
* Display first screen of the image list sample application.
*/
import * as React from 'react';
import * as RX from 'reactxp';
export interface SearchFieldProps {
onChange: (value: string) => void;
}
interface SearchFieldState {
value: string;
}
const _styles = {
input: RX.Styles.createTextInputStyle({
borderStyle: 'solid',
borderColor: '#999',
borderWidth: 1,
fontSize: 16,
padding: 5,
margin: 10,
height: 30
})
};
export class SearchField extends RX.Component {
readonly state: SearchFieldState = { value: '' };
render() {
return (
);
}
private _handleChangeText = (value: string) => (
this.setState({ value }, () => this.props.onChange(value))
)
}
export default SearchField;
================================================
FILE: samples/ImageList/src/index.tsx
================================================
import App from './App';
App.init();
================================================
FILE: samples/ImageList/src/services/GiphyClient.ts
================================================
/**
* REST client "adapter" for talking to Giphy service.
*/
import * as SyncTasks from 'synctasks';
import { GenericRestClient } from 'simplerestclients';
interface GiphyImageDescriptor {
height: string;
width: string;
url: string;
}
interface GiphyImages {
fixed_height_downsampled: GiphyImageDescriptor;
fixed_height_still: GiphyImageDescriptor;
fixed_height: GiphyImageDescriptor;
fixed_width_still: GiphyImageDescriptor;
fixed_width: GiphyImageDescriptor;
original: GiphyImageDescriptor;
}
interface GiphyDataImage {
images: GiphyImages;
}
interface GiphySearchResponse {
data: GiphyDataImage[];
}
export interface GiphySearchResult {
originalUrl: string;
smallUrl: string;
}
const GIPHY_API_URL = 'https://api.giphy.com/v1/gifs/search';
const GIPHY_API_KEY = 'dc6zaTOxFJmzC';
export class GiphyClient extends GenericRestClient {
searchImages(query: string, limit = 25, offset = 0, rating = 'g'): SyncTasks.Promise {
const url = this._buildUrl(query, limit, offset, rating);
return this._performApiCall(url, 'GET', undefined, undefined)
.then(response => {
if (!response.body || !response.body.data) {
return [];
}
return response.body.data.map(this._normilizeResponse);
});
}
private _buildUrl = (query: string, limit: number, offset: number, rating: string): string => (
`?api_key=${ GIPHY_API_KEY }&q=${ encodeURIComponent(query) }&limit=${ limit }&offset=${ offset }&rating=${ rating }`
)
private _normilizeResponse = ({ images }: GiphyDataImage): GiphySearchResult => ({
originalUrl: images.original.url,
smallUrl: images.fixed_height.url
})
}
export default new GiphyClient(GIPHY_API_URL);
================================================
FILE: samples/ImageList/src/stores/ImageStore.ts
================================================
/**
* Basic store based on ReSub.
*/
import * as SyncTasks from 'synctasks';
import { StoreBase, AutoSubscribeStore, autoSubscribe } from 'resub';
import GiphyClient from '../services/GiphyClient';
export interface Image {
originalUrl: string;
smallUrl: string;
}
@AutoSubscribeStore
export class ImageStore extends StoreBase {
private _isSearchPending = false;
private _isFirstSearch = true;
private _lastSearchQuery = '';
private _searchQuery = '';
private _images: Image[] = [];
private _request: SyncTasks.Promise | null = null;
@autoSubscribe
getImages() {
return this._images;
}
@autoSubscribe
getSearchQuery() {
return this._searchQuery;
}
@autoSubscribe
isPerformingSearch() {
return this._isSearchPending;
}
@autoSubscribe
isFirstSearch() {
return this._isFirstSearch;
}
updateImages(searchQuery: string) {
const searchQueryTrimmed = searchQuery.trim();
this._searchQuery = searchQuery;
if (this._shouldSkipSearch(searchQueryTrimmed)) {
return;
}
this._isFirstSearch = false;
this._lastSearchQuery = searchQueryTrimmed;
this._images = [];
// If the query is empty, don't bother with the API call.
if (!searchQuery) {
this._cancelPreviousSearch();
this.trigger();
return;
}
this._isSearchPending = true;
this.trigger();
this._cancelPreviousSearch();
this._request = this._searchImages(searchQuery);
}
private _searchImages(query: string): SyncTasks.Promise {
return GiphyClient.searchImages(query)
.then(images => {
this._images = images;
this._isSearchPending = false;
this.trigger();
})
.catch(({ canceled }) => {
if (!canceled) {
this._isSearchPending = false;
this.trigger();
}
});
}
private _cancelPreviousSearch(): void {
if (this._request) {
this._request.cancel();
this._request = null;
}
}
// If this is the same as the last query or initial query is empty - don't bother.
private _shouldSkipSearch(query: string): boolean {
return (!query && !this._images.length) || (query === this._lastSearchQuery);
}
}
export default new ImageStore();
================================================
FILE: samples/ImageList/src/views/ImageList.tsx
================================================
/**
* Displays a simple list of images.
*/
import * as React from 'react';
import * as RX from 'reactxp';
import { ComponentBase } from 'resub';
import { VirtualListView, VirtualListViewItemInfo, VirtualListViewCellRenderDetails } from 'reactxp-virtuallistview';
import ImageStore, { Image } from '../stores/ImageStore';
interface ImageListItemInfo extends VirtualListViewItemInfo {
image: Image;
}
interface ImageListState {
isPerformingSearch: boolean;
isFirstSearch: boolean;
searchQuery: string;
images: ImageListItemInfo[];
}
const _itemHeight = 120;
const _styles = {
list: RX.Styles.createViewStyle({
backgroundColor: '#fff',
flexDirection: 'column',
alignSelf: 'stretch',
margin: 0
}),
row: RX.Styles.createViewStyle({
borderBottomWidth: 1,
borderTopWidth: 1,
borderColor: '#ccc',
borderStyle: 'solid',
flexDirection: 'row',
alignItems: 'center',
flex: 1,
height: _itemHeight
}),
image: RX.Styles.createImageStyle({
height: 100,
width: 133
}),
main: RX.Styles.createViewStyle({
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'stretch',
flex: 1
}),
labelText: RX.Styles.createTextStyle({
textAlign: 'center',
fontSize: 30
}),
linkText: RX.Styles.createLinkStyle({
padding: 12,
fontSize: 14,
color: 'blue',
flex: -1
}),
searchQueryText: RX.Styles.createTextStyle({
fontWeight: 'bold',
fontSize: 30
})
};
export class ImageList extends ComponentBase<{}, ImageListState> {
render() {
const { isPerformingSearch, isFirstSearch, searchQuery, images } = this.state;
// If the search is pending, render a spinner.
if (isPerformingSearch) {
return (
);
}
if (!isFirstSearch && !images.length && searchQuery) {
return (
No Results for { searchQuery }
);
}
return (
);
}
protected _buildState(): ImageListState {
return {
isPerformingSearch: ImageStore.isPerformingSearch(),
isFirstSearch: ImageStore.isFirstSearch(),
searchQuery: ImageStore.getSearchQuery(),
images: ImageStore.getImages().map(this._normilizeImage)
};
}
private _renderItem = (details: VirtualListViewCellRenderDetails) => {
const item = details.item;
return (
{ item.image.originalUrl }
);
}
private _normilizeImage = (image: Image) => ({
template: 'image',
height: _itemHeight,
image,
key: image.smallUrl
})
}
export default ImageList;
================================================
FILE: samples/ImageList/src/views/RootView.tsx
================================================
/**
* RootView.tsx
*
* Display first screen of the image list sample application.
*/
import * as React from 'react';
import * as RX from 'reactxp';
import ImageStore from '../stores/ImageStore';
import SearchField from '../controls/SearchField';
import ImageList from './ImageList';
const _styles = {
main: RX.Styles.createViewStyle({
alignSelf: 'stretch',
flex: 1
}),
images: RX.Styles.createViewStyle({
marginTop: 10,
padding: 10,
alignSelf: 'stretch',
flex: 1
}),
statusSpacer: RX.Styles.createViewStyle({
marginTop: 22
})
};
class RootView extends RX.Component {
render() {
return (
);
}
private _updateImages = (query: string) => (
ImageStore.updateImages(query)
)
}
export default RootView;
================================================
FILE: samples/ImageList/tsconfig.json
================================================
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"strictPropertyInitialization": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"moduleResolution": "node",
"strictNullChecks": true,
"esModuleInterop": true,
"noUnusedLocals": true,
"noImplicitThis": true,
"declarationDir": "dist-types",
"importHelpers": true,
"noImplicitAny": true,
"skipLibCheck": true,
"declaration": true,
"jsxFactory": "React",
"noResolve": false,
"module": "es2015",
"target": "es2015",
"jsx": "react",
"lib": ["es5", "es2015", "dom"]
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
================================================
FILE: samples/ImageList/web/index.hmr.js
================================================
import '../index';
module.hot.accept();
================================================
FILE: samples/ImageList/web/template.html
================================================
RXPImageList
================================================
FILE: samples/ImageList/web/webpack/common.js
================================================
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const path = require('path');
const ROOT_PATH = path.join(__dirname, '..', '..');
const DIST_PATH = path.join(ROOT_PATH, 'dist-web');
const APP_PATH = path.join(ROOT_PATH, 'src');
const WEB_PATH = path.join(ROOT_PATH, 'web');
const TS_CONFIG_PATH = path.join(ROOT_PATH, 'tsconfig.json');
const buildConfig = (env, argv) => ({
entry: ROOT_PATH,
resolve: {
extensions: ['.ts', '.tsx', '.js'],
},
module: {
rules: [
{ test: /\.jsx?$/, loader: 'eslint-loader', include: APP_PATH, enforce: 'pre' },
{ test: /\.tsx?$/, loader: 'babel-loader', include: APP_PATH }
],
},
plugins: [
new webpack.DefinePlugin({ __DEV__: argv.mode === 'development' }),
new HtmlWebpackPlugin({ inject: true, template: path.join(WEB_PATH, 'template.html') }),
new ForkTsCheckerWebpackPlugin({ tsconfig: TS_CONFIG_PATH, async: true }),
],
});
module.exports = {
buildConfig,
APP_PATH,
DIST_PATH,
WEB_PATH,
};
================================================
FILE: samples/ImageList/web/webpack/dev.js
================================================
const webpack = require('webpack');
const merge = require('webpack-merge');
const path = require('path');
const { buildConfig, APP_PATH, WEB_PATH } = require('./common');
module.exports = (env, argv) => (
merge(buildConfig(env, argv), {
entry: path.join(WEB_PATH, 'index.hmr.js'),
devtool: 'inline-source-map',
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
devServer: {
contentBase: APP_PATH,
openPage: '',
inline: true,
stats: 'minimal',
open: true,
port: 9999,
hot: true,
},
})
);
================================================
FILE: samples/ImageList/web/webpack/prod.js
================================================
const CompressionPlugin = require('compression-webpack-plugin');
const merge = require('webpack-merge');
const { buildConfig, DIST_PATH } = require('./common');
module.exports = (env, argv) => merge(buildConfig(env, argv), {
devtool: false,
output: {
filename: 'bundle-[hash].js',
path: DIST_PATH,
},
plugins: [
new CompressionPlugin({ algorithm: 'gzip', filename: '[path].gz' }),
],
});
================================================
FILE: samples/ImageList/windows/.gitignore
================================================
*AppPackages*
*BundleArtifacts*
#OS junk files
[Tt]humbs.db
*.DS_Store
#Visual Studio files
*.[Oo]bj
*.user
*.aps
*.pch
*.vspscc
*.vssscc
*_i.c
*_p.c
*.ncb
*.suo
*.tlb
*.tlh
*.bak
*.[Cc]ache
*.ilk
*.log
*.lib
*.sbr
*.sdf
*.opensdf
*.opendb
*.unsuccessfulbuild
ipch/
[Oo]bj/
[Bb]in
[Dd]ebug*/
[Rr]elease*/
Ankh.NoLoad
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
#MonoDevelop
*.pidb
*.userprefs
#Tooling
_ReSharper*/
*.resharper
[Tt]est[Rr]esult*
*.sass-cache
#Project files
[Bb]uild/
#Subversion files
.svn
# Office Temp Files
~$*
# vim Temp Files
*~
#NuGet
packages/
*.nupkg
#ncrunch
*ncrunch*
*crunch*.local.xml
# visual studio database projects
*.dbmdl
#Test files
*.testsettings
#Other files
*.DotSettings
.vs/
*project.lock.json
================================================
FILE: samples/ImageList/windows/RXPImageList/App.xaml
================================================
================================================
FILE: samples/ImageList/windows/RXPImageList/App.xaml.cs
================================================
using ReactNative;
namespace RXPImageList
{
///
/// Provides application-specific behavior to supplement the default Application class.
///
sealed partial class App : ReactApplication
{
private readonly ReactNativeHost _host = new MainReactNativeHost();
///
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
///
public App()
{
this.InitializeComponent();
}
///
/// The React Native host.
///
public override ReactNativeHost Host => _host;
}
}
================================================
FILE: samples/ImageList/windows/RXPImageList/MainReactNativeHost.cs
================================================
using ReactNative;
using ReactNative.Modules.Core;
using ReactNative.Shell;
using System.Collections.Generic;
namespace RXPImageList
{
class MainReactNativeHost : ReactNativeHost
{
public override string MainComponentName => "RXApp";
#if !BUNDLE || DEBUG
public override bool UseDeveloperSupport => true;
#else
public override bool UseDeveloperSupport => false;
#endif
protected override string JavaScriptMainModuleName => "index";
#if BUNDLE
protected override string JavaScriptBundleFile => "ms-appx:///ReactAssets/index.windows.bundle";
#endif
protected override List Packages => new List
{
new MainReactPackage(),
};
}
}
================================================
FILE: samples/ImageList/windows/RXPImageList/Package.appxmanifest
================================================
RXPImageList
React Native for UWP
Assets\StoreLogo.png
================================================
FILE: samples/ImageList/windows/RXPImageList/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("RXPImageList")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RXPImageList")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]
================================================
FILE: samples/ImageList/windows/RXPImageList/Properties/Default.rd.xml
================================================
================================================
FILE: samples/ImageList/windows/RXPImageList/RXPImageList.csproj
================================================
Debug
x86
e1a04243-db95-4b1a-97de-1aa66875892d
AppContainerExe
Properties
RXPImageList
RXPImageList
en-US
UAP
10.0.14393.0
10.0.14393.0
14
512
{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
RXPImageList_TemporaryKey.pfx
D61C83D73FE27BA89E6F8D76ACE8A828936D91C0
PackageReference
Debug
Debug
Release
true
bin\x86\Debug\
DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
;2008
full
x86
false
prompt
true
true
bin\x86\DebugBundle\
DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE
;2008
true
full
x86
false
prompt
MinimumRecommendedRules.ruleset
true
bin\x86\Release\
TRACE;NETFX_CORE;WINDOWS_UWP
true
;2008
pdbonly
x86
false
prompt
true
true
bin\x86\ReleaseBundle\
TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE
true
;2008
true
pdbonly
x86
false
prompt
MinimumRecommendedRules.ruleset
true
true
true
bin\ARM\Debug\
DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
;2008
full
ARM
false
prompt
true
true
bin\ARM\DebugBundle\
DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE
;2008
true
full
ARM
false
prompt
MinimumRecommendedRules.ruleset
true
bin\ARM\Release\
TRACE;NETFX_CORE;WINDOWS_UWP
true
;2008
pdbonly
ARM
false
prompt
true
true
bin\ARM\ReleaseBundle\
TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE
true
;2008
true
pdbonly
ARM
false
prompt
MinimumRecommendedRules.ruleset
true
true
true
bin\x64\Debug\
DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
;2008
full
x64
false
prompt
true
true
bin\x64\DebugBundle\
DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE
;2008
true
full
x64
false
prompt
MinimumRecommendedRules.ruleset
true
bin\x64\Release\
TRACE;NETFX_CORE;WINDOWS_UWP
true
;2008
pdbonly
x64
false
prompt
true
true
bin\x64\ReleaseBundle\
TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;BUNDLE
true
;2008
true
pdbonly
x64
false
prompt
MinimumRecommendedRules.ruleset
true
true
App.xaml
Designer
MSBuild:Compile
Designer
{c7673ad5-e3aa-468c-a5fd-fa38154e205c}
ReactNative
PreserveNewest
6.0.6
14.0
%(Filename)%(Extension)
PreserveNewest
False
================================================
FILE: samples/ImageList/windows/RXPImageList/ReactAssets/.gitignore
================================================
*
!.gitignore
================================================
FILE: samples/ImageList/windows/RXPImageList.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.271
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RXPImageList", "RXPImageList\RXPImageList.csproj", "e1a04243-db95-4b1a-97de-1aa66875892d"
ProjectSection(ProjectDependencies) = postProject
{2EACF721-73B5-46AE-9775-4A8674D05A9C} = {2EACF721-73B5-46AE-9775-4A8674D05A9C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactNative", "..\node_modules\react-native-windows\ReactWindows\ReactNative\ReactNative.csproj", "{C7673AD5-E3AA-468C-A5FD-FA38154E205C}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ReactNative.Shared", "..\node_modules\react-native-windows\ReactWindows\ReactNative.Shared\ReactNative.Shared.shproj", "{EEA8B852-4D07-48E1-8294-A21AB5909FE5}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ChakraBridge", "..\node_modules\react-native-windows\ReactWindows\ChakraBridge\ChakraBridge.vcxproj", "{4B72C796-16D5-4E3A-81C0-3E36F531E578}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactNativeWebViewBridge", "..\node_modules\react-native-windows\ReactWindows\ReactNativeWebViewBridge\ReactNativeWebViewBridge.csproj", "{7596216B-669C-41F8-86DA-F3637F6545C0}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Facebook.Yoga.Shared", "..\node_modules\react-native-windows\Yoga\csharp\Facebook.Yoga\Facebook.Yoga.Shared.shproj", "{91C42D32-291D-4B72-90B4-551663D60B8B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yoga.uwp", "..\node_modules\react-native-windows\Yoga\csharp\Yoga\Yoga.Universal.vcxproj", "{2EACF721-73B5-46AE-9775-4A8674D05A9C}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
ReactNative.Shared\ReactNative.Shared.projitems*{c7673ad5-e3aa-468c-a5fd-fa38154e205c}*SharedItemsImports = 4
ReactNative.Shared\ReactNative.Shared.projitems*{eea8b852-4d07-48e1-8294-a21ab5909fe5}*SharedItemsImports = 13
..\Yoga\csharp\Facebook.Yoga\Facebook.Yoga.Shared.projitems*{c7673ad5-e3aa-468c-a5fd-fa38154e205c}*SharedItemsImports = 4
..\Yoga\csharp\Facebook.Yoga\Facebook.Yoga.Shared.projitems*{91c42d32-291d-4b72-90b4-551663d60b8b}*SharedItemsImports = 13
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
DebugBundle|ARM = DebugBundle|ARM
DebugBundle|x64 = DebugBundle|x64
DebugBundle|x86 = DebugBundle|x86
Release|ARM = Release|ARM
Release|x64 = Release|x64
Release|x86 = Release|x86
ReleaseBundle|ARM = ReleaseBundle|ARM
ReleaseBundle|x64 = ReleaseBundle|x64
ReleaseBundle|x86 = ReleaseBundle|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{e1a04243-db95-4b1a-97de-1aa66875892d}.Debug|ARM.ActiveCfg = Debug|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.Debug|ARM.Build.0 = Debug|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.Debug|ARM.Deploy.0 = Debug|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.Debug|x64.ActiveCfg = Debug|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.Debug|x64.Build.0 = Debug|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.Debug|x64.Deploy.0 = Debug|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.Debug|x86.ActiveCfg = Debug|x86
{e1a04243-db95-4b1a-97de-1aa66875892d}.Debug|x86.Build.0 = Debug|x86
{e1a04243-db95-4b1a-97de-1aa66875892d}.Debug|x86.Deploy.0 = Debug|x86
{e1a04243-db95-4b1a-97de-1aa66875892d}.DebugBundle|ARM.ActiveCfg = DebugBundle|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.DebugBundle|ARM.Build.0 = DebugBundle|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.DebugBundle|ARM.Deploy.0 = DebugBundle|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.DebugBundle|x64.ActiveCfg = DebugBundle|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.DebugBundle|x64.Build.0 = DebugBundle|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.DebugBundle|x64.Deploy.0 = DebugBundle|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.DebugBundle|x86.ActiveCfg = DebugBundle|x86
{e1a04243-db95-4b1a-97de-1aa66875892d}.DebugBundle|x86.Build.0 = DebugBundle|x86
{e1a04243-db95-4b1a-97de-1aa66875892d}.DebugBundle|x86.Deploy.0 = DebugBundle|x86
{e1a04243-db95-4b1a-97de-1aa66875892d}.Release|ARM.ActiveCfg = Release|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.Release|ARM.Build.0 = Release|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.Release|ARM.Deploy.0 = Release|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.Release|x64.ActiveCfg = Release|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.Release|x64.Build.0 = Release|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.Release|x64.Deploy.0 = Release|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.Release|x86.ActiveCfg = Release|x86
{e1a04243-db95-4b1a-97de-1aa66875892d}.Release|x86.Build.0 = Release|x86
{e1a04243-db95-4b1a-97de-1aa66875892d}.Release|x86.Deploy.0 = Release|x86
{e1a04243-db95-4b1a-97de-1aa66875892d}.ReleaseBundle|ARM.ActiveCfg = ReleaseBundle|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.ReleaseBundle|ARM.Build.0 = ReleaseBundle|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.ReleaseBundle|ARM.Deploy.0 = ReleaseBundle|ARM
{e1a04243-db95-4b1a-97de-1aa66875892d}.ReleaseBundle|x64.ActiveCfg = ReleaseBundle|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.ReleaseBundle|x64.Build.0 = ReleaseBundle|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.ReleaseBundle|x64.Deploy.0 = ReleaseBundle|x64
{e1a04243-db95-4b1a-97de-1aa66875892d}.ReleaseBundle|x86.ActiveCfg = ReleaseBundle|x86
{e1a04243-db95-4b1a-97de-1aa66875892d}.ReleaseBundle|x86.Build.0 = ReleaseBundle|x86
{e1a04243-db95-4b1a-97de-1aa66875892d}.ReleaseBundle|x86.Deploy.0 = ReleaseBundle|x86
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|ARM.ActiveCfg = Debug|ARM
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|ARM.Build.0 = Debug|ARM
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x64.ActiveCfg = Debug|x64
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x64.Build.0 = Debug|x64
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x86.ActiveCfg = Debug|x86
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x86.Build.0 = Debug|x86
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|ARM.ActiveCfg = Debug|ARM
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|ARM.Build.0 = Debug|ARM
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|x64.ActiveCfg = Debug|x64
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|x64.Build.0 = Debug|x64
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|x86.ActiveCfg = Debug|x86
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.DebugBundle|x86.Build.0 = Debug|x86
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|ARM.ActiveCfg = Release|ARM
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|ARM.Build.0 = Release|ARM
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x64.ActiveCfg = Release|x64
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x64.Build.0 = Release|x64
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x86.ActiveCfg = Release|x86
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x86.Build.0 = Release|x86
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|ARM.ActiveCfg = Release|ARM
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|ARM.Build.0 = Release|ARM
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|x64.ActiveCfg = Release|x64
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|x64.Build.0 = Release|x64
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|x86.ActiveCfg = Release|x86
{C7673AD5-E3AA-468C-A5FD-FA38154E205C}.ReleaseBundle|x86.Build.0 = Release|x86
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|ARM.ActiveCfg = Debug|ARM
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|ARM.Build.0 = Debug|ARM
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x64.ActiveCfg = Debug|x64
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x64.Build.0 = Debug|x64
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x86.ActiveCfg = Debug|Win32
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x86.Build.0 = Debug|Win32
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|ARM.ActiveCfg = Debug|ARM
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|ARM.Build.0 = Debug|ARM
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|x64.ActiveCfg = Debug|x64
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|x64.Build.0 = Debug|x64
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|x86.ActiveCfg = Debug|Win32
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.DebugBundle|x86.Build.0 = Debug|Win32
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|ARM.ActiveCfg = Release|ARM
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|ARM.Build.0 = Release|ARM
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x64.ActiveCfg = Release|x64
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x64.Build.0 = Release|x64
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x86.ActiveCfg = Release|Win32
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x86.Build.0 = Release|Win32
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|ARM.ActiveCfg = Release|ARM
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|ARM.Build.0 = Release|ARM
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|x64.ActiveCfg = Release|x64
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|x64.Build.0 = Release|x64
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|x86.ActiveCfg = Release|Win32
{4B72C796-16D5-4E3A-81C0-3E36F531E578}.ReleaseBundle|x86.Build.0 = Release|Win32
{7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|ARM.ActiveCfg = Debug|ARM
{7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|ARM.Build.0 = Debug|ARM
{7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|x64.ActiveCfg = Debug|x64
{7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|x64.Build.0 = Debug|x64
{7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|x86.ActiveCfg = Debug|x86
{7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|x86.Build.0 = Debug|x86
{7596216B-669C-41F8-86DA-F3637F6545C0}.DebugBundle|ARM.ActiveCfg = Debug|ARM
{7596216B-669C-41F8-86DA-F3637F6545C0}.DebugBundle|ARM.Build.0 = Debug|ARM
{7596216B-669C-41F8-86DA-F3637F6545C0}.DebugBundle|x64.ActiveCfg = Debug|x64
{7596216B-669C-41F8-86DA-F3637F6545C0}.DebugBundle|x64.Build.0 = Debug|x64
{7596216B-669C-41F8-86DA-F3637F6545C0}.DebugBundle|x86.ActiveCfg = Debug|x86
{7596216B-669C-41F8-86DA-F3637F6545C0}.DebugBundle|x86.Build.0 = Debug|x86
{7596216B-669C-41F8-86DA-F3637F6545C0}.Release|ARM.ActiveCfg = Release|ARM
{7596216B-669C-41F8-86DA-F3637F6545C0}.Release|ARM.Build.0 = Release|ARM
{7596216B-669C-41F8-86DA-F3637F6545C0}.Release|x64.ActiveCfg = Release|x64
{7596216B-669C-41F8-86DA-F3637F6545C0}.Release|x64.Build.0 = Release|x64
{7596216B-669C-41F8-86DA-F3637F6545C0}.Release|x86.ActiveCfg = Release|x86
{7596216B-669C-41F8-86DA-F3637F6545C0}.Release|x86.Build.0 = Release|x86
{7596216B-669C-41F8-86DA-F3637F6545C0}.ReleaseBundle|ARM.ActiveCfg = Release|ARM
{7596216B-669C-41F8-86DA-F3637F6545C0}.ReleaseBundle|ARM.Build.0 = Release|ARM
{7596216B-669C-41F8-86DA-F3637F6545C0}.ReleaseBundle|x64.ActiveCfg = Release|x64
{7596216B-669C-41F8-86DA-F3637F6545C0}.ReleaseBundle|x64.Build.0 = Release|x64
{7596216B-669C-41F8-86DA-F3637F6545C0}.ReleaseBundle|x86.ActiveCfg = Release|x86
{7596216B-669C-41F8-86DA-F3637F6545C0}.ReleaseBundle|x86.Build.0 = Release|x86
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Debug|ARM.ActiveCfg = Debug|ARM
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Debug|ARM.Build.0 = Debug|ARM
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Debug|x64.ActiveCfg = Debug|x64
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Debug|x64.Build.0 = Debug|x64
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Debug|x86.ActiveCfg = Debug|Win32
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Debug|x86.Build.0 = Debug|Win32
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.DebugBundle|ARM.ActiveCfg = Debug|ARM
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.DebugBundle|ARM.Build.0 = Debug|ARM
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.DebugBundle|x64.ActiveCfg = Debug|x64
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.DebugBundle|x64.Build.0 = Debug|x64
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.DebugBundle|x86.ActiveCfg = Debug|Win32
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.DebugBundle|x86.Build.0 = Debug|Win32
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Release|ARM.ActiveCfg = Release|ARM
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Release|ARM.Build.0 = Release|ARM
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Release|x64.ActiveCfg = Release|x64
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Release|x64.Build.0 = Release|x64
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Release|x86.ActiveCfg = Release|Win32
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.Release|x86.Build.0 = Release|Win32
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.ReleaseBundle|ARM.ActiveCfg = Release|ARM
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.ReleaseBundle|ARM.Build.0 = Release|ARM
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.ReleaseBundle|x64.ActiveCfg = Release|x64
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.ReleaseBundle|x64.Build.0 = Release|x64
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.ReleaseBundle|x86.ActiveCfg = Release|Win32
{2EACF721-73B5-46AE-9775-4A8674D05A9C}.ReleaseBundle|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
================================================
FILE: samples/README.md
================================================
# ReactXP Sample Code
Here are some small projects that demonstrate how to use ReactXP.
[Image List](https://github.com/Microsoft/reactxp/tree/master/samples/ImageList)
[RXP Test](https://github.com/Microsoft/reactxp/tree/master/samples/RXPTest)
[To Do List](https://github.com/Microsoft/reactxp/tree/master/samples/TodoList)
[Hello World](https://github.com/Microsoft/reactxp/tree/master/samples/hello-world)
[Hello World JS](https://github.com/Microsoft/reactxp/tree/master/samples/hello-world-js)
================================================
FILE: samples/RXPTest/.gitignore
================================================
# OSX
#
.DS_Store
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
# BUCK
buck-out/
\.buckd/
*.keystore
.vscode
dist/
*package-lock.json
================================================
FILE: samples/RXPTest/README.md
================================================
# RXPTest
This app provides tests for all of the functionality exposed by ReactXP.
### Caveat: Development Dependencies on ReactXP
*If/when RXPTest has development dependencies on (as yet) unpublished changes to ReactXP, a successfull build may require following the [Testing your Change](https://github.com/Microsoft/reactxp/blob/master/CONTRIBUTING.md#testing-your-change) instructions to copy the appropriate `dist` folder structure to RXPTest. It may also require appropriate types being specified in "devDependencies" in RXPTest's package.json eg `"@types/react": "16.7.20"`*
### Building
- From the RXPTest directory, run `npm install`. This fetches the dependencies.
### Building RXPTest for Web
- Run `npm run web-watch`. This compiles the TypeScript code and recompiles it whenever any files are changed.
- Open `index.html` in your browser to run the test in a browser.
### Building for React Native
- Run `npm run rn-watch`. This compiles the TypeScript code and recompiles it whenever any files are changed.
- In another command prompt run `npm start`. This starts the React Native Packager.
- For iOS or Android: Use Xcode or Android Studio to build and deploy the native app code just like you would with any other React Native project.
- For Windows: Open `windows\RXPTest.sln` in Visual Studio 2017. Build and run the app for x64 or x86.
================================================
FILE: samples/RXPTest/android/app/BUCK
================================================
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
lib_deps = []
create_aar_targets(glob(["libs/*.aar"]))
create_jar_targets(glob(["libs/*.jar"]))
android_library(
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = "build_config",
package = "com.rxptest",
)
android_resource(
name = "res",
package = "com.rxptest",
res = "src/main/res",
)
android_binary(
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)
================================================
FILE: samples/RXPTest/android/app/build.gradle
================================================
apply plugin: "com.android.application"
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
entryFile: "index.js"
]
apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId "com.rxptest"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ["armeabi-v7a":1, "x86":2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
}
dependencies {
implementation project(':@react-native-community_netinfo')
implementation project(':react-native-webview')
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation "com.facebook.react:react-native:+" // From node_modules
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}
================================================
FILE: samples/RXPTest/android/app/build_defs.bzl
================================================
"""Helper definitions to glob .aar and .jar targets"""
def create_aar_targets(aarfiles):
for aarfile in aarfiles:
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
lib_deps.append(":" + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
def create_jar_targets(jarfiles):
for jarfile in jarfiles:
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
lib_deps.append(":" + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)
================================================
FILE: samples/RXPTest/android/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Disabling obfuscation is useful if you collect stack traces from production crashes
# (unless you are using a system that supports de-obfuscate the stack traces).
-dontobfuscate
# React Native
# Keep our interfaces so they can be used by other ProGuard rules.
# See http://sourceforge.net/p/proguard/bugs/466/
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
# Do not strip any method/class that is annotated with @DoNotStrip
-keep @com.facebook.proguard.annotations.DoNotStrip class *
-keep @com.facebook.common.internal.DoNotStrip class *
-keepclassmembers class * {
@com.facebook.proguard.annotations.DoNotStrip *;
@com.facebook.common.internal.DoNotStrip *;
}
-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
void set*(***);
*** get*();
}
-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
-keep class * extends com.facebook.react.bridge.NativeModule { *; }
-keepclassmembers,includedescriptorclasses class * { native ; }
-keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; }
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; }
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; }
-dontwarn com.facebook.react.**
# okhttp
-keepattributes Signature
-keepattributes *Annotation*
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
# okio
-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**
================================================
FILE: samples/RXPTest/android/app/src/debug/AndroidManifest.xml
================================================
================================================
FILE: samples/RXPTest/android/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: samples/RXPTest/android/app/src/main/java/com/rxphelloworld/MainActivity.java
================================================
package com.rxptest;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "RXApp";
}
}
================================================
FILE: samples/RXPTest/android/app/src/main/java/com/rxphelloworld/MainApplication.java
================================================
package com.rxptest;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import com.reactnativecommunity.netinfo.NetInfoPackage;
import com.reactnativecommunity.webview.RNCWebViewPackage;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List getPackages() {
return Arrays.asList(
new MainReactPackage(),
new NetInfoPackage(),
new RNCWebViewPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index.android";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
================================================
FILE: samples/RXPTest/android/app/src/main/res/values/strings.xml
================================================
RXPTest
================================================
FILE: samples/RXPTest/android/app/src/main/res/values/styles.xml
================================================
================================================
FILE: samples/RXPTest/android/build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "28.0.3"
minSdkVersion = 16
compileSdkVersion = 28
targetSdkVersion = 28
supportLibVersion = "28.0.0"
}
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
google()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
}
================================================
FILE: samples/RXPTest/android/gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: samples/RXPTest/android/gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useDeprecatedNdk=true
================================================
FILE: samples/RXPTest/android/gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: samples/RXPTest/android/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: samples/RXPTest/android/keystores/BUCK
================================================
keystore(
name = "debug",
properties = "debug.keystore.properties",
store = "debug.keystore",
visibility = [
"PUBLIC",
],
)
================================================
FILE: samples/RXPTest/android/keystores/debug.keystore.properties
================================================
key.store=debug.keystore
key.alias=androiddebugkey
key.store.password=android
key.alias.password=android
================================================
FILE: samples/RXPTest/android/settings.gradle
================================================
rootProject.name = 'RXPTest'
include ':@react-native-community_netinfo'
project(':@react-native-community_netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
include ':react-native-webview'
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
include ':app'
================================================
FILE: samples/RXPTest/index.android.js
================================================
require('./dist/index');
================================================
FILE: samples/RXPTest/index.html
================================================
================================================
FILE: samples/RXPTest/index.ios.js
================================================
require('./dist/index');
================================================
FILE: samples/RXPTest/index.windows.js
================================================
require('./dist/index');
================================================
FILE: samples/RXPTest/ios/RXPTest/AppDelegate.h
================================================
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import
#import
@interface AppDelegate : UIResponder
@property (nonatomic, strong) UIWindow *window;
@end
================================================
FILE: samples/RXPTest/ios/RXPTest/AppDelegate.m
================================================
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "AppDelegate.h"
#import
#import
#import
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"RXApp"
initialProperties:nil];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
@end
================================================
FILE: samples/RXPTest/ios/RXPTest/Base.lproj/LaunchScreen.xib
================================================
================================================
FILE: samples/RXPTest/ios/RXPTest/Images.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: samples/RXPTest/ios/RXPTest/Info.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleDisplayName
RXPTest
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
APPL
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1
LSRequiresIPhoneOS
NSAppTransportSecurity
NSExceptionDomains
localhost
NSExceptionAllowsInsecureHTTPLoads
NSLocationWhenInUseUsageDescription
UILaunchStoryboardName
LaunchScreen
UIRequiredDeviceCapabilities
armv7
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UIViewControllerBasedStatusBarAppearance
================================================
FILE: samples/RXPTest/ios/RXPTest/main.m
================================================
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
================================================
FILE: samples/RXPTest/ios/RXPTest.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
6D5B77E9E6F648EB8ADF799E /* libRNCNetInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D84626BC41A4406DAC31CDCF /* libRNCNetInfo.a */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
D2278308222FDC55009150CC /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2278307222FDC55009150CC /* JavaScriptCore.framework */; };
DC59425A11A24663A46F6493 /* libRNCWebView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C292C692AED4DC2B496B1DB /* libRNCWebView.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTActionSheet;
};
00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTGeolocation;
};
00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
remoteInfo = RCTImage;
};
00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B511DB1A9E6C8500147676;
remoteInfo = RCTNetwork;
};
00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 832C81801AAF6DEF007FA2F7;
remoteInfo = RCTVibration;
};
07AE0CFA1F74268D0035D6C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7;
remoteInfo = "third-party";
};
07AE0CFC1F74268D0035D6C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D383D3C1EBD27B6005632C8;
remoteInfo = "third-party-tvOS";
};
07AE0CFE1F74268D0035D6C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 139D7E881E25C6D100323FB7;
remoteInfo = "double-conversion";
};
07AE0D001F74268D0035D6C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D383D621EBD27B9005632C8;
remoteInfo = "double-conversion-tvOS";
};
07C03E341EC4EB8600E549B0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28131D9B038B00D4039D;
remoteInfo = "React-tvOS";
};
07C03E361EC4EB8600E549B0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D3C06751DE3340C00C268FA;
remoteInfo = "yoga-tvOS";
};
07C03E381EC4EB8600E549B0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4;
remoteInfo = "cxxreact-tvOS";
};
0CFA0AE922987EC4007F740A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = EDEBC6D6214B3E7000DD5AC8;
remoteInfo = jsi;
};
0CFA0AEB22987EC4007F740A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = EDEBC73B214B45A300DD5AC8;
remoteInfo = jsiexecutor;
};
0CFA0AED22987EC4007F740A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = ED296FB6214C9A0900B7C4FE;
remoteInfo = "jsi-tvOS";
};
0CFA0AEF22987EC4007F740A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = ED296FEE214C9CF800B7C4FE;
remoteInfo = "jsiexecutor-tvOS";
};
0CFA0AF722987EC4007F740A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 6ACD0BF3CAC840B79C2544A5 /* RNCNetInfo.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RNCNetInfo;
};
0CFA0AF922987EC4007F740A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 6ACD0BF3CAC840B79C2544A5 /* RNCNetInfo.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = B5027B1B2237B30F00F1AABA;
remoteInfo = "RNCNetInfo-tvOS";
};
0CFA0AFC22987EC4007F740A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 16D0CEE186654808941ED16F /* RNCWebView.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RNCWebView;
};
139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTSettings;
};
139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3C86DF461ADF2C930047B81A;
remoteInfo = RCTWebSocket;
};
146834031AC3E56700842450 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
remoteInfo = React;
};
3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A283A1D9B042B00D4039D;
remoteInfo = "RCTImage-tvOS";
};
3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28471D9B043800D4039D;
remoteInfo = "RCTLinking-tvOS";
};
3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28541D9B044C00D4039D;
remoteInfo = "RCTNetwork-tvOS";
};
3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28611D9B046600D4039D;
remoteInfo = "RCTSettings-tvOS";
};
3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A287B1D9B048500D4039D;
remoteInfo = "RCTText-tvOS";
};
3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28881D9B049200D4039D;
remoteInfo = "RCTWebSocket-tvOS";
};
3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D3C059A1DE3340900C268FA;
remoteInfo = yoga;
};
3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4;
remoteInfo = cxxreact;
};
5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTAnimation;
};
5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2D2A28201D9B03D100D4039D;
remoteInfo = "RCTAnimation-tvOS";
};
78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTLinking;
};
832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 58B5119B1A9E6C1200147676;
remoteInfo = RCTText;
};
8CCE8B8F2182ACCF00855E24 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = EBF21BDC1FC498900052F4D5;
remoteInfo = jsinspector;
};
8CCE8B912182ACCF00855E24 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = EBF21BFA1FC4989A0052F4D5;
remoteInfo = "jsinspector-tvOS";
};
D4A8C3B020247A9D008BE855 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3DBE0D001F3B181A0099AA32;
remoteInfo = fishhook;
};
D4A8C3B220247A9D008BE855 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32;
remoteInfo = "fishhook-tvOS";
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; };
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; };
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; };
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; };
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; };
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; };
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; };
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; };
13B07F961A680F5B00A75B9A /* RXPTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RXPTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = RXPTest/AppDelegate.h; sourceTree = ""; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = RXPTest/AppDelegate.m; sourceTree = ""; };
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = RXPTest/Images.xcassets; sourceTree = ""; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RXPTest/Info.plist; sourceTree = "