├── .bundle
└── config
├── .editorconfig
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── .npmrc
├── .prettierrc.js
├── .vscode
├── extensions.json
└── settings.json
├── App.tsx
├── Gemfile
├── Gemfile.lock
├── README.md
├── android
├── app
│ ├── build.gradle
│ ├── debug.keystore
│ ├── proguard-rules.pro
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── myuidemo
│ │ │ ├── MainActivity.java
│ │ │ ├── MainApplication.java
│ │ │ └── MyUiPackage.java
│ │ └── res
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── strings.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
├── babel.config.js
├── demo
├── activity-indicator
│ └── ActivityIndicatorScreen.tsx
├── assets
│ ├── adaptive-icon.png
│ ├── album-art-1.jpg
│ ├── album-art-2.jpg
│ ├── album-art-3.jpg
│ ├── album-art-4.jpg
│ ├── album-art-5.jpg
│ ├── album-art-6.jpg
│ ├── album-art-7.jpg
│ ├── album-art-8.jpg
│ ├── anabelle.jpg
│ ├── arrow.png
│ ├── arrowOther.png
│ ├── avatar-1.png
│ ├── avatar-2.png
│ ├── book.jpg
│ ├── candice.jpg
│ ├── checkboxOff.png
│ ├── checkboxOn.png
│ ├── comment.png
│ ├── cover.webp
│ ├── egg.jpg
│ ├── favicon.png
│ ├── icon.png
│ ├── indicator@2x.png
│ ├── indicator@3x.png
│ ├── like.png
│ ├── mohamad.jpg
│ ├── mubariz.jpg
│ ├── retweet.pdf
│ ├── retweet.png
│ ├── retweet.svg
│ ├── send.png
│ ├── share.png
│ ├── splash.png
│ ├── tamara.jpg
│ ├── trilo-3.json
│ ├── trilo-4.json
│ ├── trilo-5.json
│ ├── window_closed@2x.png
│ └── window_closed@3x.png
├── bottom-sheet
│ ├── BottomModal.tsx
│ ├── BottomSheetBackdropShadow
│ │ └── index.tsx
│ ├── BottomSheetFlashList
│ │ └── index.tsx
│ ├── BottomSheetPagerView
│ │ └── index.tsx
│ ├── BottomSheetWithoutScrollView
│ │ ├── Button.tsx
│ │ └── index.tsx
│ ├── Home.tsx
│ └── index.ts
├── components
│ ├── CondeInput.tsx
│ ├── FlatListPage.tsx
│ ├── LoremIpsum.tsx
│ ├── Messages
│ │ ├── Avatar.tsx
│ │ ├── ImageMessage.tsx
│ │ ├── MessageItem.tsx
│ │ ├── Messages.tsx
│ │ ├── MessagesFlatList.tsx
│ │ ├── TextBubble.tsx
│ │ ├── TextInputBar.tsx
│ │ ├── data
│ │ │ └── messages.ts
│ │ ├── index.ts
│ │ ├── models
│ │ │ ├── Message.ts
│ │ │ └── MessageType.ts
│ │ └── userName.tsx
│ ├── Modal.tsx
│ ├── ModalButton.tsx
│ ├── ModalToolbar.tsx
│ ├── PrimaryButton.tsx
│ ├── ScrollViewPage.tsx
│ ├── TabBar
│ │ ├── ScrollBar.tsx
│ │ ├── TabBar.tsx
│ │ ├── TabBarIndicator.tsx
│ │ ├── TabBarItem.tsx
│ │ └── index.tsx
│ ├── WebViewPage
│ │ ├── index.tsx
│ │ └── loading.json
│ ├── contacts
│ │ ├── ContactCell.tsx
│ │ ├── ContactDivider.tsx
│ │ ├── ContactHeader.tsx
│ │ ├── ContactSectionHeader.tsx
│ │ ├── Contacts.tsx
│ │ ├── ContactsSectionList.tsx
│ │ ├── data
│ │ │ └── contacts.ts
│ │ └── models
│ │ │ └── Contact.ts
│ ├── tabview
│ │ ├── Shared
│ │ │ ├── Albums.tsx
│ │ │ ├── Article.tsx
│ │ │ ├── Chat.tsx
│ │ │ ├── Contacts.tsx
│ │ │ └── Profile.tsx
│ │ └── index.tsx
│ ├── twitter
│ │ ├── CustomCellRendererComponent.tsx
│ │ ├── TweetCell.tsx
│ │ ├── TweetContent.tsx
│ │ ├── Twitter.tsx
│ │ ├── TwitterFlatList.tsx
│ │ ├── data
│ │ │ └── tweets.ts
│ │ ├── index.ts
│ │ └── models
│ │ │ ├── Author.ts
│ │ │ └── Tweet.ts
│ └── usePagerView.tsx
├── image-crop
│ ├── ImageCropDemo.tsx
│ ├── ImageCropPage.tsx
│ ├── ImageCropResultPage.tsx
│ └── index.ts
├── keyboard-insets
│ ├── Home.tsx
│ ├── KeyboardAdvoiding
│ │ └── index.tsx
│ ├── KeyboardChat
│ │ ├── Message
│ │ │ ├── data.ts
│ │ │ ├── index.tsx
│ │ │ ├── styles.ts
│ │ │ └── types.ts
│ │ ├── driver
│ │ │ ├── Driver.ts
│ │ │ ├── KeyboardDriver.ts
│ │ │ └── ViewDriver.ts
│ │ ├── icon
│ │ │ ├── emoji@2x.png
│ │ │ ├── emoji@3x.png
│ │ │ ├── keyboard@2x.png
│ │ │ ├── keyboard@3x.png
│ │ │ ├── plus@2x.png
│ │ │ └── plus@3x.png
│ │ ├── index.tsx
│ │ └── styles.ts
│ ├── KeyboardChatReanimated
│ │ ├── Message
│ │ │ ├── data.ts
│ │ │ ├── index.tsx
│ │ │ ├── styles.ts
│ │ │ └── types.ts
│ │ ├── RekeyboardInsetsView.tsx
│ │ ├── icon
│ │ │ ├── emoji@2x.png
│ │ │ ├── emoji@3x.png
│ │ │ ├── keyboard@2x.png
│ │ │ ├── keyboard@3x.png
│ │ │ ├── plus@2x.png
│ │ │ └── plus@3x.png
│ │ ├── index.tsx
│ │ ├── styles.ts
│ │ └── useRekeyboard.ts
│ ├── ModalTextInput
│ │ └── index.tsx
│ └── index.ts
├── nested-scroll
│ ├── Home.tsx
│ ├── NestedScrollFlatList
│ │ └── index.tsx
│ ├── NestedScrollPagerViewStickyHeader
│ │ └── index.tsx
│ ├── NestedScrollParallaxHeader
│ │ ├── AnimatedNavbar.tsx
│ │ ├── ParallaxHeader.tsx
│ │ ├── components
│ │ │ ├── HeaderComponent.tsx
│ │ │ ├── HeaderNavBar.tsx
│ │ │ ├── RoundButton.tsx
│ │ │ └── TopNavBar.tsx
│ │ ├── hooks
│ │ │ ├── useAnimatedNavbar.ts
│ │ │ └── useAnimatedScrollView.ts
│ │ ├── icons
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.tsx
│ │ │ └── Share.tsx
│ │ └── index.tsx
│ ├── NestedScrollTabView
│ │ └── index.tsx
│ └── index.ts
├── overlay
│ ├── Alert
│ │ ├── Button.tsx
│ │ ├── Message.tsx
│ │ ├── Title.tsx
│ │ └── index.tsx
│ ├── AlertScreen.tsx
│ ├── Home.tsx
│ ├── Hoverball
│ │ ├── Ball.ios.tsx
│ │ ├── Ball.tsx
│ │ ├── Menu.tsx
│ │ ├── index.tsx
│ │ └── types.ts
│ ├── HoverballScreen.tsx
│ ├── Toast
│ │ └── index.tsx
│ ├── ToastScreen.tsx
│ └── index.ts
├── pull-to-refresh
│ ├── Home.tsx
│ ├── PullRefreshFlatList
│ │ └── index.tsx
│ ├── PullRefreshFlatListNestedScroll
│ │ └── index.tsx
│ ├── PullRefreshNestedScrollPagerView
│ │ └── index.tsx
│ ├── PullRefreshPagerView
│ │ └── index.tsx
│ ├── PullRefreshPagerViewNestedScroll
│ │ └── index.tsx
│ ├── PullRefreshScrollView
│ │ └── index.tsx
│ ├── PullRefreshWebView
│ │ └── index.tsx
│ ├── PullRefreshWithoutScrollView
│ │ ├── ExpandingDot.tsx
│ │ ├── Page.tsx
│ │ ├── index.tsx
│ │ └── usePagerView.ts
│ ├── PullToRefresh
│ │ ├── LottiePullToRefreshFooter.tsx
│ │ ├── LottiePullToRefreshHeader.tsx
│ │ ├── SpinnerPullToRefreshHeader.tsx
│ │ ├── car.json
│ │ ├── google-loading.json
│ │ ├── index.ts
│ │ └── square-loading.json
│ └── index.ts
└── wheel-picker
│ ├── CityPicker.tsx
│ ├── TimePicker.tsx
│ ├── WheelPickerDemo.tsx
│ ├── index.ts
│ └── pc-code.json
├── index.ts
├── ios
├── .xcode.env
├── MyUiDemo.xcodeproj
│ ├── project.pbxproj
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── MyUiDemo.xcscheme
├── MyUiDemo.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── MyUiDemo
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Base.lproj
│ │ └── LaunchScreen.storyboard
│ ├── Info.plist
│ ├── PrivacyInfo.xcprivacy
│ └── main.m
├── Podfile
└── Podfile.lock
├── jest.config.js
├── metro.config.js
├── package.json
├── packages
├── activity-indicator
│ ├── LICENSE
│ ├── README.md
│ ├── android
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── reactnative
│ │ │ └── activityindicator
│ │ │ ├── ActivityIndicator.java
│ │ │ ├── ActivityIndicatorManager.java
│ │ │ └── ActivityIndicatorPackage.java
│ ├── docs
│ │ └── assets
│ │ │ └── activity.png
│ ├── package.json
│ ├── react-native.config.js
│ ├── src
│ │ ├── ActivityIndicatorAndroid.ts
│ │ └── index.tsx
│ └── tsconfig.json
├── bottom-sheet
│ ├── LICENSE
│ ├── README.md
│ ├── RNBottomSheet.podspec
│ ├── android
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── reactnative
│ │ │ └── bottomsheet
│ │ │ ├── BottomSheet.java
│ │ │ ├── BottomSheetManager.java
│ │ │ ├── BottomSheetPackage.java
│ │ │ ├── BottomSheetState.java
│ │ │ ├── OffsetChangedEvent.java
│ │ │ └── StateChangedEvent.java
│ ├── docs
│ │ └── assets
│ │ │ ├── pagerview.gif
│ │ │ ├── scrollview.gif
│ │ │ └── struct.png
│ ├── ios
│ │ ├── BottomSheet.xcodeproj
│ │ │ └── project.pbxproj
│ │ └── BottomSheet
│ │ │ ├── RNBottomSheet.h
│ │ │ ├── RNBottomSheet.m
│ │ │ ├── RNBottomSheetManager.h
│ │ │ ├── RNBottomSheetManager.m
│ │ │ ├── RNBottomSheetOffsetChangedEvent.h
│ │ │ ├── RNBottomSheetOffsetChangedEvent.m
│ │ │ ├── RNBottomSheetState.h
│ │ │ ├── RNBottomSheetState.m
│ │ │ ├── RNBottomSheetStateChangedEvent.h
│ │ │ └── RNBottomSheetStateChangedEvent.m
│ ├── package.json
│ ├── react-native.config.js
│ ├── src
│ │ ├── index.tsx
│ │ └── splitLayoutProps.ts
│ └── tsconfig.json
├── image-crop
│ ├── LICENSE
│ ├── README.md
│ ├── RNImageCrop.podspec
│ ├── android
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── reactnative
│ │ │ │ └── imagecrop
│ │ │ │ ├── ImageCropPackage.java
│ │ │ │ ├── ObjectRect.java
│ │ │ │ ├── RNImageCropView.java
│ │ │ │ └── RNImageCropViewManager.java
│ │ │ └── res
│ │ │ └── layout
│ │ │ └── rn_crop_view.xml
│ ├── ios
│ │ ├── ImageCrop.xcodeproj
│ │ │ └── project.pbxproj
│ │ └── ImageCrop
│ │ │ ├── RNImageCrop.h
│ │ │ ├── RNImageCrop.m
│ │ │ ├── RNImageCropManager.h
│ │ │ └── RNImageCropManager.m
│ ├── package.json
│ ├── react-native.config.js
│ ├── src
│ │ ├── ImageCropView.tsx
│ │ ├── ImageCropViewRef.ts
│ │ ├── index.ts
│ │ └── typings.ts
│ └── tsconfig.json
├── keyboard-insets
│ ├── LICENSE
│ ├── README.md
│ ├── RNKeyboardInsets.podspec
│ ├── android
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── reactnative
│ │ │ └── keyboardinsets
│ │ │ ├── EdgeInsets.java
│ │ │ ├── KeyboardAutoHandler.java
│ │ │ ├── KeyboardInsetsCallback.java
│ │ │ ├── KeyboardInsetsModule.java
│ │ │ ├── KeyboardInsetsPackage.java
│ │ │ ├── KeyboardInsetsView.java
│ │ │ ├── KeyboardInsetsViewManager.java
│ │ │ ├── KeyboardManualHandler.java
│ │ │ ├── KeyboardPositionChangedEvent.java
│ │ │ ├── KeyboardStatusChangedEvent.java
│ │ │ └── SystemUI.java
│ ├── docs
│ │ └── assets
│ │ │ ├── avoiding.gif
│ │ │ └── chat.gif
│ ├── ios
│ │ ├── KeyboardInsets.xcodeproj
│ │ │ └── project.pbxproj
│ │ └── KeyboardInsets
│ │ │ ├── RNKeyboardAutoHandler.h
│ │ │ ├── RNKeyboardAutoHandler.m
│ │ │ ├── RNKeyboardInsetsModule.h
│ │ │ ├── RNKeyboardInsetsModule.m
│ │ │ ├── RNKeyboardInsetsView.h
│ │ │ ├── RNKeyboardInsetsView.m
│ │ │ ├── RNKeyboardInsetsViewManager.h
│ │ │ ├── RNKeyboardInsetsViewManager.m
│ │ │ ├── RNKeyboardManualHandler.h
│ │ │ ├── RNKeyboardManualHandler.m
│ │ │ ├── RNKeyboardPositionChangedEvent.h
│ │ │ ├── RNKeyboardPositionChangedEvent.m
│ │ │ ├── RNKeyboardStatusChangedEvent.h
│ │ │ └── RNKeyboardStatusChangedEvent.m
│ ├── package.json
│ ├── react-native.config.js
│ ├── src
│ │ ├── KeyboardInsetsView.tsx
│ │ ├── hook.ts
│ │ ├── index.ts
│ │ └── native.ts
│ └── tsconfig.json
├── modal-edge-to-edge
│ ├── LICENSE
│ ├── README.md
│ ├── android
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── reactnative
│ │ │ └── modalx
│ │ │ ├── EdgeToEdgeModule.java
│ │ │ ├── EdgeToEdgePackage.java
│ │ │ ├── ModalHostManager.java
│ │ │ ├── ModalHostView.java
│ │ │ ├── SystemUI.java
│ │ │ └── SystemUI30.java
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── nested-scroll-webview
│ ├── LICENSE
│ ├── README.md
│ ├── android
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ ├── reactnative
│ │ │ └── nestedscrollwebview
│ │ │ │ └── NestedScrollWebViewPackage.java
│ │ │ └── reactnativecommunity
│ │ │ └── webview
│ │ │ ├── RNCNestedScrollWebView.java
│ │ │ └── RNCNestedScrollWebViewManager.java
│ ├── package.json
│ ├── react-native.config.js
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── nested-scroll
│ ├── LICENSE
│ ├── README.md
│ ├── RNNestedScrollView.podspec
│ ├── android
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── reactnative
│ │ │ └── nestedscroll
│ │ │ ├── NestedScrollFlingHelper.java
│ │ │ ├── NestedScrollView.java
│ │ │ ├── NestedScrollViewHeader.java
│ │ │ ├── NestedScrollViewHeaderManager.java
│ │ │ ├── NestedScrollViewLocalData.java
│ │ │ ├── NestedScrollViewManager.java
│ │ │ ├── NestedScrollViewPackage.java
│ │ │ ├── NestedScrollViewShadowNode.java
│ │ │ └── NestedViewHeaderScrollEvent.java
│ ├── docs
│ │ └── assets
│ │ │ ├── parallax.gif
│ │ │ ├── sticky.gif
│ │ │ └── struct.png
│ ├── ios
│ │ ├── NestedScrollView.xcodeproj
│ │ │ └── project.pbxproj
│ │ └── NestedScrollView
│ │ │ ├── RNNestedScrollEvent.h
│ │ │ ├── RNNestedScrollEvent.m
│ │ │ ├── RNNestedScrollShadowView.h
│ │ │ ├── RNNestedScrollShadowView.m
│ │ │ ├── RNNestedScrollView.h
│ │ │ ├── RNNestedScrollView.m
│ │ │ ├── RNNestedScrollViewHeader.h
│ │ │ ├── RNNestedScrollViewHeader.m
│ │ │ ├── RNNestedScrollViewHeaderManager.h
│ │ │ ├── RNNestedScrollViewHeaderManager.m
│ │ │ ├── RNNestedScrollViewLocalData.h
│ │ │ ├── RNNestedScrollViewLocalData.m
│ │ │ ├── RNNestedScrollViewManager.h
│ │ │ └── RNNestedScrollViewManager.m
│ ├── package.json
│ ├── react-native.config.js
│ ├── src
│ │ ├── NestedScrollViewHeader
│ │ │ └── index.tsx
│ │ └── index.tsx
│ └── tsconfig.json
├── overlay
│ ├── LICENSE
│ ├── README.md
│ ├── RNOverlay.podspec
│ ├── android
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── reactnative
│ │ │ └── overlay
│ │ │ ├── Overlay.java
│ │ │ ├── OverlayModule.java
│ │ │ ├── OverlayPackage.java
│ │ │ └── OverlayRootView.java
│ ├── ios
│ │ ├── Overlay.xcodeproj
│ │ │ └── project.pbxproj
│ │ └── Overlay
│ │ │ ├── RNOverlay.h
│ │ │ ├── RNOverlay.m
│ │ │ ├── RNOverlayModule.h
│ │ │ └── RNOverlayModule.m
│ ├── package.json
│ ├── react-native.config.js
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── pull-to-refresh
│ ├── LICENSE
│ ├── README.md
│ ├── RNPullToRefresh.podspec
│ ├── android
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── com
│ │ │ └── reactnative
│ │ │ └── pulltorefresh
│ │ │ ├── OnRefreshChangeListener.java
│ │ │ ├── PullToRefresh.java
│ │ │ ├── PullToRefreshFooter.java
│ │ │ ├── PullToRefreshFooterLocalData.java
│ │ │ ├── PullToRefreshFooterManager.java
│ │ │ ├── PullToRefreshFooterShadowNode.java
│ │ │ ├── PullToRefreshHeader.java
│ │ │ ├── PullToRefreshHeaderLocalData.java
│ │ │ ├── PullToRefreshHeaderManager.java
│ │ │ ├── PullToRefreshHeaderShadowNode.java
│ │ │ ├── PullToRefreshManager.java
│ │ │ ├── PullToRefreshPackage.java
│ │ │ ├── PullToRefreshState.java
│ │ │ └── event
│ │ │ ├── OffsetChangedEvent.java
│ │ │ ├── RefreshEvent.java
│ │ │ └── StateChangedEvent.java
│ ├── docs
│ │ └── assets
│ │ │ ├── separated.gif
│ │ │ └── shared.gif
│ ├── ios
│ │ ├── PullToRefresh.xcodeproj
│ │ │ └── project.pbxproj
│ │ └── PullToRefresh
│ │ │ ├── Event
│ │ │ ├── RNRefreshOffsetChangedEvent.h
│ │ │ ├── RNRefreshOffsetChangedEvent.m
│ │ │ ├── RNRefreshStateChangedEvent.h
│ │ │ ├── RNRefreshStateChangedEvent.m
│ │ │ ├── RNRefreshingEvent.h
│ │ │ └── RNRefreshingEvent.m
│ │ │ ├── Footer
│ │ │ ├── RNRefreshFooter.h
│ │ │ ├── RNRefreshFooter.m
│ │ │ ├── RNRefreshFooterLocalData.h
│ │ │ ├── RNRefreshFooterLocalData.m
│ │ │ ├── RNRefreshFooterManager.h
│ │ │ ├── RNRefreshFooterManager.m
│ │ │ ├── RNRefreshFooterShadowView.h
│ │ │ └── RNRefreshFooterShadowView.m
│ │ │ ├── Header
│ │ │ ├── RNRefreshHeader.h
│ │ │ ├── RNRefreshHeader.m
│ │ │ ├── RNRefreshHeaderLocalData.h
│ │ │ ├── RNRefreshHeaderLocalData.m
│ │ │ ├── RNRefreshHeaderManager.h
│ │ │ ├── RNRefreshHeaderManager.m
│ │ │ ├── RNRefreshHeaderShadowView.h
│ │ │ └── RNRefreshHeaderShadowView.m
│ │ │ ├── RNPullToRefresh.h
│ │ │ ├── RNPullToRefresh.m
│ │ │ ├── RNPullToRefreshManager.h
│ │ │ ├── RNPullToRefreshManager.m
│ │ │ ├── RNRefreshState.h
│ │ │ └── RNRefreshState.m
│ ├── package.json
│ ├── react-native.config.js
│ ├── src
│ │ ├── Footer
│ │ │ ├── DefaultPullToRefreshFooter.tsx
│ │ │ ├── index.ts
│ │ │ └── native.tsx
│ │ ├── Header
│ │ │ ├── DefaultPullToRefreshHeader.tsx
│ │ │ ├── index.ts
│ │ │ └── native.tsx
│ │ ├── PullToRefresh
│ │ │ ├── index.tsx
│ │ │ └── native.tsx
│ │ ├── RefreshControl.tsx
│ │ ├── index.ts
│ │ └── types.ts
│ └── tsconfig.json
└── wheel-picker
│ ├── LICENSE
│ ├── README.md
│ ├── RNWheelPicker.podspec
│ ├── android
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── reactnative
│ │ └── wheelpicker
│ │ ├── PickerView.java
│ │ ├── PickerViewManager.java
│ │ ├── WheelPickerPackage.java
│ │ └── wheel
│ │ ├── IPickerViewData.java
│ │ ├── InertiaTimerTask.java
│ │ ├── LoopViewGestureListener.java
│ │ ├── MessageHandler.java
│ │ ├── OnItemSelectedListener.java
│ │ ├── SmoothScrollTimerTask.java
│ │ ├── WheelAdapter.java
│ │ └── WheelView.java
│ ├── docs
│ └── assets
│ │ └── wheelpicker.png
│ ├── ios
│ ├── WheelPicker.xcodeproj
│ │ └── project.pbxproj
│ └── WheelPicker
│ │ ├── RNWheelPicker.h
│ │ ├── RNWheelPicker.m
│ │ ├── RNWheelPickerManager.h
│ │ └── RNWheelPickerManager.m
│ ├── package.json
│ ├── react-native.config.js
│ ├── src
│ ├── WheelPicker.tsx
│ ├── index.tsx
│ └── typing.ts
│ └── tsconfig.json
├── patches
└── react-native-pager-view+6.7.1.patch
├── tsconfig.json
└── yarn.lock
/.bundle/config:
--------------------------------------------------------------------------------
1 | BUNDLE_PATH: "vendor/bundle"
2 | BUNDLE_FORCE_RUBY_PLATFORM: 1
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = tab
6 | indent_size = 4
7 | max_line_length = 100
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.yml]
13 | indent_style = space
14 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: '@react-native',
4 | };
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/.npmrc
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | arrowParens: 'avoid',
3 | bracketSameLine: true,
4 | bracketSpacing: false,
5 | singleQuote: true,
6 | trailingComma: 'all',
7 | };
8 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "esbenp.prettier-vscode",
4 | "dbaeumer.vscode-eslint",
5 | "eamodio.gitlens",
6 | "editorconfig.editorconfig"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
3 | "editor.defaultFormatter": "esbenp.prettier-vscode",
4 | "editor.formatOnSave": true,
5 | "editor.codeActionsOnSave": {
6 | "source.fixAll.eslint": "explicit"
7 | },
8 | "typescript.tsdk": "./node_modules/typescript/lib",
9 | "typescript.preferences.includePackageJsonAutoImports": "off"
10 | }
11 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
4 | ruby ">= 2.6.10"
5 |
6 | # Exclude problematic versions of cocoapods and activesupport that causes build failures.
7 | gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
8 | gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
9 | gem 'xcodeproj', '< 1.26.0'
10 | gem 'concurrent-ruby', '< 1.3.4'
11 |
12 | # Ruby 3.4.0 has removed some libraries from the standard library.
13 | gem 'bigdecimal'
14 | gem 'logger'
15 | gem 'benchmark'
16 | gem 'mutex_m'
17 |
--------------------------------------------------------------------------------
/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/app/debug.keystore
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/example/myuidemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.myuidemo;
2 |
3 | import com.reactnative.hybridnavigation.ReactAppCompatActivity;
4 |
5 | public class MainActivity extends ReactAppCompatActivity {
6 |
7 | }
--------------------------------------------------------------------------------
/android/app/src/main/java/com/example/myuidemo/MyUiPackage.java:
--------------------------------------------------------------------------------
1 | package com.example.myuidemo;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.facebook.react.ReactPackage;
6 | import com.facebook.react.bridge.NativeModule;
7 | import com.facebook.react.bridge.ReactApplicationContext;
8 | import com.facebook.react.uimanager.ViewManager;
9 | import com.reactnativecommunity.webview.RNCNestedScrollWebViewManager;
10 |
11 | import java.util.Arrays;
12 | import java.util.Collections;
13 | import java.util.List;
14 |
15 | public class MyUiPackage implements ReactPackage {
16 | @NonNull
17 | @Override
18 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
19 | return Collections.emptyList();
20 | }
21 |
22 | @NonNull
23 | @Override
24 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
25 | return Arrays.asList(
26 | new RNCNestedScrollWebViewManager()
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MyUiDemo
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | buildToolsVersion = "35.0.0"
4 | minSdkVersion = 24
5 | compileSdkVersion = 35
6 | targetSdkVersion = 35
7 | ndkVersion = "27.1.12297006"
8 | kotlinVersion = "2.0.21"
9 | }
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | dependencies {
15 | classpath("com.android.tools.build:gradle")
16 | classpath("com.facebook.react:react-native-gradle-plugin")
17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
18 | }
19 | }
20 |
21 | apply plugin: "com.facebook.react.rootproject"
22 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
2 | plugins { id("com.facebook.react.settings") }
3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
4 | rootProject.name = 'MyUiDemo'
5 | include ':app'
6 | includeBuild('../node_modules/@react-native/gradle-plugin')
7 |
8 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:@react-native/babel-preset'],
3 | plugins: [
4 | 'react-native-reanimated/plugin',
5 | [
6 | 'module-resolver',
7 | {
8 | root: './demo',
9 | cwd: 'babelrc',
10 | extensions: ['.ts', '.tsx', '.js', '.jsx'],
11 | alias: {
12 | assets: './demo/assets',
13 | },
14 | },
15 | ],
16 | ],
17 | };
18 |
--------------------------------------------------------------------------------
/demo/activity-indicator/ActivityIndicatorScreen.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {View, StyleSheet} from 'react-native';
3 |
4 | import ActivityIndicator from '@sdcx/activity-indicator';
5 | import {withNavigationItem} from 'hybrid-navigation';
6 |
7 | function ActivityIndicatorScreen() {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 | );
16 | }
17 |
18 | export default withNavigationItem({
19 | titleItem: {
20 | title: '菊花 Loading',
21 | },
22 | })(ActivityIndicatorScreen);
23 |
24 | const styles = StyleSheet.create({
25 | container: {
26 | flex: 1,
27 | flexDirection: 'row',
28 | justifyContent: 'space-around',
29 | alignItems: 'center',
30 | paddingHorizontal: 80,
31 | },
32 | });
33 |
--------------------------------------------------------------------------------
/demo/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/demo/assets/album-art-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/album-art-1.jpg
--------------------------------------------------------------------------------
/demo/assets/album-art-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/album-art-2.jpg
--------------------------------------------------------------------------------
/demo/assets/album-art-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/album-art-3.jpg
--------------------------------------------------------------------------------
/demo/assets/album-art-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/album-art-4.jpg
--------------------------------------------------------------------------------
/demo/assets/album-art-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/album-art-5.jpg
--------------------------------------------------------------------------------
/demo/assets/album-art-6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/album-art-6.jpg
--------------------------------------------------------------------------------
/demo/assets/album-art-7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/album-art-7.jpg
--------------------------------------------------------------------------------
/demo/assets/album-art-8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/album-art-8.jpg
--------------------------------------------------------------------------------
/demo/assets/anabelle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/anabelle.jpg
--------------------------------------------------------------------------------
/demo/assets/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/arrow.png
--------------------------------------------------------------------------------
/demo/assets/arrowOther.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/arrowOther.png
--------------------------------------------------------------------------------
/demo/assets/avatar-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/avatar-1.png
--------------------------------------------------------------------------------
/demo/assets/avatar-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/avatar-2.png
--------------------------------------------------------------------------------
/demo/assets/book.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/book.jpg
--------------------------------------------------------------------------------
/demo/assets/candice.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/candice.jpg
--------------------------------------------------------------------------------
/demo/assets/checkboxOff.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/checkboxOff.png
--------------------------------------------------------------------------------
/demo/assets/checkboxOn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/checkboxOn.png
--------------------------------------------------------------------------------
/demo/assets/comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/comment.png
--------------------------------------------------------------------------------
/demo/assets/cover.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/cover.webp
--------------------------------------------------------------------------------
/demo/assets/egg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/egg.jpg
--------------------------------------------------------------------------------
/demo/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/favicon.png
--------------------------------------------------------------------------------
/demo/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/icon.png
--------------------------------------------------------------------------------
/demo/assets/indicator@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/indicator@2x.png
--------------------------------------------------------------------------------
/demo/assets/indicator@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/indicator@3x.png
--------------------------------------------------------------------------------
/demo/assets/like.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/like.png
--------------------------------------------------------------------------------
/demo/assets/mohamad.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/mohamad.jpg
--------------------------------------------------------------------------------
/demo/assets/mubariz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/mubariz.jpg
--------------------------------------------------------------------------------
/demo/assets/retweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/retweet.png
--------------------------------------------------------------------------------
/demo/assets/retweet.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/demo/assets/send.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/send.png
--------------------------------------------------------------------------------
/demo/assets/share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/share.png
--------------------------------------------------------------------------------
/demo/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/splash.png
--------------------------------------------------------------------------------
/demo/assets/tamara.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/tamara.jpg
--------------------------------------------------------------------------------
/demo/assets/window_closed@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/window_closed@2x.png
--------------------------------------------------------------------------------
/demo/assets/window_closed@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/assets/window_closed@3x.png
--------------------------------------------------------------------------------
/demo/bottom-sheet/BottomSheetWithoutScrollView/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, Text, TouchableOpacity} from 'react-native';
3 |
4 | interface ButtonProps {
5 | onPress?: () => void;
6 | text: string;
7 | }
8 |
9 | export default function Button({onPress, text}: ButtonProps) {
10 | return (
11 |
12 | {text}
13 |
14 | );
15 | }
16 |
17 | const styles = StyleSheet.create({
18 | button: {
19 | marginRight: 16,
20 | justifyContent: 'center',
21 | },
22 | text: {
23 | fontSize: 16,
24 | color: '#FFFFFF',
25 | },
26 | });
27 |
--------------------------------------------------------------------------------
/demo/bottom-sheet/index.ts:
--------------------------------------------------------------------------------
1 | import Navigation from 'hybrid-navigation';
2 |
3 | import Home from './Home';
4 | import BottomSheetWithoutScrollView from './BottomSheetWithoutScrollView';
5 | import BottomSheetFlashList from './BottomSheetFlashList';
6 | import BottomSheetPagerView from './BottomSheetPagerView';
7 | import BottomSheetBackdropShadow from './BottomSheetBackdropShadow';
8 |
9 | export function registerBottomSheetComponent() {
10 | Navigation.registerComponent('BottomSheet', () => Home);
11 | Navigation.registerComponent('BottomSheetWithoutScrollView', () => BottomSheetWithoutScrollView);
12 | Navigation.registerComponent('BottomSheetFlashList', () => BottomSheetFlashList);
13 | Navigation.registerComponent('BottomSheetPagerView', () => BottomSheetPagerView);
14 | Navigation.registerComponent('BottomSheetBackdropShadow', () => BottomSheetBackdropShadow);
15 | }
16 |
--------------------------------------------------------------------------------
/demo/components/Messages/Avatar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet} from 'react-native';
3 | import FastImage from 'react-native-fast-image';
4 |
5 | interface AvatarProps {
6 | avatar?: string;
7 | }
8 |
9 | const Avatar = ({avatar}: AvatarProps) => {
10 | if (avatar === undefined) {
11 | return null;
12 | }
13 | return ;
14 | };
15 |
16 | export default Avatar;
17 |
18 | const styles = StyleSheet.create({
19 | avatar: {
20 | height: 30,
21 | width: 30,
22 | borderRadius: 15,
23 | margin: 8,
24 | marginRight: 0,
25 | marginTop: 0,
26 | flexShrink: 0,
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/demo/components/Messages/ImageMessage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {View, StyleSheet, Image} from 'react-native';
3 |
4 | interface ImageMessageProps {
5 | image: string;
6 | mine: boolean;
7 | }
8 |
9 | const ImageMessage = ({image, mine}: ImageMessageProps) => {
10 | return (
11 |
12 |
13 |
14 | );
15 | };
16 |
17 | export default ImageMessage;
18 |
19 | const styles = StyleSheet.create({
20 | otherImageWrapper: {
21 | margin: 8,
22 | },
23 | mineImageWrapper: {
24 | margin: 8,
25 | display: 'flex',
26 | flexDirection: 'row',
27 | justifyContent: 'flex-end',
28 | },
29 | image: {
30 | width: '80%',
31 | height: 200,
32 | borderRadius: 8,
33 | marginTop: 8,
34 | },
35 | });
36 |
--------------------------------------------------------------------------------
/demo/components/Messages/MessageItem.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Message from './models/Message';
4 | import ImageMessage from './ImageMessage';
5 | import TextBubble from './TextBubble';
6 | import userName from './userName';
7 | import MessageType from './models/MessageType';
8 |
9 | const MessageItem = ({item}: {item: Message}) => {
10 | const mine = item.sender === userName;
11 | switch (item.type) {
12 | case MessageType.Text:
13 | return ;
14 | case MessageType.Image:
15 | return ;
16 | }
17 | };
18 |
19 | export default MessageItem;
20 |
--------------------------------------------------------------------------------
/demo/components/Messages/index.ts:
--------------------------------------------------------------------------------
1 | export {default as Messages} from './Messages';
2 | export {default as MessagesFlatList} from './MessagesFlatList';
3 |
--------------------------------------------------------------------------------
/demo/components/Messages/models/Message.ts:
--------------------------------------------------------------------------------
1 | import MessageType from './MessageType';
2 |
3 | export interface TextMessage {
4 | id: string;
5 | text: string;
6 | avatar?: string;
7 | sender: string;
8 | type: MessageType.Text;
9 | }
10 |
11 | export interface ImageMessage {
12 | id: string;
13 | avatar?: string;
14 | type: MessageType.Image;
15 | sender: string;
16 | image: string;
17 | }
18 |
19 | type Message = ImageMessage | TextMessage;
20 |
21 | export default Message;
22 |
--------------------------------------------------------------------------------
/demo/components/Messages/models/MessageType.ts:
--------------------------------------------------------------------------------
1 | enum MessageType {
2 | Text,
3 | Image,
4 | }
5 | export default MessageType;
6 |
--------------------------------------------------------------------------------
/demo/components/Messages/userName.tsx:
--------------------------------------------------------------------------------
1 | const userName = 'John';
2 | export default userName;
3 |
--------------------------------------------------------------------------------
/demo/components/Modal.tsx:
--------------------------------------------------------------------------------
1 | import React, {PropsWithChildren} from 'react';
2 | import {useNavigator} from 'hybrid-navigation';
3 | import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native';
4 |
5 | interface ModalProps {
6 | cancelable?: boolean;
7 | style?: StyleProp;
8 | }
9 |
10 | export function Modal(props: PropsWithChildren) {
11 | const {cancelable = true, style, children} = props;
12 |
13 | const navigator = useNavigator();
14 |
15 | const onPress = () => {
16 | if (cancelable) {
17 | navigator.hideModal();
18 | }
19 | };
20 |
21 | return (
22 |
23 |
24 | {children}
25 |
26 | );
27 | }
28 |
29 | const styles = StyleSheet.create({
30 | container: {
31 | flex: 1,
32 | justifyContent: 'center',
33 | },
34 | overlay: {
35 | position: 'absolute',
36 | left: 0,
37 | right: 0,
38 | top: 0,
39 | bottom: 0,
40 | },
41 | box: {
42 | backgroundColor: '#FFFFFF',
43 | borderRadius: 12,
44 | marginHorizontal: 48,
45 | minHeight: 160,
46 | overflow: 'hidden',
47 | },
48 | });
49 |
--------------------------------------------------------------------------------
/demo/components/ModalButton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Pressable, StyleProp, StyleSheet, Text, TextStyle, ViewStyle} from 'react-native';
3 |
4 | interface ModalButtonProps {
5 | text: string;
6 | onPress?: () => void;
7 | buttonStyle?: StyleProp;
8 | textStyle?: StyleProp;
9 | }
10 |
11 | export default function ModalButton(props: ModalButtonProps) {
12 | const {text, onPress, buttonStyle, textStyle} = props;
13 | return (
14 |
15 | {text}
16 |
17 | );
18 | }
19 |
20 | const styles = StyleSheet.create({
21 | button: {
22 | flex: 1,
23 | alignItems: 'center',
24 | justifyContent: 'center',
25 | },
26 |
27 | text: {
28 | fontSize: 18,
29 | },
30 | });
31 |
--------------------------------------------------------------------------------
/demo/components/ModalToolbar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, View} from 'react-native';
3 | import ModalButton from './ModalButton';
4 |
5 | interface ModalToolbarProps {
6 | onCancel?: () => void;
7 | onConfirm?: () => void;
8 | }
9 |
10 | export function ModalToolbar(props: ModalToolbarProps) {
11 | const {onCancel, onConfirm} = props;
12 | return (
13 |
14 |
15 |
22 |
23 | );
24 | }
25 |
26 | const styles = StyleSheet.create({
27 | divider: {
28 | borderLeftColor: '#DFE3E579',
29 | borderLeftWidth: 1,
30 | },
31 | textCancel: {
32 | color: '#292F33',
33 | },
34 | textConfirm: {
35 | color: '#1A9EFF',
36 | },
37 | buttons: {
38 | borderTopColor: '#DFE3E579',
39 | borderTopWidth: 1,
40 | flexDirection: 'row',
41 | height: 48,
42 | },
43 | });
44 |
--------------------------------------------------------------------------------
/demo/components/TabBar/TabBarIndicator.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Animated, StyleProp, StyleSheet, ViewStyle} from 'react-native';
3 |
4 | interface TabBarIndicatorProps {
5 | style?: StyleProp;
6 | scrollX: Animated.AnimatedInterpolation;
7 | }
8 |
9 | export default function TabBarIndicator({style, scrollX}: TabBarIndicatorProps) {
10 | return (
11 |
15 | );
16 | }
17 |
18 | const styles = StyleSheet.create({
19 | indicator: {
20 | position: 'absolute',
21 | left: 0,
22 | bottom: 0,
23 | width: 24,
24 | height: 4,
25 | backgroundColor: '#448AFF',
26 | borderRadius: 2,
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/demo/components/TabBar/TabBarItem.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Animated,
4 | Pressable,
5 | StyleProp,
6 | StyleSheet,
7 | TextStyle,
8 | ViewProps,
9 | ViewStyle,
10 | } from 'react-native';
11 |
12 | interface TabBarItemProps {
13 | title: string;
14 | onPress?: () => void;
15 | onLayout: ViewProps['onLayout'];
16 | style?: StyleProp;
17 | labelStyle?:
18 | | Animated.WithAnimatedObject
19 | | Animated.WithAnimatedArray>;
20 | }
21 |
22 | export default function TabBarItem({title, style, labelStyle, onPress, onLayout}: TabBarItemProps) {
23 | return (
24 |
25 | {title}
26 |
27 | );
28 | }
29 |
30 | const styles = StyleSheet.create({
31 | tab: {
32 | height: '100%',
33 | minWidth: 24,
34 | justifyContent: 'center',
35 | alignItems: 'center',
36 | },
37 | label: {
38 | color: '#333333',
39 | fontSize: 16,
40 | },
41 | });
42 |
--------------------------------------------------------------------------------
/demo/components/TabBar/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet} from 'react-native';
3 | import ScrollBar from './ScrollBar';
4 | import TabBar, {TabBarProps} from './TabBar';
5 |
6 | export default function (props: TabBarProps) {
7 | return (
8 |
9 |
10 |
11 | );
12 | }
13 |
14 | const styles = StyleSheet.create({
15 | scrollbar: {
16 | height: 48,
17 | flexGrow: 0,
18 | },
19 | tabbar: {
20 | height: '100%',
21 | borderBottomColor: '#eee',
22 | borderBottomWidth: 1,
23 | flex: 1,
24 | justifyContent: 'space-evenly',
25 | },
26 | tab: {
27 | paddingHorizontal: 16,
28 | },
29 | });
30 |
--------------------------------------------------------------------------------
/demo/components/contacts/ContactCell.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, View, Text} from 'react-native';
3 |
4 | import Contact from './models/Contact';
5 |
6 | interface ContactCellProps {
7 | contact: Contact;
8 | }
9 |
10 | const ContactCell = ({contact}: ContactCellProps) => {
11 | return (
12 |
13 |
14 | {contact.firstName}
15 | {contact.lastName}
16 |
17 |
18 | );
19 | };
20 |
21 | export default ContactCell;
22 |
23 | const styles = StyleSheet.create({
24 | rowContainer: {
25 | backgroundColor: 'white',
26 | padding: 10,
27 | },
28 | firstName: {
29 | fontSize: 18,
30 | },
31 | lastName: {
32 | fontSize: 18,
33 | fontWeight: 'bold',
34 | },
35 | });
36 |
--------------------------------------------------------------------------------
/demo/components/contacts/ContactDivider.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, View} from 'react-native';
3 |
4 | import Contact from './models/Contact';
5 |
6 | interface ContactDividerProps {
7 | leadingItem: Contact | string;
8 | trailingItem: Contact | string;
9 | }
10 |
11 | const ContactDivider = ({leadingItem, trailingItem}: ContactDividerProps) => {
12 | if (typeof leadingItem === 'string' || typeof trailingItem === 'string') {
13 | return null;
14 | }
15 | return (
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default ContactDivider;
23 |
24 | const styles = StyleSheet.create({
25 | divider: {
26 | marginHorizontal: 10,
27 | height: StyleSheet.hairlineWidth,
28 | backgroundColor: '#DDD',
29 | },
30 | dividerContainer: {
31 | height: StyleSheet.hairlineWidth,
32 | backgroundColor: 'white',
33 | },
34 | });
35 |
--------------------------------------------------------------------------------
/demo/components/contacts/ContactHeader.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Text, StyleSheet} from 'react-native';
3 |
4 | const ContactHeader = () => {
5 | return My contacts;
6 | };
7 |
8 | export default ContactHeader;
9 |
10 | const styles = StyleSheet.create({
11 | headerTitle: {
12 | backgroundColor: 'white',
13 | paddingHorizontal: 10,
14 | paddingVertical: 20,
15 | fontSize: 17,
16 | fontWeight: 'bold',
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/demo/components/contacts/ContactSectionHeader.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, View, Text} from 'react-native';
3 |
4 | interface ContactSectionHeaderProps {
5 | title: string;
6 | }
7 |
8 | const ContactSectionHeader = ({title}: ContactSectionHeaderProps) => {
9 | return (
10 |
11 | {title}
12 |
13 | );
14 | };
15 |
16 | export default ContactSectionHeader;
17 |
18 | const styles = StyleSheet.create({
19 | headerTitle: {
20 | paddingLeft: 10,
21 | paddingVertical: 4,
22 | },
23 | header: {
24 | backgroundColor: '#FAFAFA',
25 | },
26 | });
27 |
--------------------------------------------------------------------------------
/demo/components/contacts/models/Contact.ts:
--------------------------------------------------------------------------------
1 | export default interface Contact {
2 | firstName: string;
3 | lastName: string;
4 | }
5 |
--------------------------------------------------------------------------------
/demo/components/tabview/Shared/Albums.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Image, Dimensions, ScrollView, StyleSheet} from 'react-native';
3 |
4 | const COVERS = [
5 | require('assets/album-art-1.jpg'),
6 | require('assets/album-art-2.jpg'),
7 | require('assets/album-art-3.jpg'),
8 | require('assets/album-art-4.jpg'),
9 | require('assets/album-art-5.jpg'),
10 | require('assets/album-art-6.jpg'),
11 | require('assets/album-art-7.jpg'),
12 | require('assets/album-art-8.jpg'),
13 | ];
14 |
15 | const Albums = () => {
16 | return (
17 |
18 | {COVERS.map((source, i) => (
19 |
20 | ))}
21 |
22 | );
23 | };
24 |
25 | export default Albums;
26 |
27 | const styles = StyleSheet.create({
28 | container: {
29 | backgroundColor: '#343C46',
30 | },
31 | content: {
32 | flexDirection: 'row',
33 | flexWrap: 'wrap',
34 | },
35 | cover: {
36 | width: '50%',
37 | height: Dimensions.get('window').width / 2,
38 | },
39 | });
40 |
--------------------------------------------------------------------------------
/demo/components/twitter/TweetCell.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Pressable} from 'react-native';
3 |
4 | import Tweet from './models/Tweet';
5 | import TweetContent from './TweetContent';
6 |
7 | export interface TweetCellProps {
8 | tweet: Tweet;
9 | }
10 |
11 | const TweetCell = ({tweet}: TweetCellProps) => {
12 | return (
13 | {}}>
14 |
15 |
16 | );
17 | };
18 |
19 | export default TweetCell;
20 |
--------------------------------------------------------------------------------
/demo/components/twitter/index.ts:
--------------------------------------------------------------------------------
1 | export {default as Twitter} from './Twitter';
2 | export {default as TwitterFlatList} from './TwitterFlatList';
3 |
--------------------------------------------------------------------------------
/demo/components/twitter/models/Author.ts:
--------------------------------------------------------------------------------
1 | export default interface Author {
2 | name: string;
3 | avatar: string;
4 | screenName: string;
5 | }
6 |
--------------------------------------------------------------------------------
/demo/components/twitter/models/Tweet.ts:
--------------------------------------------------------------------------------
1 | import Author from './Author';
2 |
3 | export default interface Tweet {
4 | id: string;
5 | author: Author;
6 | fullText: string;
7 | retweetCount: number;
8 | replyCount: number;
9 | favoriteCount: number;
10 | }
11 |
--------------------------------------------------------------------------------
/demo/image-crop/ImageCropResultPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {NavigationProps, withNavigationItem} from 'hybrid-navigation';
3 | import {StyleSheet, View} from 'react-native';
4 | import FastImage from 'react-native-fast-image';
5 |
6 | interface Props extends NavigationProps {
7 | fileUri: string;
8 | }
9 |
10 | function ImageCropResultPage({fileUri}: Props) {
11 | return (
12 |
13 |
21 |
22 | );
23 | }
24 |
25 | export default withNavigationItem({titleItem: {title: 'CropResultPage'}})(ImageCropResultPage);
26 |
27 | const styles = StyleSheet.create({
28 | container: {
29 | flex: 1,
30 | justifyContent: 'flex-start',
31 | alignItems: 'stretch',
32 | backgroundColor: '#eef',
33 | },
34 | image: {
35 | flex: 1,
36 | },
37 | });
38 |
--------------------------------------------------------------------------------
/demo/image-crop/index.ts:
--------------------------------------------------------------------------------
1 | import Navigation from 'hybrid-navigation';
2 |
3 | import ImageCropDemo from './ImageCropDemo';
4 | import ImageCropPage from './ImageCropPage';
5 | import ImageCropResultPage from './ImageCropResultPage';
6 |
7 | export function registerImageCropComponent() {
8 | Navigation.registerComponent('ImageCropDemo', () => ImageCropDemo);
9 | Navigation.registerComponent('ImageCropPage', () => ImageCropPage);
10 | Navigation.registerComponent('ImageCropResultPage', () => ImageCropResultPage);
11 | }
12 |
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChat/Message/data.ts:
--------------------------------------------------------------------------------
1 | import type {MessageProps} from './types';
2 |
3 | export const history: MessageProps[] = [
4 | {text: 'Hmmmm🤔'},
5 | {text: 'It looks like it still will be laggy...'},
6 | {text: "But I don't know what should I try next"},
7 | {text: 'Reanimated?', sender: true},
8 | {text: 'A little bit disappointed 😔'},
9 | {text: '🤯'},
10 | {text: 'Try to check it. I hope it helps you...', sender: true},
11 | {text: 'It really pushes you to think twice on how to design it first'},
12 | {
13 | text: 'Looks promising!😎 I was always looking for a solution that would allow us to run animations on native thread and provide at least stable 60 FPS',
14 | },
15 | {text: 'You have to check it!!!', sender: true},
16 | {text: "Ha-ha! I'm definitely going to check it!"},
17 | {text: 'Hello! How are you?'},
18 | {text: "Hi! I'm good. How are you?", sender: true},
19 | {
20 | text: "I'm fine, thank you! Have you seen new keyboard animation library?",
21 | },
22 | {text: 'No! Let me check.', sender: true},
23 | {
24 | text: "Wow! I've been looking for it for a while. It's awesome!",
25 | sender: true,
26 | },
27 | ];
28 |
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChat/Message/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Text, View} from 'react-native';
3 | import styles from './styles';
4 |
5 | import type {MessageProps} from './types';
6 |
7 | export default function Message({text, sender}: MessageProps) {
8 | return (
9 |
10 | {text}
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChat/Message/styles.ts:
--------------------------------------------------------------------------------
1 | import {StyleSheet} from 'react-native';
2 |
3 | const container = {
4 | borderRadius: 10,
5 | padding: 10,
6 | margin: 10,
7 | marginVertical: 5,
8 | };
9 | export default StyleSheet.create({
10 | senderContainer: {
11 | alignSelf: 'flex-end',
12 | backgroundColor: '#e0e0e0',
13 | ...container,
14 | },
15 | recipientContainer: {
16 | alignSelf: 'flex-start',
17 | backgroundColor: '#50FF00',
18 | ...container,
19 | },
20 | message: {
21 | color: '#000000',
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChat/Message/types.ts:
--------------------------------------------------------------------------------
1 | export type MessageProps = {
2 | text: string;
3 | sender?: boolean;
4 | };
5 |
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChat/driver/Driver.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Animated} from 'react-native';
3 |
4 | export interface DriverState {
5 | // sender bottom
6 | bottom: number;
7 | driver: Driver | undefined;
8 | setDriver: React.Dispatch>;
9 | setTranslateY: React.Dispatch>;
10 | }
11 |
12 | export interface Driver {
13 | show: (state: DriverState) => void;
14 | hide: (state: DriverState) => void;
15 | toggle: (state: DriverState) => void;
16 | shown: boolean;
17 | height: number;
18 | name: string;
19 | }
20 |
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChat/icon/emoji@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChat/icon/emoji@2x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChat/icon/emoji@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChat/icon/emoji@3x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChat/icon/keyboard@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChat/icon/keyboard@2x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChat/icon/keyboard@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChat/icon/keyboard@3x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChat/icon/plus@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChat/icon/plus@2x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChat/icon/plus@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChat/icon/plus@3x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChatReanimated/Message/data.ts:
--------------------------------------------------------------------------------
1 | import type {MessageProps} from './types';
2 |
3 | export const history: MessageProps[] = [
4 | {text: 'Hmmmm🤔'},
5 | {text: 'It looks like it still will be laggy...'},
6 | {text: "But I don't know what should I try next"},
7 | {text: 'Reanimated?', sender: true},
8 | {text: 'A little bit disappointed 😔'},
9 | {text: '🤯'},
10 | {text: 'Try to check it. I hope it helps you...', sender: true},
11 | {text: 'It really pushes you to think twice on how to design it first'},
12 | {
13 | text: 'Looks promising!😎 I was always looking for a solution that would allow us to run animations on native thread and provide at least stable 60 FPS',
14 | },
15 | {text: 'You have to check it!!!', sender: true},
16 | {text: "Ha-ha! I'm definitely going to check it!"},
17 | {text: 'Hello! How are you?'},
18 | {text: "Hi! I'm good. How are you?", sender: true},
19 | {
20 | text: "I'm fine, thank you! Have you seen new keyboard animation library?",
21 | },
22 | {text: 'No! Let me check.', sender: true},
23 | {
24 | text: "Wow! I've been looking for it for a while. It's awesome!",
25 | sender: true,
26 | },
27 | ];
28 |
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChatReanimated/Message/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Text, View} from 'react-native';
3 | import styles from './styles';
4 |
5 | import type {MessageProps} from './types';
6 |
7 | export default function Message({text, sender}: MessageProps) {
8 | return (
9 |
10 | {text}
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChatReanimated/Message/styles.ts:
--------------------------------------------------------------------------------
1 | import {StyleSheet} from 'react-native';
2 |
3 | const container = {
4 | borderRadius: 10,
5 | padding: 10,
6 | margin: 10,
7 | marginVertical: 5,
8 | };
9 | export default StyleSheet.create({
10 | senderContainer: {
11 | alignSelf: 'flex-end',
12 | backgroundColor: '#e0e0e0',
13 | ...container,
14 | },
15 | recipientContainer: {
16 | alignSelf: 'flex-start',
17 | backgroundColor: '#50FF00',
18 | ...container,
19 | },
20 | message: {
21 | color: '#000000',
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChatReanimated/Message/types.ts:
--------------------------------------------------------------------------------
1 | export type MessageProps = {
2 | text: string;
3 | sender?: boolean;
4 | };
5 |
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChatReanimated/icon/emoji@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChatReanimated/icon/emoji@2x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChatReanimated/icon/emoji@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChatReanimated/icon/emoji@3x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChatReanimated/icon/keyboard@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChatReanimated/icon/keyboard@2x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChatReanimated/icon/keyboard@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChatReanimated/icon/keyboard@3x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChatReanimated/icon/plus@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChatReanimated/icon/plus@2x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChatReanimated/icon/plus@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/demo/keyboard-insets/KeyboardChatReanimated/icon/plus@3x.png
--------------------------------------------------------------------------------
/demo/keyboard-insets/KeyboardChatReanimated/useRekeyboard.ts:
--------------------------------------------------------------------------------
1 | import {useCallback} from 'react';
2 | import {useSharedValue} from 'react-native-reanimated';
3 | import {
4 | KeyboardPositionChangedEventData,
5 | KeyboardStatusChangedEventData,
6 | } from '@sdcx/keyboard-insets';
7 |
8 | export function useRekeyboard() {
9 | const position = useSharedValue(0);
10 | const height = useSharedValue(0);
11 | const shown = useSharedValue(false);
12 | const transitioning = useSharedValue(false);
13 |
14 | const onStatusChanged = useCallback(
15 | (e: KeyboardStatusChangedEventData) => {
16 | 'worklet';
17 | shown.value = e.shown;
18 | transitioning.value = e.transitioning;
19 | height.value = e.height;
20 | },
21 | [shown, transitioning, height],
22 | );
23 |
24 | const onPositionChanged = useCallback(
25 | (e: KeyboardPositionChangedEventData) => {
26 | 'worklet';
27 | position.value = e.position;
28 | },
29 | [position],
30 | );
31 |
32 | const keyboard = {
33 | position,
34 | height,
35 | shown,
36 | transitioning,
37 | };
38 |
39 | return {keyboard, onStatusChanged, onPositionChanged};
40 | }
41 |
--------------------------------------------------------------------------------
/demo/keyboard-insets/index.ts:
--------------------------------------------------------------------------------
1 | import Navigation from 'hybrid-navigation';
2 |
3 | import Home from './Home';
4 | import KeyboardAdvoiding from './KeyboardAdvoiding';
5 | import KeyboardChat from './KeyboardChat';
6 | import KeyboardChatReanimated from './KeyboardChatReanimated';
7 | import ModalTextInput from './ModalTextInput';
8 |
9 | export function registerKeyboardComponent() {
10 | Navigation.registerComponent('Keyboard', () => Home);
11 | Navigation.registerComponent('KeyboardAdvoiding', () => KeyboardAdvoiding);
12 | Navigation.registerComponent('KeyboardChat', () => KeyboardChat);
13 | Navigation.registerComponent('KeyboardChatReanimated', () => KeyboardChatReanimated);
14 | Navigation.registerComponent('ModalTextInput', () => ModalTextInput);
15 | }
16 |
--------------------------------------------------------------------------------
/demo/nested-scroll/NestedScrollParallaxHeader/components/HeaderComponent.tsx:
--------------------------------------------------------------------------------
1 | import {StyleSheet, Text, View} from 'react-native';
2 | import React from 'react';
3 |
4 | export const HeaderComponent = () => (
5 |
6 |
7 | Foreground component
8 |
9 |
10 | );
11 |
12 | const styles = StyleSheet.create({
13 | container: {
14 | flex: 1,
15 | backgroundColor: 'transparent',
16 | alignItems: 'center',
17 | justifyContent: 'center',
18 | },
19 | titleContainer: {
20 | backgroundColor: 'red',
21 | },
22 | });
23 |
--------------------------------------------------------------------------------
/demo/nested-scroll/NestedScrollParallaxHeader/components/HeaderNavBar.tsx:
--------------------------------------------------------------------------------
1 | import {RoundButton} from './RoundButton';
2 | import {StyleSheet, View} from 'react-native';
3 | import * as React from 'react';
4 | import ArrowLeft from '../icons/ArrowLeft';
5 | import ArrowRight from '../icons/ArrowRight';
6 | import {Share} from '../icons/Share';
7 | import {useNavigator} from 'hybrid-navigation';
8 |
9 | export const HeaderNavBar = () => {
10 | const navigator = useNavigator();
11 | return (
12 |
13 | } onPress={navigator.pop} />
14 |
15 |
16 | } onPress={() => null} />
17 |
18 | } onPress={() => null} />
19 |
20 |
21 | );
22 | };
23 |
24 | const styles = StyleSheet.create({
25 | container: {
26 | width: '100%',
27 | paddingHorizontal: 8,
28 | flexDirection: 'row',
29 | justifyContent: 'space-between',
30 | },
31 | btnRightContainer: {
32 | flexDirection: 'row',
33 | alignItems: 'center',
34 | },
35 | btnRight: {
36 | marginRight: 8,
37 | },
38 | });
39 |
--------------------------------------------------------------------------------
/demo/nested-scroll/NestedScrollParallaxHeader/components/RoundButton.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {StyleSheet, TouchableOpacity} from 'react-native';
3 |
4 | type Props = {
5 | icon: JSX.Element;
6 | onPress: () => void;
7 | };
8 | export const RoundButton = ({icon, onPress}: Props) => {
9 | return (
10 |
11 | {icon}
12 |
13 | );
14 | };
15 | const styles = StyleSheet.create({
16 | container: {
17 | backgroundColor: 'white',
18 | width: 30,
19 | height: 30,
20 | borderRadius: 15,
21 | alignItems: 'center',
22 | justifyContent: 'center',
23 | },
24 | });
25 |
--------------------------------------------------------------------------------
/demo/nested-scroll/NestedScrollParallaxHeader/hooks/useAnimatedNavbar.ts:
--------------------------------------------------------------------------------
1 | // fork:https://github.com/kanelloc/react-native-animated-header-scroll-view/blob/main/src/hooks/useAnimateNavbar.ts
2 |
3 | import type {Animated} from 'react-native';
4 |
5 | export const useAnimatedNavbar = (
6 | scroll: Animated.Value,
7 | imageHeight: number,
8 | headerHeight: number,
9 | ) => {
10 | const HEADER_HEIGHT_DIFFERENCE = imageHeight - headerHeight;
11 | const headerOpacity = scroll.interpolate({
12 | inputRange: [0, HEADER_HEIGHT_DIFFERENCE * 0.75, HEADER_HEIGHT_DIFFERENCE],
13 | outputRange: [0, 0, 1],
14 | extrapolate: 'clamp',
15 | });
16 | const overflowHeaderOpacity = scroll.interpolate({
17 | inputRange: [0, HEADER_HEIGHT_DIFFERENCE * 0.75, HEADER_HEIGHT_DIFFERENCE],
18 | outputRange: [1, 1, 0],
19 | extrapolate: 'clamp',
20 | });
21 |
22 | return [headerOpacity, overflowHeaderOpacity];
23 | };
24 |
--------------------------------------------------------------------------------
/demo/nested-scroll/NestedScrollParallaxHeader/icons/ArrowLeft.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Svg, {Path} from 'react-native-svg';
3 | import {StyleSheet} from 'react-native';
4 |
5 | const ArrowLeft = (props: any) => (
6 |
21 | );
22 |
23 | export default ArrowLeft;
24 |
--------------------------------------------------------------------------------
/demo/nested-scroll/NestedScrollParallaxHeader/icons/ArrowRight.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {StyleSheet} from 'react-native';
3 | import type {SvgProps} from 'react-native-svg';
4 | import Svg, {Path} from 'react-native-svg';
5 |
6 | const ArrowRight = ({color = '#000000', style, ...props}: SvgProps) => (
7 |
19 | );
20 |
21 | export default ArrowRight;
22 |
--------------------------------------------------------------------------------
/demo/nested-scroll/NestedScrollParallaxHeader/icons/Share.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import type {SvgProps} from 'react-native-svg';
3 | import Svg, {Path} from 'react-native-svg';
4 |
5 | export const Share = (props: SvgProps) => (
6 |
15 | );
16 |
--------------------------------------------------------------------------------
/demo/nested-scroll/index.ts:
--------------------------------------------------------------------------------
1 | import Navigation from 'hybrid-navigation';
2 |
3 | import Home from './Home';
4 | import NestedScrollFlatList from './NestedScrollFlatList';
5 | import NestedScrollParallaxHeader from './NestedScrollParallaxHeader';
6 | import NestedScrollTabView from './NestedScrollTabView';
7 | import NestedScrollPagerViewStickyHeader from './NestedScrollPagerViewStickyHeader';
8 |
9 | export function registerNestedScrollComponent() {
10 | Navigation.registerComponent('NestedScroll', () => Home);
11 | Navigation.registerComponent('NestedScrollFlatList', () => NestedScrollFlatList);
12 | Navigation.registerComponent('NestedScrollParallaxHeader', () => NestedScrollParallaxHeader);
13 | Navigation.registerComponent('NestedScrollTabView', () => NestedScrollTabView);
14 | Navigation.registerComponent(
15 | 'NestedScrollPagerViewStickyHeader',
16 | () => NestedScrollPagerViewStickyHeader,
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/demo/overlay/Alert/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, Text, ViewProps, TextStyle, StyleProp, Pressable} from 'react-native';
3 |
4 | interface Props extends ViewProps {
5 | onPress: () => void;
6 | text: string;
7 | textStyle?: StyleProp;
8 | }
9 |
10 | export default function Button({style, text, onPress, textStyle}: Props) {
11 | return (
12 |
13 | {text}
14 |
15 | );
16 | }
17 |
18 | const styles = StyleSheet.create({
19 | button: {
20 | height: 44,
21 | flex: 1,
22 | borderRadius: 4,
23 | alignItems: 'center',
24 | justifyContent: 'center',
25 | backgroundColor: '#F2F6FC',
26 | },
27 | text: {
28 | fontSize: 16,
29 | color: '#606972',
30 | fontWeight: 'bold',
31 | },
32 | });
33 |
--------------------------------------------------------------------------------
/demo/overlay/Alert/Message.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, Text, View} from 'react-native';
3 |
4 | interface MessageProps {
5 | message: string;
6 | }
7 |
8 | export default function Message({message}: MessageProps) {
9 | return (
10 |
11 | {message}
12 |
13 | );
14 | }
15 |
16 | const styles = StyleSheet.create({
17 | box: {
18 | paddingHorizontal: 12,
19 | },
20 | message: {
21 | color: '#1D2023',
22 | fontSize: 15,
23 | lineHeight: 25,
24 | marginVertical: 6,
25 | },
26 | });
27 |
--------------------------------------------------------------------------------
/demo/overlay/Alert/Title.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, Text} from 'react-native';
3 |
4 | interface TitleProps {
5 | title: string;
6 | }
7 |
8 | export default function Title({title}: TitleProps) {
9 | return {title};
10 | }
11 |
12 | const styles = StyleSheet.create({
13 | title: {
14 | color: '#1D2023',
15 | fontSize: 17,
16 | fontWeight: 'bold',
17 | lineHeight: 26,
18 | alignSelf: 'center',
19 | marginVertical: 6,
20 | },
21 | });
22 |
--------------------------------------------------------------------------------
/demo/overlay/Hoverball/types.ts:
--------------------------------------------------------------------------------
1 | export interface Anchor {
2 | x: number;
3 | y: number;
4 | size: number;
5 | }
6 |
7 | export interface BallProps {
8 | anchor: Anchor;
9 | onOffsetChanged?: (x: number, y: number) => void;
10 | }
11 |
--------------------------------------------------------------------------------
/demo/overlay/HoverballScreen.tsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect} from 'react';
2 | import {View, Text, StyleSheet, Switch} from 'react-native';
3 | import {withNavigationItem} from 'hybrid-navigation';
4 | import Hoverball from './Hoverball';
5 |
6 | function FloatingBall() {
7 | const [enabled, setEnabled] = React.useState(false);
8 |
9 | useEffect(() => {
10 | enabled ? Hoverball.show() : Hoverball.hide();
11 | }, [enabled]);
12 |
13 | return (
14 |
15 | 开启或关闭悬浮球
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | export default withNavigationItem({
23 | titleItem: {
24 | title: '悬浮球',
25 | },
26 | })(FloatingBall);
27 |
28 | const styles = StyleSheet.create({
29 | container: {
30 | flex: 1,
31 | justifyContent: 'flex-start',
32 | alignItems: 'stretch',
33 | paddingTop: 16,
34 | paddingLeft: 32,
35 | paddingRight: 32,
36 | },
37 | text: {
38 | backgroundColor: 'transparent',
39 | fontSize: 17,
40 | alignSelf: 'center',
41 | },
42 | });
43 |
--------------------------------------------------------------------------------
/demo/overlay/ToastScreen.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {View, StyleSheet, Button} from 'react-native';
3 | import {withNavigationItem} from 'hybrid-navigation';
4 | import Toast from './Toast';
5 |
6 | function ToastScreen() {
7 | return (
8 |
9 |
19 | );
20 | }
21 |
22 | export default withNavigationItem({
23 | titleItem: {
24 | title: 'Toast',
25 | },
26 | })(ToastScreen);
27 |
28 | const styles = StyleSheet.create({
29 | container: {
30 | flex: 1,
31 | justifyContent: 'flex-start',
32 | alignItems: 'stretch',
33 | paddingTop: 16,
34 | paddingLeft: 32,
35 | paddingRight: 32,
36 | },
37 | text: {
38 | backgroundColor: 'transparent',
39 | fontSize: 17,
40 | alignSelf: 'center',
41 | },
42 | });
43 |
--------------------------------------------------------------------------------
/demo/overlay/index.ts:
--------------------------------------------------------------------------------
1 | import Navigation from 'hybrid-navigation';
2 |
3 | import Home from './Home';
4 | import HoverballScreen from './HoverballScreen';
5 | import ToastScreen from './ToastScreen';
6 | import AlertScreen from './AlertScreen';
7 |
8 | export function registerOverlayComponent() {
9 | Navigation.registerComponent('Overlay', () => Home);
10 | Navigation.registerComponent('Hoverball', () => HoverballScreen);
11 | Navigation.registerComponent('Toast', () => ToastScreen);
12 | Navigation.registerComponent('Alert', () => AlertScreen);
13 | }
14 |
--------------------------------------------------------------------------------
/demo/pull-to-refresh/PullRefreshScrollView/index.tsx:
--------------------------------------------------------------------------------
1 | import {withNavigationItem} from 'hybrid-navigation';
2 | import React, {useRef, useState} from 'react';
3 | import {ScrollViewPage} from '../../components/ScrollViewPage';
4 | import {PullToRefresh} from '@sdcx/pull-to-refresh';
5 |
6 | function PullRefreshScrollView() {
7 | const [refreshing, setRefreshing] = useState(false);
8 |
9 | const pendingAction = useRef | null>(null);
10 |
11 | const clearPendingAction = () => {
12 | if (pendingAction.current) {
13 | clearTimeout(pendingAction.current);
14 | }
15 | };
16 |
17 | const beginRefresh = async () => {
18 | setRefreshing(true);
19 |
20 | pendingAction.current = setTimeout(() => {
21 | endRefresh();
22 | }, 1500);
23 | };
24 |
25 | const endRefresh = () => {
26 | clearPendingAction();
27 | setRefreshing(false);
28 | };
29 |
30 | return (
31 |
32 |
33 |
34 | );
35 | }
36 |
37 | export default withNavigationItem({
38 | titleItem: {
39 | title: 'PullRefresh + ScrollView',
40 | },
41 | })(PullRefreshScrollView);
42 |
--------------------------------------------------------------------------------
/demo/pull-to-refresh/PullRefreshWithoutScrollView/Page.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, View} from 'react-native';
3 | import PrimaryButton from '../../components/PrimaryButton';
4 |
5 | export default function Page() {
6 | const onPress = () => {
7 | console.log('click test');
8 | };
9 | return (
10 |
11 |
12 |
13 |
14 |
15 | );
16 | }
17 |
18 | const styles = StyleSheet.create({
19 | container: {
20 | width: '100%',
21 | height: '100%',
22 | justifyContent: 'center',
23 | zIndex: 1,
24 | },
25 | card: {
26 | marginHorizontal: 32,
27 | height: 400,
28 | backgroundColor: '#FFFFFF',
29 | borderRadius: 20,
30 | justifyContent: 'space-around',
31 | },
32 | button: {
33 | marginHorizontal: 32,
34 | },
35 | });
36 |
--------------------------------------------------------------------------------
/demo/pull-to-refresh/PullToRefresh/index.ts:
--------------------------------------------------------------------------------
1 | import {PullToRefresh} from '@sdcx/pull-to-refresh';
2 | import {LottiePullToRefreshHeader} from './LottiePullToRefreshHeader';
3 | import {LottiePullToRefreshFooter} from './LottiePullToRefreshFooter';
4 |
5 | PullToRefresh.setDefaultHeader(LottiePullToRefreshHeader);
6 | PullToRefresh.setDefaultFooter(LottiePullToRefreshFooter);
7 |
--------------------------------------------------------------------------------
/demo/wheel-picker/WheelPickerDemo.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {StyleSheet, View} from 'react-native';
3 | import TimePicker from './TimePicker';
4 | import {withNavigationItem} from 'hybrid-navigation';
5 | import CityPicker from './CityPicker';
6 |
7 | export default withNavigationItem({
8 | titleItem: {
9 | title: 'WheelPicker',
10 | },
11 | })(WheelPicker);
12 |
13 | function WheelPicker() {
14 | const [citycode, setCitycode] = useState('15');
15 |
16 | return (
17 |
18 |
19 |
20 |
21 | );
22 | }
23 |
24 | const styles = StyleSheet.create({
25 | container: {
26 | flex: 1,
27 | justifyContent: 'center',
28 | alignItems: 'center',
29 | backgroundColor: '#F5FCFF',
30 | },
31 | region: {
32 | width: 300,
33 | height: 224,
34 | },
35 | time: {
36 | width: 200,
37 | height: 224,
38 | },
39 | });
40 |
--------------------------------------------------------------------------------
/demo/wheel-picker/index.ts:
--------------------------------------------------------------------------------
1 | import Navigation from 'hybrid-navigation';
2 |
3 | import WheelPicker from './WheelPickerDemo';
4 |
5 | export function registerWheelPicker() {
6 | Navigation.registerComponent('WheelPicker', () => WheelPicker);
7 | }
8 |
--------------------------------------------------------------------------------
/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | export NODE_BINARY=$(command -v node)
2 |
--------------------------------------------------------------------------------
/ios/MyUiDemo.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/MyUiDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/MyUiDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : UIResponder
5 |
6 | @property (strong, nonatomic) UIWindow *window;
7 |
8 | @end
--------------------------------------------------------------------------------
/ios/MyUiDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/ios/MyUiDemo/PrivacyInfo.xcprivacy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPrivacyAccessedAPITypes
6 |
7 |
8 | NSPrivacyAccessedAPIType
9 | NSPrivacyAccessedAPICategoryFileTimestamp
10 | NSPrivacyAccessedAPITypeReasons
11 |
12 | C617.1
13 | 3B52.1
14 |
15 |
16 |
17 | NSPrivacyAccessedAPIType
18 | NSPrivacyAccessedAPICategoryUserDefaults
19 | NSPrivacyAccessedAPITypeReasons
20 |
21 | CA92.1
22 |
23 |
24 |
25 | NSPrivacyAccessedAPIType
26 | NSPrivacyAccessedAPICategorySystemBootTime
27 | NSPrivacyAccessedAPITypeReasons
28 |
29 | 35F9.1
30 |
31 |
32 |
33 | NSPrivacyCollectedDataTypes
34 |
35 | NSPrivacyTracking
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/ios/MyUiDemo/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import "AppDelegate.h"
3 |
4 | int main(int argc, char * argv[]) {
5 | NSString * appDelegateClassName;
6 | @autoreleasepool {
7 | // Setup code that might create autoreleased objects goes here.
8 | appDelegateClassName = NSStringFromClass([AppDelegate class]);
9 | }
10 | return UIApplicationMain(argc, argv, nil, appDelegateClassName);
11 | }
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Disable New Architecture
2 | ENV['RCT_NEW_ARCH_ENABLED'] = '0'
3 |
4 | # Resolve react_native_pods.rb with node to allow for hoisting
5 | require Pod::Executable.execute_command('node', ['-p',
6 | 'require.resolve(
7 | "react-native/scripts/react_native_pods.rb",
8 | {paths: [process.argv[1]]},
9 | )', __dir__]).strip
10 |
11 | platform :ios, min_ios_version_supported
12 | prepare_react_native_project!
13 |
14 | linkage = ENV['USE_FRAMEWORKS']
15 | if linkage != nil
16 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
17 | use_frameworks! :linkage => linkage.to_sym
18 | end
19 |
20 | target 'MyUiDemo' do
21 | config = use_native_modules!
22 | use_react_native!(
23 | :path => config[:reactNativePath],
24 | :hermes_enabled => true,
25 | :app_path => "#{Pod::Config.instance.installation_root}/.."
26 | )
27 |
28 | post_install do |installer|
29 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
30 | react_native_post_install(
31 | installer,
32 | config[:reactNativePath],
33 | :mac_catalyst_enabled => false
34 | )
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'react-native',
3 | };
4 |
--------------------------------------------------------------------------------
/metro.config.js:
--------------------------------------------------------------------------------
1 | const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
2 |
3 | /**
4 | * Metro configuration
5 | * https://reactnative.dev/docs/metro
6 | *
7 | * @type {import('@react-native/metro-config').MetroConfig}
8 | */
9 | const config = {};
10 |
11 | module.exports = mergeConfig(getDefaultConfig(__dirname), config);
12 |
--------------------------------------------------------------------------------
/packages/activity-indicator/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 listenzz@163.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/activity-indicator/README.md:
--------------------------------------------------------------------------------
1 | # ActivityIndicator
2 |
3 | `ActivityIndicator` 是一个 React Native 原生 UI 组件,是一个菊花转圈的动画。
4 |
5 |
6 |
7 | ## Installation
8 |
9 | ```bash
10 | yarn add @sdcx/activity-indicator
11 | ```
12 |
13 | ## Usage
14 |
15 | ```tsx
16 | import ActivityIndicator from '@sdcx/activity-indicator';
17 |
18 | function App() {
19 | return (
20 |
21 |
22 |
23 | );
24 | }
25 | ```
26 |
27 | ## Props
28 |
29 | 基本与 RN 的 [ActivityIndicator](https://reactnative.dev/docs/activityindicator) 一致。
30 |
--------------------------------------------------------------------------------
/packages/activity-indicator/android/build.gradle:
--------------------------------------------------------------------------------
1 | // android/build.gradle
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 |
7 | apply plugin: 'com.android.library'
8 |
9 | android {
10 | compileOptions {
11 | sourceCompatibility JavaVersion.VERSION_1_8
12 | targetCompatibility JavaVersion.VERSION_1_8
13 | }
14 |
15 | compileSdkVersion safeExtGet('compileSdkVersion', 30)
16 | buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
17 |
18 | defaultConfig {
19 | minSdkVersion safeExtGet('minSdkVersion', 21)
20 | targetSdkVersion safeExtGet('targetSdkVersion', 30)
21 | versionCode 1
22 | versionName "1.0.0"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | consumerProguardFiles 'proguard-rules.pro'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation 'com.facebook.react:react-native:+'
35 | }
36 |
--------------------------------------------------------------------------------
/packages/activity-indicator/android/proguard-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/activity-indicator/android/proguard-rules.pro
--------------------------------------------------------------------------------
/packages/activity-indicator/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/activity-indicator/android/src/main/java/com/reactnative/activityindicator/ActivityIndicatorPackage.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.activityindicator;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | import com.facebook.react.ReactPackage;
9 | import com.facebook.react.bridge.NativeModule;
10 | import com.facebook.react.bridge.ReactApplicationContext;
11 | import com.facebook.react.uimanager.ViewManager;
12 |
13 | public class ActivityIndicatorPackage implements ReactPackage {
14 | @NonNull
15 | @Override
16 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
17 | return Collections.emptyList();
18 | }
19 |
20 | @NonNull
21 | @Override
22 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
23 | return Collections.singletonList(new ActivityIndicatorManager());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/activity-indicator/docs/assets/activity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/activity-indicator/docs/assets/activity.png
--------------------------------------------------------------------------------
/packages/activity-indicator/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sdcx/activity-indicator",
3 | "description": "A native activity indicator for React Native",
4 | "version": "0.2.0",
5 | "main": "./dist/index.js",
6 | "typings": "./dist/index.d.ts",
7 | "react-native": "src/index",
8 | "nativePackage": true,
9 | "files": [
10 | "src",
11 | "dist",
12 | "android",
13 | "!android/build",
14 | "!**/__tests__"
15 | ],
16 | "repository": "https://github.com/sdcxtech/react-native-troika",
17 | "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/activity-indicator#readme",
18 | "author": "sdcx",
19 | "license": "MIT",
20 | "keywords": [
21 | "react-native",
22 | "activity-indicator"
23 | ],
24 | "scripts": {
25 | "build": "rm -rf ./dist && tsc -p tsconfig.json",
26 | "prepare": "npm run build"
27 | },
28 | "peerDependencies": {
29 | "react": ">=16.8",
30 | "react-native": ">=0.60"
31 | },
32 | "devDependencies": {}
33 | }
34 |
--------------------------------------------------------------------------------
/packages/activity-indicator/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/packages/activity-indicator/src/ActivityIndicatorAndroid.ts:
--------------------------------------------------------------------------------
1 | import {requireNativeComponent} from 'react-native';
2 |
3 | const ActivityIndicatorAndroid = requireNativeComponent('ActivityIndicatorAndroid');
4 |
5 | export default ActivityIndicatorAndroid;
6 |
--------------------------------------------------------------------------------
/packages/activity-indicator/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | ActivityIndicator as ActivityIndicatorIOS,
4 | ActivityIndicatorProps,
5 | Platform,
6 | } from 'react-native';
7 | import ActivityIndicatorAndroid from './ActivityIndicatorAndroid';
8 |
9 | const ActivityIndicator = (props: ActivityIndicatorProps) => {
10 | if (Platform.OS === 'ios') {
11 | return ;
12 | } else {
13 | return ;
14 | }
15 | };
16 |
17 | export default ActivityIndicator;
18 |
--------------------------------------------------------------------------------
/packages/activity-indicator/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./dist",
6 | "noEmit": false,
7 | "strict": true,
8 | "noImplicitAny": true,
9 | "allowImportingTsExtensions": false,
10 | "baseUrl": "./"
11 | },
12 | "include": ["./src/**/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 listenzz@163.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/RNBottomSheet.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "RNBottomSheet"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 |
10 | s.homepage = package["homepage"]
11 | s.license = package["license"]
12 | s.authors = package["author"]
13 | s.platforms = { :ios => "10.0" }
14 | s.source = { :git => "https://github.com/github-account/bottom-sheet.git", :tag => "#{s.version}" }
15 |
16 | s.source_files = "ios/BottomSheet/**/*.{h,m,mm}"
17 | s.dependency "React-Core"
18 | end
--------------------------------------------------------------------------------
/packages/bottom-sheet/android/build.gradle:
--------------------------------------------------------------------------------
1 | // android/build.gradle
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 |
7 | apply plugin: 'com.android.library'
8 |
9 | android {
10 | compileOptions {
11 | sourceCompatibility JavaVersion.VERSION_1_8
12 | targetCompatibility JavaVersion.VERSION_1_8
13 | }
14 |
15 | compileSdkVersion safeExtGet('compileSdkVersion', 30)
16 | buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
17 |
18 | defaultConfig {
19 | minSdkVersion safeExtGet('minSdkVersion', 21)
20 | targetSdkVersion safeExtGet('targetSdkVersion', 30)
21 | versionCode 1
22 | versionName "1.0.0"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | consumerProguardFiles 'proguard-rules.pro'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation 'com.facebook.react:react-native:+'
35 | }
36 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/android/proguard-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/bottom-sheet/android/proguard-rules.pro
--------------------------------------------------------------------------------
/packages/bottom-sheet/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/android/src/main/java/com/reactnative/bottomsheet/BottomSheetPackage.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.bottomsheet;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | import com.facebook.react.ReactPackage;
9 | import com.facebook.react.bridge.NativeModule;
10 | import com.facebook.react.bridge.ReactApplicationContext;
11 | import com.facebook.react.uimanager.ViewManager;
12 |
13 | public class BottomSheetPackage implements ReactPackage {
14 | @NonNull
15 | @Override
16 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
17 | return Collections.emptyList();
18 | }
19 |
20 | @NonNull
21 | @Override
22 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
23 | return Collections.singletonList(new BottomSheetManager());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/android/src/main/java/com/reactnative/bottomsheet/BottomSheetState.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.bottomsheet;
2 |
3 | public enum BottomSheetState {
4 | COLLAPSED, EXPANDED, HIDDEN, DRAGGING, SETTLING
5 | }
6 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/android/src/main/java/com/reactnative/bottomsheet/StateChangedEvent.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.bottomsheet;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import com.facebook.react.bridge.Arguments;
6 | import com.facebook.react.bridge.WritableMap;
7 | import com.facebook.react.uimanager.events.Event;
8 |
9 | public class StateChangedEvent extends Event {
10 | public static final String Name = "stateEvent";
11 | public static final String JSEventName = "onStateChanged";
12 |
13 | private final String state;
14 |
15 | public StateChangedEvent(int surfaceId, int viewTag, String state) {
16 | super(surfaceId, viewTag);
17 | this.state = state;
18 | }
19 |
20 | @Override
21 | public String getEventName() {
22 | return Name;
23 | }
24 |
25 | @Nullable
26 | protected WritableMap getEventData() {
27 | WritableMap data = Arguments.createMap();
28 | data.putString("state", state);
29 | return data;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/docs/assets/pagerview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/bottom-sheet/docs/assets/pagerview.gif
--------------------------------------------------------------------------------
/packages/bottom-sheet/docs/assets/scrollview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/bottom-sheet/docs/assets/scrollview.gif
--------------------------------------------------------------------------------
/packages/bottom-sheet/docs/assets/struct.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/bottom-sheet/docs/assets/struct.png
--------------------------------------------------------------------------------
/packages/bottom-sheet/ios/BottomSheet/RNBottomSheet.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 |
5 | #import "RNBottomSheetState.h"
6 |
7 | NS_ASSUME_NONNULL_BEGIN
8 |
9 | @interface RNBottomSheet : RCTView
10 |
11 | @property(nonatomic, copy) RCTDirectEventBlock onSlide;
12 | @property(nonatomic, copy) RCTDirectEventBlock onStateChanged;
13 | @property(nonatomic, assign) CGFloat peekHeight;
14 | @property(nonatomic, assign) RNBottomSheetState state;
15 | @property(nonatomic, assign) BOOL draggable;
16 |
17 | - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
18 |
19 | @end
20 |
21 | NS_ASSUME_NONNULL_END
22 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/ios/BottomSheet/RNBottomSheetManager.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNBottomSheetManager : RCTViewManager
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/ios/BottomSheet/RNBottomSheetManager.m:
--------------------------------------------------------------------------------
1 | #import "RNBottomSheetManager.h"
2 | #import "RNBottomSheet.h"
3 | #import "RNBottomSheetState.h"
4 |
5 | @implementation RNBottomSheetManager
6 |
7 | RCT_EXPORT_MODULE(BottomSheet)
8 |
9 | - (UIView *)view {
10 | RNBottomSheet *bottomSheet = [[RNBottomSheet alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
11 | bottomSheet.pointerEvents = RCTPointerEventsBoxNone;
12 | return bottomSheet;
13 | }
14 |
15 | RCT_EXPORT_VIEW_PROPERTY(onSlide, RCTDirectEventBlock)
16 | RCT_EXPORT_VIEW_PROPERTY(onStateChanged, RCTDirectEventBlock)
17 | RCT_EXPORT_VIEW_PROPERTY(peekHeight, CGFloat)
18 | RCT_EXPORT_VIEW_PROPERTY(draggable, BOOL)
19 |
20 | RCT_CUSTOM_VIEW_PROPERTY(state, NSString, RNBottomSheet) {
21 | view.state = RNBottomSheetStateFromString([RCTConvert NSString:json]);
22 | }
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/ios/BottomSheet/RNBottomSheetOffsetChangedEvent.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNBottomSheetOffsetChangedEvent : NSObject
6 |
7 | - (instancetype)initWithViewTag:(NSNumber *)viewTag progress:(CGFloat)progress offset:(CGFloat)offset minY:(CGFloat)minY maxY:(CGFloat)maxY;
8 |
9 | @end
10 |
11 | NS_ASSUME_NONNULL_END
12 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/ios/BottomSheet/RNBottomSheetState.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | typedef NS_ENUM(NSUInteger, RNBottomSheetState) {
6 | RNBottomSheetStateCollapsed = 0,
7 | RNBottomSheetStateExpanded,
8 | RNBottomSheetStateHidden,
9 | RNBottomSheetStateDragging,
10 | RNBottomSheetStateSettling,
11 | };
12 |
13 | RCT_EXTERN RNBottomSheetState RNBottomSheetStateFromString(NSString *state);
14 |
15 | RCT_EXTERN NSString* RNBottomSheetStateToString(RNBottomSheetState state);
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/ios/BottomSheet/RNBottomSheetState.m:
--------------------------------------------------------------------------------
1 | #import "RNBottomSheetState.h"
2 |
3 | RNBottomSheetState RNBottomSheetStateFromString(NSString *state) {
4 | if ([state isEqualToString:@"collapsed"]) {
5 | return RNBottomSheetStateCollapsed;
6 | }
7 | if ([state isEqualToString:@"expanded"]) {
8 | return RNBottomSheetStateExpanded;
9 | }
10 | if ([state isEqualToString:@"settling"]) {
11 | return RNBottomSheetStateSettling;
12 | }
13 | if ([state isEqualToString:@"dragging"]) {
14 | return RNBottomSheetStateDragging;
15 | }
16 | return RNBottomSheetStateHidden;
17 | }
18 |
19 |
20 | NSString* RNBottomSheetStateToString(RNBottomSheetState state) {
21 | if (state == RNBottomSheetStateCollapsed) {
22 | return @"collapsed";
23 | }
24 |
25 | if (state == RNBottomSheetStateExpanded) {
26 | return @"expanded";
27 | }
28 |
29 | if (state == RNBottomSheetStateSettling) {
30 | return @"settling";
31 | }
32 |
33 | if (state == RNBottomSheetStateDragging) {
34 | return @"dragging";
35 | }
36 |
37 | return @"hidden";
38 | }
39 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/ios/BottomSheet/RNBottomSheetStateChangedEvent.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "RNBottomSheetState.h"
4 |
5 | NS_ASSUME_NONNULL_BEGIN
6 |
7 | @interface RNBottomSheetStateChangedEvent : NSObject
8 |
9 | - (instancetype)initWithViewTag:(NSNumber *)viewTag state:(RNBottomSheetState)state;
10 |
11 | @end
12 |
13 | NS_ASSUME_NONNULL_END
14 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sdcx/bottom-sheet",
3 | "description": "A react-native BottomSheet component.",
4 | "version": "0.15.0",
5 | "main": "./dist/index.js",
6 | "typings": "./dist/index.d.ts",
7 | "react-native": "src/index",
8 | "nativePackage": true,
9 | "files": [
10 | "src",
11 | "dist",
12 | "android",
13 | "ios",
14 | "docs",
15 | "RNBottomSheet.podspec",
16 | "!android/build",
17 | "!ios/build",
18 | "!**/__tests__"
19 | ],
20 | "repository": "https://github.com/sdcxtech/react-native-troika",
21 | "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/bottom-sheet/README.md",
22 | "author": "sdcx",
23 | "license": "MIT",
24 | "keywords": [
25 | "react-native",
26 | "bottom-sheet"
27 | ],
28 | "scripts": {
29 | "build": "rm -rf ./dist && tsc -p tsconfig.json",
30 | "prepare": "npm run build",
31 | "tsc": "tsc",
32 | "test": "jest",
33 | "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
34 | },
35 | "peerDependencies": {
36 | "react": ">=16.8",
37 | "react-native": ">=0.60"
38 | },
39 | "devDependencies": {}
40 | }
41 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/packages/bottom-sheet/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./dist",
6 | "noEmit": false,
7 | "strict": true,
8 | "noImplicitAny": true,
9 | "allowImportingTsExtensions": false,
10 | "baseUrl": "./"
11 | },
12 | "include": ["./src/**/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/image-crop/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 listenzz@163.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/image-crop/README.md:
--------------------------------------------------------------------------------
1 | # ImageCropView
2 |
3 | `ImageCropView` 是一个 React Native 原生 UI 组件,用于头像裁剪以及图片裁剪,同时支持图像主体识别后设置需要裁剪的主体区域。
4 |
5 | ## Installation
6 |
7 | ```bash
8 | yarn add @sdcx/image-crop
9 | # &
10 | pod install
11 | ```
12 |
13 | ## Usage
14 |
15 | ```
16 | {}}
22 | objectRect={objectRect}
23 | />
24 | ```
25 | #### cropStyle
26 | 默认default为裁剪矩形,若需要裁剪圆形头像,则设为circular;
27 |
28 | #### objectRect
29 | objectRect可设置默认的图片裁剪区域,且当cropStyle为default时有效。
30 |
31 | objectRect的四个属性分别是(单位都是像素px):
32 | 1. left: 裁剪区域离图片左边的距离;
33 | 2. top: 裁剪区域离图片上边的距离;
34 | 3. width: 裁剪区域的宽度;
35 | 4. height: 裁剪区域的高度;
36 |
37 | #### 裁剪
38 | ```
39 | imageCropViewRef.crop()
40 | ```
41 | 然后裁剪成功后会在onCropped属性中回调裁剪后图片的uri;
42 |
--------------------------------------------------------------------------------
/packages/image-crop/RNImageCrop.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "RNImageCrop"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 |
10 | s.homepage = package["homepage"]
11 | s.license = package["license"]
12 | s.authors = package["author"]
13 | s.platforms = { :ios => "10.0" }
14 | s.source = { :git => "https://github.com/github-account/image-crop.git", :tag => "#{s.version}" }
15 |
16 | s.source_files = "ios/ImageCrop/**/*.{h,m,mm}"
17 | s.dependency "React-Core"
18 | s.dependency "TOCropViewController"
19 | end
20 |
--------------------------------------------------------------------------------
/packages/image-crop/android/build.gradle:
--------------------------------------------------------------------------------
1 | // android/build.gradle
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 |
7 | apply plugin: 'com.android.library'
8 |
9 | android {
10 | compileOptions {
11 | sourceCompatibility JavaVersion.VERSION_1_8
12 | targetCompatibility JavaVersion.VERSION_1_8
13 | }
14 |
15 | compileSdkVersion safeExtGet('compileSdkVersion', 30)
16 | buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
17 |
18 | defaultConfig {
19 | minSdkVersion safeExtGet('minSdkVersion', 21)
20 | targetSdkVersion safeExtGet('targetSdkVersion', 30)
21 | versionCode 1
22 | versionName "1.0.0"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | consumerProguardFiles 'proguard-rules.pro'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation 'com.facebook.react:react-native:+'
35 | implementation 'com.github.yalantis:ucrop:2.2.6'
36 | }
37 |
--------------------------------------------------------------------------------
/packages/image-crop/android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | -dontwarn com.yalantis.ucrop**
2 | -keep class com.yalantis.ucrop** { *; }
3 | -keep interface com.yalantis.ucrop** { *; }
--------------------------------------------------------------------------------
/packages/image-crop/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/image-crop/android/src/main/java/com/reactnative/imagecrop/ImageCropPackage.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.imagecrop;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import java.util.Arrays;
6 | import java.util.Collections;
7 | import java.util.List;
8 |
9 | import com.facebook.react.ReactPackage;
10 | import com.facebook.react.bridge.NativeModule;
11 | import com.facebook.react.bridge.ReactApplicationContext;
12 | import com.facebook.react.uimanager.ViewManager;
13 |
14 | public class ImageCropPackage implements ReactPackage {
15 | @NonNull
16 | @Override
17 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
18 | return Collections.emptyList();
19 | }
20 |
21 | @NonNull
22 | @Override
23 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
24 | return Arrays.asList(new RNImageCropViewManager());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/image-crop/android/src/main/res/layout/rn_crop_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/image-crop/ios/ImageCrop/RNImageCrop.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import "TOCropView.h"
5 |
6 | @interface RNImageCrop : UIView
7 |
8 | /**
9 | RN传递进来的属性值:需要裁剪图片路径
10 | */
11 | @property(nonatomic, copy, nonnull) NSString *fileUri;
12 |
13 | /**
14 | RN传递进来的属性值:裁剪样式,default | circular
15 | */
16 | @property(nonatomic, copy, nullable) NSString *cropStyle;
17 |
18 | /**
19 | RN传递进来的属性值:图像主体Rect,如{"width":208,"left":43,"top":111,"height":354}
20 | */
21 | @property(nonatomic, copy, nullable) id objectRect;
22 |
23 | /**
24 | 选定图片区域后,确认裁剪操作
25 | */
26 | - (void)crop;
27 |
28 | @property(nonatomic, copy, nullable) RCTBubblingEventBlock onCropped;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/packages/image-crop/ios/ImageCrop/RNImageCropManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // RNImageCropManager.h
3 | // RNImageCrop
4 | //
5 | // Created by vibe on 2023/4/28.
6 | //
7 |
8 | #import
9 | #import
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface RNImageCropManager : RCTViewManager
15 |
16 | @end
17 |
18 | NS_ASSUME_NONNULL_END
19 |
--------------------------------------------------------------------------------
/packages/image-crop/ios/ImageCrop/RNImageCropManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // RNCropManager.m
3 | // RNCrop
4 | //
5 | // Created by vibe on 2022/2/8.
6 | //
7 |
8 | #import "RNImageCropManager.h"
9 | #import "RNImageCrop.h"
10 |
11 | @implementation RNImageCropManager
12 |
13 | RCT_EXPORT_MODULE(RNImageCrop)
14 | RCT_EXPORT_VIEW_PROPERTY(fileUri, NSString)
15 | RCT_EXPORT_VIEW_PROPERTY(cropStyle, NSString)
16 | RCT_EXPORT_VIEW_PROPERTY(objectRect, id)
17 | RCT_EXPORT_VIEW_PROPERTY(onCropped, RCTBubblingEventBlock)
18 | RCT_EXPORT_METHOD(crop:(nonnull NSNumber *)reactTag) {
19 | [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) {
20 | RNImageCrop *rnCrop = viewRegistry[reactTag];
21 | if (![rnCrop isKindOfClass:[RNImageCrop class]]) {
22 | RCTLogError(@"Invalid view returned from registry, expecting RNCrop, got: %@", rnCrop);
23 | } else {
24 | dispatch_async(dispatch_get_main_queue(), ^{
25 | RNImageCrop *rnCrop = (RNImageCrop *)viewRegistry[reactTag];
26 | [rnCrop crop];
27 | });
28 | }
29 | }];
30 | }
31 |
32 | - (UIView *)view
33 | {
34 | return [RNImageCrop new];
35 | }
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/packages/image-crop/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sdcx/image-crop",
3 | "description": "A React Native ui component for image crop.",
4 | "version": "0.2.0",
5 | "main": "./dist/index.js",
6 | "typings": "./dist/index.d.ts",
7 | "react-native": "src/index",
8 | "nativePackage": true,
9 | "files": [
10 | "src",
11 | "dist",
12 | "android",
13 | "ios",
14 | "RNImageCrop.podspec",
15 | "!android/build",
16 | "!ios/build",
17 | "!**/__tests__"
18 | ],
19 | "repository": "https://github.com/sdcxtech/react-native-troika",
20 | "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/image-crop#readme",
21 | "author": "sdcx",
22 | "license": "MIT",
23 | "keywords": [
24 | "react-native",
25 | "image-crop"
26 | ],
27 | "scripts": {
28 | "build": "rm -rf ./dist && tsc -p tsconfig.json",
29 | "prepare": "npm run build",
30 | "tsc": "tsc",
31 | "test": "jest",
32 | "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
33 | },
34 | "peerDependencies": {
35 | "react": ">=16.8",
36 | "react-native": ">=0.60"
37 | },
38 | "devDependencies": {}
39 | }
40 |
--------------------------------------------------------------------------------
/packages/image-crop/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/packages/image-crop/src/ImageCropViewRef.ts:
--------------------------------------------------------------------------------
1 | export interface ImageCropViewRef {
2 | crop: () => void;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/image-crop/src/index.ts:
--------------------------------------------------------------------------------
1 | import ImageCropView from './ImageCropView';
2 | import {ImageCropViewRef} from './ImageCropViewRef';
3 | import {ObjectRect} from './typings';
4 |
5 | export {ImageCropView};
6 | export type {ImageCropViewRef, ObjectRect};
7 |
--------------------------------------------------------------------------------
/packages/image-crop/src/typings.ts:
--------------------------------------------------------------------------------
1 | export interface ObjectRect {
2 | top: number;
3 | left: number;
4 | width: number;
5 | height: number;
6 | }
7 |
--------------------------------------------------------------------------------
/packages/image-crop/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./dist",
6 | "noEmit": false,
7 | "strict": true,
8 | "noImplicitAny": true,
9 | "allowImportingTsExtensions": false,
10 | "baseUrl": "./"
11 | },
12 | "include": ["./src/**/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 listenzz@163.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/RNKeyboardInsets.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "RNKeyboardInsets"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 |
10 | s.homepage = package["homepage"]
11 | s.license = package["license"]
12 | s.authors = package["author"]
13 | s.platforms = { :ios => "10.0" }
14 | s.source = { :git => "https://github.com/github-account/keyboard-insets.git", :tag => "#{s.version}" }
15 |
16 | s.source_files = "ios/KeyboardInsets/**/*.{h,m,mm}"
17 | s.dependency "React-Core"
18 | end
--------------------------------------------------------------------------------
/packages/keyboard-insets/android/build.gradle:
--------------------------------------------------------------------------------
1 | // android/build.gradle
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 |
7 | apply plugin: 'com.android.library'
8 |
9 | android {
10 | compileOptions {
11 | sourceCompatibility JavaVersion.VERSION_1_8
12 | targetCompatibility JavaVersion.VERSION_1_8
13 | }
14 |
15 | compileSdkVersion safeExtGet('compileSdkVersion', 30)
16 | buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
17 |
18 | defaultConfig {
19 | minSdkVersion safeExtGet('minSdkVersion', 21)
20 | targetSdkVersion safeExtGet('targetSdkVersion', 30)
21 | versionCode 1
22 | versionName "1.0.0"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | consumerProguardFiles 'proguard-rules.pro'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation 'com.facebook.react:react-native:+'
35 | implementation "androidx.appcompat:appcompat:1.3.1"
36 | }
37 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/android/proguard-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/keyboard-insets/android/proguard-rules.pro
--------------------------------------------------------------------------------
/packages/keyboard-insets/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/android/src/main/java/com/reactnative/keyboardinsets/EdgeInsets.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.keyboardinsets;
2 |
3 | public class EdgeInsets {
4 | public int left;
5 | public int top;
6 | public int right;
7 | public int bottom;
8 |
9 | public EdgeInsets() {
10 |
11 | }
12 |
13 | public EdgeInsets(int left, int top, int right, int bottom) {
14 | this.left = left;
15 | this.top = top;
16 | this.right = right;
17 | this.bottom = bottom;
18 | }
19 |
20 | @Override
21 | public String toString() {
22 | return "EdgeInsets{" +
23 | "left=" + left +
24 | ", top=" + top +
25 | ", right=" + right +
26 | ", bottom=" + bottom +
27 | '}';
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/android/src/main/java/com/reactnative/keyboardinsets/KeyboardInsetsPackage.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.keyboardinsets;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | import com.facebook.react.ReactPackage;
9 | import com.facebook.react.bridge.NativeModule;
10 | import com.facebook.react.bridge.ReactApplicationContext;
11 | import com.facebook.react.uimanager.ViewManager;
12 |
13 | public class KeyboardInsetsPackage implements ReactPackage {
14 | @NonNull
15 | @Override
16 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
17 | return Collections.singletonList(new KeyboardInsetsModule(reactContext));
18 | }
19 |
20 | @NonNull
21 | @Override
22 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
23 | return Collections.singletonList(new KeyboardInsetsViewManager());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/android/src/main/java/com/reactnative/keyboardinsets/KeyboardInsetsView.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.keyboardinsets;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import com.facebook.react.views.view.ReactViewGroup;
7 |
8 | public class KeyboardInsetsView extends ReactViewGroup {
9 |
10 | private String mode = "auto";
11 |
12 | private float extraHeight = 0.0f;
13 |
14 | public KeyboardInsetsView(Context context) {
15 | super(context);
16 | }
17 |
18 | public void setMode(String mode) {
19 | this.mode = mode;
20 | }
21 |
22 | public boolean isAutoMode() {
23 | return this.mode.equals("auto");
24 | }
25 |
26 | public void setExtraHeight(float extraHeight) {
27 | this.extraHeight = extraHeight;
28 | }
29 |
30 | public float getExtraHeight() {
31 | return this.extraHeight;
32 | }
33 |
34 |
35 | @Override
36 | public void requestChildFocus(View child, View focused) {
37 | super.requestChildFocus(child, focused);
38 | requestApplyInsets();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/android/src/main/java/com/reactnative/keyboardinsets/KeyboardPositionChangedEvent.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.keyboardinsets;
2 |
3 | import com.facebook.react.bridge.Arguments;
4 | import com.facebook.react.bridge.WritableMap;
5 | import com.facebook.react.uimanager.PixelUtil;
6 | import com.facebook.react.uimanager.events.Event;
7 | import com.facebook.react.uimanager.events.RCTEventEmitter;
8 |
9 | public class KeyboardPositionChangedEvent extends Event {
10 |
11 | private final int position;
12 |
13 | public KeyboardPositionChangedEvent(int viewId, int position) {
14 | super(viewId);
15 | this.position = position;
16 | }
17 |
18 | @Override
19 | public String getEventName() {
20 | return "topPositionChanged";
21 | }
22 |
23 | @Override
24 | public void dispatch(RCTEventEmitter rctEventEmitter) {
25 | WritableMap map = Arguments.createMap();
26 | map.putDouble("position", PixelUtil.toDIPFromPixel(position));
27 | rctEventEmitter.receiveEvent(getViewTag(), getEventName(), map);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/docs/assets/avoiding.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/keyboard-insets/docs/assets/avoiding.gif
--------------------------------------------------------------------------------
/packages/keyboard-insets/docs/assets/chat.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/keyboard-insets/docs/assets/chat.gif
--------------------------------------------------------------------------------
/packages/keyboard-insets/ios/KeyboardInsets/RNKeyboardAutoHandler.h:
--------------------------------------------------------------------------------
1 | #import "RNKeyboardInsetsView.h"
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNKeyboardAutoHandler : NSObject
6 |
7 | - (instancetype)initWithKeyboardInsetsView:(RNKeyboardInsetsView *)view;
8 |
9 | @end
10 |
11 | NS_ASSUME_NONNULL_END
12 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/ios/KeyboardInsets/RNKeyboardInsetsModule.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNKeyboardInsetsModule : NSObject
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/ios/KeyboardInsets/RNKeyboardInsetsView.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | NS_ASSUME_NONNULL_BEGIN
5 |
6 | @interface RNKeyboardInsetsView : RCTView
7 |
8 | @property(nonatomic, copy) RCTDirectEventBlock onStatusChanged;
9 | @property(nonatomic, copy) RCTDirectEventBlock onPositionChanged;
10 |
11 | @property(nonatomic, copy) NSString *mode;
12 | @property(nonatomic, assign) CGFloat extraHeight;
13 | @property(nonatomic, assign) BOOL explicitly;
14 | @property(nonatomic, strong) RCTEventDispatcher *eventDispatcher;
15 |
16 | - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
17 |
18 | @end
19 |
20 | @protocol RNKeyboardHandler
21 |
22 | - (void)keyboardWillShow:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight;
23 |
24 | - (void)keyboardDidShow:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight;
25 |
26 | - (void)keyboardWillHide:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight;
27 |
28 | - (void)keyboardDidHide:(UIView *)focusView keyboardHeight:(CGFloat)keyboardHeight;
29 |
30 | - (void)handleKeyboardTransition:(CGFloat)position;
31 |
32 | @end
33 |
34 | NS_ASSUME_NONNULL_END
35 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/ios/KeyboardInsets/RNKeyboardInsetsViewManager.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNKeyboardInsetsViewManager : RCTViewManager
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/ios/KeyboardInsets/RNKeyboardInsetsViewManager.m:
--------------------------------------------------------------------------------
1 | #import "RNKeyboardInsetsViewManager.h"
2 | #import "RNKeyboardInsetsView.h"
3 |
4 | @implementation RNKeyboardInsetsViewManager
5 |
6 | RCT_EXPORT_MODULE(KeyboardInsetsView)
7 |
8 | - (UIView *)view {
9 | return [[RNKeyboardInsetsView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
10 | }
11 |
12 | RCT_EXPORT_VIEW_PROPERTY(mode, NSString)
13 | RCT_EXPORT_VIEW_PROPERTY(extraHeight, CGFloat)
14 | RCT_EXPORT_VIEW_PROPERTY(explicitly, BOOL)
15 |
16 | RCT_EXPORT_VIEW_PROPERTY(onStatusChanged, RCTDirectEventBlock)
17 | RCT_EXPORT_VIEW_PROPERTY(onPositionChanged, RCTDirectEventBlock)
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/ios/KeyboardInsets/RNKeyboardManualHandler.h:
--------------------------------------------------------------------------------
1 | #import "RNKeyboardInsetsView.h"
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNKeyboardManualHandler : NSObject
6 |
7 | - (instancetype)initWithKeyboardInsetsView:(RNKeyboardInsetsView *)view;
8 |
9 | @end
10 |
11 | NS_ASSUME_NONNULL_END
12 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/ios/KeyboardInsets/RNKeyboardPositionChangedEvent.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNKeyboardPositionChangedEvent : NSObject
6 |
7 | - (instancetype) initWithReactTag:(NSNumber *)reactTag
8 | position:(NSNumber *)position;
9 |
10 | @end
11 |
12 | NS_ASSUME_NONNULL_END
13 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/ios/KeyboardInsets/RNKeyboardPositionChangedEvent.m:
--------------------------------------------------------------------------------
1 | #import "RNKeyboardPositionChangedEvent.h"
2 |
3 | #import
4 |
5 | @implementation RNKeyboardPositionChangedEvent {
6 | NSNumber* _position;
7 | }
8 |
9 | @synthesize viewTag = _viewTag;
10 |
11 | - (NSString *)eventName {
12 | return @"onPositionChanged";
13 | }
14 |
15 | - (instancetype) initWithReactTag:(NSNumber *)reactTag
16 | position:(NSNumber *)position {
17 | RCTAssertParam(reactTag);
18 | if ((self = [super init])) {
19 | _viewTag = reactTag;
20 | _position = position;
21 | }
22 | return self;
23 | }
24 |
25 | RCT_NOT_IMPLEMENTED(- (instancetype)init)
26 | - (uint16_t)coalescingKey {
27 | return 0;
28 | }
29 |
30 | - (BOOL)canCoalesce {
31 | return YES;
32 | }
33 |
34 | + (NSString *)moduleDotMethod {
35 | return @"RCTEventEmitter.receiveEvent";
36 | }
37 |
38 | - (NSArray *)arguments {
39 | return @[self.viewTag, RCTNormalizeInputEventName(self.eventName), @{
40 | @"position": _position,
41 | }];
42 | }
43 |
44 | - (id)coalesceWithEvent:(id)newEvent {
45 | return newEvent;
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/ios/KeyboardInsets/RNKeyboardStatusChangedEvent.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNKeyboardStatusChangedEvent : NSObject
6 |
7 | - (instancetype) initWithReactTag:(NSNumber *)reactTag
8 | shown:(BOOL)shown
9 | transitioning:(BOOL)transitioning
10 | height:(CGFloat)height;
11 |
12 | @end
13 |
14 | NS_ASSUME_NONNULL_END
15 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sdcx/keyboard-insets",
3 | "description": "A Keyboard Avoiding View for React Native.",
4 | "version": "0.7.0",
5 | "main": "./dist/index.js",
6 | "typings": "./dist/index.d.ts",
7 | "react-native": "src/index",
8 | "nativePackage": true,
9 | "files": [
10 | "src",
11 | "dist",
12 | "android",
13 | "ios",
14 | "RNKeyboardInsets.podspec",
15 | "!android/build",
16 | "!ios/build",
17 | "!**/__tests__"
18 | ],
19 | "repository": "https://github.com/sdcxtech/react-native-troika",
20 | "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/keyboard-insets#readme",
21 | "author": "sdcx",
22 | "license": "MIT",
23 | "keywords": [
24 | "react-native",
25 | "keyboard-aware",
26 | "keyboard-advoiding",
27 | "keyboard-tool",
28 | "keyboard-insets"
29 | ],
30 | "scripts": {
31 | "build": "rm -rf ./dist && tsc -p tsconfig.json",
32 | "prepare": "npm run build",
33 | "tsc": "tsc",
34 | "test": "jest",
35 | "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
36 | },
37 | "peerDependencies": {
38 | "react": ">=16.8",
39 | "react-native": ">=0.60"
40 | },
41 | "devDependencies": {}
42 | }
43 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/src/hook.ts:
--------------------------------------------------------------------------------
1 | import {useCallback, useState} from 'react';
2 | import {Animated} from 'react-native';
3 |
4 | interface KeyboardState {
5 | height: number;
6 | shown: boolean;
7 | transitioning: boolean;
8 | position: Animated.Value;
9 | }
10 |
11 | export function useKeyboard() {
12 | const [keyboard, setKeyboard] = useState({
13 | height: 0,
14 | shown: false,
15 | transitioning: false,
16 | position: new Animated.Value(0),
17 | });
18 |
19 | const onKeyboard = useCallback((state: KeyboardState) => {
20 | setKeyboard(state);
21 | }, []);
22 |
23 | return {keyboard, onKeyboard};
24 | }
25 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/src/index.ts:
--------------------------------------------------------------------------------
1 | import {KeyboardInsetsView, KeyboardState} from './KeyboardInsetsView';
2 | import {
3 | getEdgeInsetsForView,
4 | NativeKeyboardInsetsView,
5 | KeyboardStatusChangedEventData,
6 | KeyboardStatusChangedEvent,
7 | KeyboardPositionChangedEventData,
8 | KeyboardPositionChangedEvent,
9 | } from './native';
10 | import {useKeyboard} from './hook';
11 |
12 | export {KeyboardInsetsView, NativeKeyboardInsetsView, useKeyboard, getEdgeInsetsForView};
13 | export type {
14 | KeyboardState,
15 | KeyboardStatusChangedEventData,
16 | KeyboardStatusChangedEvent,
17 | KeyboardPositionChangedEventData,
18 | KeyboardPositionChangedEvent,
19 | };
20 |
--------------------------------------------------------------------------------
/packages/keyboard-insets/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./dist",
6 | "noEmit": false,
7 | "strict": true,
8 | "noImplicitAny": true,
9 | "allowImportingTsExtensions": false,
10 | "baseUrl": "./"
11 | },
12 | "include": ["./src/**/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/modal-edge-to-edge/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 listenzz@163.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/modal-edge-to-edge/README.md:
--------------------------------------------------------------------------------
1 | # modal-edge-to-edge
2 |
3 | 一个小玩意,使得 React Native Modal 在 Android 支持 Edge-to-Edge 模式。
4 |
5 | 仅支持 RN0.74 及以下版本,因为 RN0.75 版本开始,用 kotlin 来实现 Modal 了,无法继承。
6 |
--------------------------------------------------------------------------------
/packages/modal-edge-to-edge/android/build.gradle:
--------------------------------------------------------------------------------
1 | // android/build.gradle
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 |
7 | apply plugin: 'com.android.library'
8 |
9 | android {
10 | compileOptions {
11 | sourceCompatibility JavaVersion.VERSION_1_8
12 | targetCompatibility JavaVersion.VERSION_1_8
13 | }
14 |
15 | compileSdkVersion safeExtGet('compileSdkVersion', 33)
16 | buildToolsVersion safeExtGet('buildToolsVersion', '33.0.0')
17 |
18 | defaultConfig {
19 | minSdkVersion safeExtGet('minSdkVersion', 21)
20 | targetSdkVersion safeExtGet('targetSdkVersion', 33)
21 | versionCode 1
22 | versionName "1.0.0"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | consumerProguardFiles 'proguard-rules.pro'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation 'com.facebook.react:react-native:+'
35 | implementation "androidx.appcompat:appcompat:1.3.1"
36 | }
37 |
--------------------------------------------------------------------------------
/packages/modal-edge-to-edge/android/proguard-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/modal-edge-to-edge/android/proguard-rules.pro
--------------------------------------------------------------------------------
/packages/modal-edge-to-edge/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/modal-edge-to-edge/android/src/main/java/com/reactnative/modalx/EdgeToEdgePackage.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.modalx;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.facebook.react.ReactPackage;
6 | import com.facebook.react.bridge.NativeModule;
7 | import com.facebook.react.bridge.ReactApplicationContext;
8 | import com.facebook.react.uimanager.ViewManager;
9 |
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | public class EdgeToEdgePackage implements ReactPackage {
14 | @NonNull
15 | @Override
16 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
17 | return Collections.singletonList(new EdgeToEdgeModule(reactContext));
18 | }
19 |
20 | @NonNull
21 | @Override
22 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
23 | return Collections.emptyList();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/modal-edge-to-edge/android/src/main/java/com/reactnative/modalx/ModalHostManager.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.modalx;
2 |
3 | import com.facebook.react.module.annotations.ReactModule;
4 | import com.facebook.react.uimanager.ThemedReactContext;
5 | import com.facebook.react.views.modal.ReactModalHostManager;
6 | import com.facebook.react.views.modal.ReactModalHostView;
7 |
8 | @ReactModule(name = ReactModalHostManager.REACT_CLASS)
9 | public class ModalHostManager extends ReactModalHostManager {
10 | @Override
11 | public boolean canOverrideExistingModule() {
12 | return true;
13 | }
14 |
15 | @Override
16 | protected ReactModalHostView createViewInstance(ThemedReactContext reactContext) {
17 | return new ModalHostView(reactContext);
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/packages/modal-edge-to-edge/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sdcx/modal-edge-to-edge",
3 | "description": "TODO",
4 | "version": "0.5.0",
5 | "main": "./dist/index.js",
6 | "typings": "./dist/index.d.ts",
7 | "react-native": "src/index",
8 | "nativePackage": true,
9 | "files": [
10 | "src",
11 | "dist",
12 | "android",
13 | "!android/build",
14 | "!**/__tests__"
15 | ],
16 | "repository": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/modal-edge-to-edge",
17 | "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/modal-edge-to-edge#readme",
18 | "author": "sdcx",
19 | "license": "MIT",
20 | "keywords": [
21 | "react-native"
22 | ],
23 | "scripts": {
24 | "build": "rm -rf ./dist && tsc -p tsconfig.json",
25 | "prepare": "npm run build",
26 | "tsc": "tsc",
27 | "test": "jest",
28 | "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
29 | },
30 | "peerDependencies": {
31 | "react": ">=16.8",
32 | "react-native": ">=0.60"
33 | },
34 | "devDependencies": {}
35 | }
36 |
--------------------------------------------------------------------------------
/packages/modal-edge-to-edge/src/index.ts:
--------------------------------------------------------------------------------
1 | import {NativeModules, Platform} from 'react-native';
2 |
3 | const EdgeToEdgeModule: SystemUIType = NativeModules.EdgeToEdgeModule;
4 |
5 | interface SystemUIType {
6 | setNavigationBarColor(color: string): void;
7 | setNavigationBarStyle(style: 'dark' | 'light'): void;
8 | }
9 |
10 | const SystemUI: SystemUIType = {
11 | setNavigationBarStyle: (style: 'dark' | 'light') => {
12 | if (Platform.OS === 'android') {
13 | EdgeToEdgeModule.setNavigationBarStyle(style);
14 | }
15 | },
16 | setNavigationBarColor: (color: string) => {
17 | if (Platform.OS === 'android') {
18 | EdgeToEdgeModule.setNavigationBarColor(color);
19 | }
20 | },
21 | };
22 |
23 | export default SystemUI;
24 |
--------------------------------------------------------------------------------
/packages/modal-edge-to-edge/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./dist",
6 | "noEmit": false,
7 | "strict": true,
8 | "noImplicitAny": true,
9 | "allowImportingTsExtensions": false,
10 | "baseUrl": "./"
11 | },
12 | "include": ["./src/**/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/nested-scroll-webview/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 listenzz@163.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/nested-scroll-webview/android/build.gradle:
--------------------------------------------------------------------------------
1 | // android/build.gradle
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 |
7 | apply plugin: 'com.android.library'
8 |
9 | android {
10 | compileOptions {
11 | sourceCompatibility JavaVersion.VERSION_1_8
12 | targetCompatibility JavaVersion.VERSION_1_8
13 | }
14 |
15 | compileSdkVersion safeExtGet('compileSdkVersion', 30)
16 | buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
17 |
18 | defaultConfig {
19 | minSdkVersion safeExtGet('minSdkVersion', 21)
20 | targetSdkVersion safeExtGet('targetSdkVersion', 30)
21 | versionCode 1
22 | versionName "1.0.0"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | consumerProguardFiles 'proguard-rules.pro'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation 'com.facebook.react:react-native:+'
35 | implementation project(':react-native-webview')
36 | }
37 |
--------------------------------------------------------------------------------
/packages/nested-scroll-webview/android/proguard-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/nested-scroll-webview/android/proguard-rules.pro
--------------------------------------------------------------------------------
/packages/nested-scroll-webview/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/nested-scroll-webview/android/src/main/java/com/reactnative/nestedscrollwebview/NestedScrollWebViewPackage.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.nestedscrollwebview;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.facebook.react.ReactPackage;
6 | import com.facebook.react.bridge.NativeModule;
7 | import com.facebook.react.bridge.ReactApplicationContext;
8 | import com.facebook.react.uimanager.ViewManager;
9 |
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | public class NestedScrollWebViewPackage implements ReactPackage {
14 | @NonNull
15 | @Override
16 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
17 | return Collections.emptyList();
18 | }
19 |
20 | @NonNull
21 | @Override
22 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
23 | return Collections.emptyList();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/nested-scroll-webview/android/src/main/java/com/reactnativecommunity/webview/RNCNestedScrollWebViewManager.java:
--------------------------------------------------------------------------------
1 | package com.reactnativecommunity.webview;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.facebook.react.module.annotations.ReactModule;
6 | import com.facebook.react.uimanager.ThemedReactContext;
7 |
8 | @ReactModule(name = RNCWebViewManagerImpl.NAME)
9 | public class RNCNestedScrollWebViewManager extends RNCWebViewManager {
10 |
11 | @NonNull
12 | @Override
13 | public RNCWebViewWrapper createViewInstance(ThemedReactContext context) {
14 | return createViewInstance(context, new RNCNestedScrollWebView(context));
15 | }
16 |
17 | @Override
18 | public boolean canOverrideExistingModule() {
19 | return true;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/nested-scroll-webview/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sdcx/nested-scroll-webview",
3 | "description": "A React Native Webview that supports nested scrolling.",
4 | "version": "0.4.0",
5 | "main": "./dist/index.js",
6 | "typings": "./dist/index.d.ts",
7 | "react-native": "src/index",
8 | "nativePackage": true,
9 | "files": [
10 | "src",
11 | "dist",
12 | "android",
13 | "!android/build",
14 | "!**/__tests__"
15 | ],
16 | "repository": "https://github.com/sdcxtech/react-native-troika",
17 | "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/nested-scroll-webview/README.md",
18 | "author": "sdcx",
19 | "license": "MIT",
20 | "keywords": [
21 | "react-native",
22 | "nested-scroll",
23 | "webview"
24 | ],
25 | "scripts": {
26 | "build": "rm -rf ./dist && tsc -p tsconfig.json",
27 | "prepare": "npm run build",
28 | "tsc": "tsc",
29 | "test": "jest",
30 | "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
31 | },
32 | "peerDependencies": {
33 | "react": ">=16.8",
34 | "react-native": ">=0.60",
35 | "react-native-webview": ">=11.16.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/nested-scroll-webview/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/packages/nested-scroll-webview/src/index.ts:
--------------------------------------------------------------------------------
1 | import {NativeModules} from 'react-native';
2 |
3 | const {RNNestedScrollWebView} = NativeModules;
4 |
5 | export default RNNestedScrollWebView;
6 |
7 | export function lib(a: number, b: number) {
8 | return a + b + 2;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/nested-scroll-webview/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./dist",
6 | "noEmit": false,
7 | "strict": true,
8 | "noImplicitAny": true,
9 | "allowImportingTsExtensions": false,
10 | "baseUrl": "./"
11 | },
12 | "include": ["./src/**/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/nested-scroll/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 listenzz@163.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/nested-scroll/RNNestedScrollView.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "RNNestedScrollView"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 |
10 | s.homepage = package["homepage"]
11 | s.license = package["license"]
12 | s.authors = package["author"]
13 | s.platforms = { :ios => "10.0" }
14 | s.source = { :git => "https://github.com/github-account/nested-scroll.git", :tag => "#{s.version}" }
15 |
16 | s.source_files = "ios/NestedScrollView/**/*.{h,m,mm}"
17 | s.dependency "React-Core"
18 | end
--------------------------------------------------------------------------------
/packages/nested-scroll/android/build.gradle:
--------------------------------------------------------------------------------
1 | // android/build.gradle
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 |
7 | apply plugin: 'com.android.library'
8 |
9 | android {
10 | compileOptions {
11 | sourceCompatibility JavaVersion.VERSION_1_8
12 | targetCompatibility JavaVersion.VERSION_1_8
13 | }
14 |
15 | compileSdkVersion safeExtGet('compileSdkVersion', 30)
16 | buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
17 |
18 | defaultConfig {
19 | minSdkVersion safeExtGet('minSdkVersion', 21)
20 | targetSdkVersion safeExtGet('targetSdkVersion', 30)
21 | versionCode 1
22 | versionName "1.0.0"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | consumerProguardFiles 'proguard-rules.pro'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation 'com.facebook.react:react-native:+'
35 | }
36 |
--------------------------------------------------------------------------------
/packages/nested-scroll/android/proguard-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/nested-scroll/android/proguard-rules.pro
--------------------------------------------------------------------------------
/packages/nested-scroll/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/nested-scroll/android/src/main/java/com/reactnative/nestedscroll/NestedScrollViewLocalData.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.nestedscroll;
2 |
3 | public class NestedScrollViewLocalData {
4 | float headerNodeH;
5 | float contentNodeH;
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/packages/nested-scroll/android/src/main/java/com/reactnative/nestedscroll/NestedScrollViewPackage.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.nestedscroll;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.facebook.react.ReactPackage;
6 | import com.facebook.react.bridge.NativeModule;
7 | import com.facebook.react.bridge.ReactApplicationContext;
8 | import com.facebook.react.uimanager.ViewManager;
9 |
10 | import java.util.Arrays;
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | public class NestedScrollViewPackage implements ReactPackage {
15 | @NonNull
16 | @Override
17 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
18 | return Collections.emptyList();
19 | }
20 |
21 | @NonNull
22 | @Override
23 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
24 | return Arrays.asList(new NestedScrollViewHeaderManager(), new NestedScrollViewManager());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/nested-scroll/android/src/main/java/com/reactnative/nestedscroll/NestedViewHeaderScrollEvent.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.nestedscroll;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import com.facebook.react.bridge.Arguments;
6 | import com.facebook.react.bridge.WritableMap;
7 | import com.facebook.react.uimanager.PixelUtil;
8 | import com.facebook.react.uimanager.events.Event;
9 |
10 | public class NestedViewHeaderScrollEvent extends Event {
11 | public static final String Name = "scrollEvent";
12 | public static final String JSEventName = "onScroll";
13 | private final float y;
14 |
15 | public NestedViewHeaderScrollEvent(int surfaceId, int viewTag, int offset) {
16 | super(surfaceId, viewTag);
17 | this.y = PixelUtil.toDIPFromPixel(offset);
18 | }
19 |
20 | @Override
21 | public String getEventName() {
22 | return Name;
23 | }
24 |
25 | @Nullable
26 | protected WritableMap getEventData() {
27 | WritableMap event = Arguments.createMap();
28 | WritableMap offset = Arguments.createMap();
29 | offset.putDouble("y", y);
30 | offset.putDouble("x", 0);
31 | event.putMap("contentOffset", offset);
32 | return event;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/nested-scroll/docs/assets/parallax.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/nested-scroll/docs/assets/parallax.gif
--------------------------------------------------------------------------------
/packages/nested-scroll/docs/assets/sticky.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/nested-scroll/docs/assets/sticky.gif
--------------------------------------------------------------------------------
/packages/nested-scroll/docs/assets/struct.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/nested-scroll/docs/assets/struct.png
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollEvent.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNNestedScrollEvent : NSObject
6 |
7 | - (instancetype) initWithReactTag:(NSNumber *)reactTag
8 | offset:(CGPoint)offset;
9 |
10 | @end
11 |
12 | NS_ASSUME_NONNULL_END
13 |
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollEvent.m:
--------------------------------------------------------------------------------
1 | #import "RNNestedScrollEvent.h"
2 |
3 | #import
4 |
5 | @implementation RNNestedScrollEvent {
6 | CGPoint _offset;
7 | }
8 |
9 | @synthesize viewTag = _viewTag;
10 |
11 | - (NSString *)eventName {
12 | return @"onScroll";
13 | }
14 |
15 | - (instancetype) initWithReactTag:(NSNumber *)reactTag
16 | offset:(CGPoint)offset {
17 | RCTAssertParam(reactTag);
18 | if ((self = [super init])) {
19 | _viewTag = reactTag;
20 | _offset = offset;
21 | }
22 | return self;
23 | }
24 |
25 | RCT_NOT_IMPLEMENTED(- (instancetype)init)
26 | - (uint16_t)coalescingKey {
27 | return 0;
28 | }
29 |
30 | - (BOOL)canCoalesce {
31 | return YES;
32 | }
33 |
34 | + (NSString *)moduleDotMethod {
35 | return @"RCTEventEmitter.receiveEvent";
36 | }
37 |
38 | - (NSArray *)arguments {
39 | return @[self.viewTag, RCTNormalizeInputEventName(self.eventName), @{
40 | @"contentOffset": @{
41 | @"y": @(_offset.y),
42 | @"x": @(_offset.x)
43 | }
44 | }];
45 | }
46 |
47 | - (id)coalesceWithEvent:(id)newEvent {
48 | return newEvent;
49 | }
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollShadowView.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNNestedScrollShadowView : RCTShadowView
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollShadowView.m:
--------------------------------------------------------------------------------
1 | #import "RNNestedScrollShadowView.h"
2 | #import "RNNestedScrollViewLocalData.h"
3 |
4 | #import
5 |
6 | @implementation RNNestedScrollShadowView
7 |
8 | - (void)setLocalData:(RNNestedScrollViewLocalData *)localData {
9 | CGSize size = localData.scrollingChildSize;
10 | NSUInteger count = self.reactSubviews.count;
11 |
12 | RCTAssert(count <= 2, @"`NestedScrollView` can have at most two child component.");
13 |
14 | for (NSUInteger i = 0; i < count; i ++) {
15 | if (i == 1) {
16 | RCTShadowView *shadow = self.reactSubviews[i];
17 | [shadow setSize:size];
18 | [shadow setMinHeight:(YGValue){size.height, YGUnitPoint}];
19 | }
20 | }
21 | }
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollView.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | NS_ASSUME_NONNULL_BEGIN
5 |
6 | @interface RNNestedScrollView : UIView
7 |
8 | @property(nonatomic, assign) BOOL bounces;
9 |
10 | - (instancetype)initWithBridge:(RCTBridge *)bridge;
11 |
12 | - (void)updateContentSizeIfNeeded;
13 |
14 | @end
15 |
16 | NS_ASSUME_NONNULL_END
17 |
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollViewHeader.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | NS_ASSUME_NONNULL_BEGIN
5 |
6 | @interface RNNestedScrollViewHeader : RCTView
7 |
8 | @property(nonatomic, assign) CGFloat stickyHeight;
9 | @property(nonatomic, assign) NSUInteger stickyHeaderBeginIndex;
10 | @property(nonatomic, copy) RCTDirectEventBlock onScroll;
11 | @property(nonatomic, strong) RCTEventDispatcher *eventDispatcher;
12 |
13 | - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
14 |
15 | - (CGFloat)maxScrollRange;
16 |
17 | @end
18 |
19 | NS_ASSUME_NONNULL_END
20 |
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollViewHeaderManager.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNNestedScrollViewHeaderManager : RCTViewManager
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollViewHeaderManager.m:
--------------------------------------------------------------------------------
1 | #import "RNNestedScrollViewHeaderManager.h"
2 | #import "RNNestedScrollViewHeader.h"
3 |
4 | #import
5 |
6 | @implementation RNNestedScrollViewHeaderManager
7 |
8 | RCT_EXPORT_MODULE(NestedScrollViewHeader)
9 |
10 | - (UIView *)view {
11 | return [[RNNestedScrollViewHeader alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
12 | }
13 |
14 | RCT_CUSTOM_VIEW_PROPERTY(stickyHeight, CGFloat, RNNestedScrollViewHeader) {
15 | if (json) {
16 | [view setStickyHeight:[RCTConvert CGFloat:json]];
17 | } else {
18 | [view setStickyHeight:-1];
19 | }
20 | }
21 |
22 | RCT_CUSTOM_VIEW_PROPERTY(stickyHeaderBeginIndex, NSUInteger, RNNestedScrollViewHeader) {
23 | if (json) {
24 | [view setStickyHeaderBeginIndex:[RCTConvert NSUInteger:json]];
25 | } else {
26 | [view setStickyHeaderBeginIndex:NSUIntegerMax];
27 | }
28 | }
29 |
30 |
31 | RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollViewLocalData.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNNestedScrollViewLocalData : NSObject
6 |
7 | - (instancetype)initWithSize:(CGSize)size;
8 |
9 | @property(nonatomic, assign) CGSize scrollingChildSize;
10 |
11 | @end
12 |
13 | NS_ASSUME_NONNULL_END
14 |
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollViewLocalData.m:
--------------------------------------------------------------------------------
1 | #import "RNNestedScrollViewLocalData.h"
2 |
3 | @implementation RNNestedScrollViewLocalData
4 |
5 | - (instancetype)initWithSize:(CGSize)size {
6 | if (self = [super init]) {
7 | _scrollingChildSize = size;
8 | }
9 | return self;
10 | }
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollViewManager.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNNestedScrollViewManager : RCTViewManager
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/nested-scroll/ios/NestedScrollView/RNNestedScrollViewManager.m:
--------------------------------------------------------------------------------
1 | #import "RNNestedScrollViewManager.h"
2 | #import "RNNestedScrollView.h"
3 | #import "RNNestedScrollShadowView.h"
4 |
5 | @implementation RNNestedScrollViewManager
6 |
7 | RCT_EXPORT_MODULE(NestedScrollView)
8 |
9 | - (UIView *)view {
10 | return [[RNNestedScrollView alloc] initWithBridge:self.bridge];
11 | }
12 |
13 | - (RCTShadowView *)shadowView {
14 | return [RNNestedScrollShadowView new];
15 | }
16 |
17 | RCT_EXPORT_VIEW_PROPERTY(bounces, BOOL)
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/packages/nested-scroll/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sdcx/nested-scroll",
3 | "description": "A react-native NestedScrollView component.",
4 | "version": "0.14.0",
5 | "main": "./dist/index.js",
6 | "typings": "./dist/index.d.ts",
7 | "react-native": "src/index",
8 | "nativePackage": true,
9 | "files": [
10 | "src",
11 | "dist",
12 | "android",
13 | "ios",
14 | "RNNestedScrollView.podspec",
15 | "!android/build",
16 | "!ios/build",
17 | "!**/__tests__"
18 | ],
19 | "repository": "https://github.com/sdcxtech/react-native-troika",
20 | "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/nested-scroll/README.md",
21 | "author": "sdcx",
22 | "license": "MIT",
23 | "keywords": [
24 | "react-native",
25 | "nested-scroll"
26 | ],
27 | "scripts": {
28 | "build": "rm -rf ./dist && tsc -p tsconfig.json",
29 | "prepare": "npm run build",
30 | "tsc": "tsc",
31 | "test": "jest",
32 | "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
33 | },
34 | "peerDependencies": {
35 | "react": ">=16.8",
36 | "react-native": ">=0.60"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/nested-scroll/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/packages/nested-scroll/src/NestedScrollViewHeader/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | NativeScrollPoint,
4 | NativeSyntheticEvent,
5 | requireNativeComponent,
6 | ViewProps,
7 | } from 'react-native';
8 |
9 | interface NestedScrollEventData {
10 | contentOffset: NativeScrollPoint;
11 | }
12 |
13 | export type NestedScrollEvent = NativeSyntheticEvent;
14 |
15 | export interface NestedScrollViewHeaderProps extends ViewProps {
16 | stickyHeight?: number;
17 | stickyHeaderBeginIndex?: number;
18 | onScroll?: (event: NestedScrollEvent) => void;
19 | }
20 |
21 | const NativeNestedScrollViewHeader =
22 | requireNativeComponent('NestedScrollViewHeader');
23 |
24 | type NativeNestedScrollViewHeaderInstance = InstanceType;
25 |
26 | const NestedScrollViewHeader = React.forwardRef<
27 | NativeNestedScrollViewHeaderInstance,
28 | NestedScrollViewHeaderProps
29 | >((props, ref) => {
30 | return ;
31 | });
32 |
33 | export {NestedScrollViewHeader};
34 |
--------------------------------------------------------------------------------
/packages/nested-scroll/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./dist",
6 | "noEmit": false,
7 | "strict": true,
8 | "noImplicitAny": true,
9 | "allowImportingTsExtensions": false,
10 | "baseUrl": "./"
11 | },
12 | "include": ["./src/**/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/overlay/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 listenzz@163.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/overlay/README.md:
--------------------------------------------------------------------------------
1 | # Overlay
2 |
3 | `Overlay` 是一个 React Native 原生 UI 基础设施,它漂浮在你的 React Native 应用之上,可用于实现 Modal, Alert, Toast, Popover, Notification, Hoverball 等顶层 UI。
4 |
5 | > 这个库属于实验性质,请谨慎使用
6 |
7 | ## Installation
8 |
9 | ```bash
10 | yarn add @sdcx/overlay
11 | # &
12 | pod install
13 | ```
14 |
15 | 在你的项目更目录下添加或修改 react-native.config.js 文件,内容如下:
16 |
17 | ```js
18 | // react-native.config.js
19 | module.exports = {
20 | dependencies: {
21 | '@sdcx/overlay': {
22 | platforms: {
23 | android: {
24 | packageInstance: 'new OverlayPackage(getReactNativeHost())',
25 | },
26 | },
27 | },
28 | },
29 | }
30 | ```
31 |
32 | ## Usage
33 |
34 | 请查看 Hoverball 示例代码
35 |
--------------------------------------------------------------------------------
/packages/overlay/RNOverlay.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "RNOverlay"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 |
10 | s.homepage = package["homepage"]
11 | s.license = package["license"]
12 | s.authors = package["author"]
13 | s.platforms = { :ios => "11.0" }
14 | s.source = { :git => "https://github.com/github-account/overlay.git", :tag => "#{s.version}" }
15 |
16 | s.source_files = "ios/Overlay/**/*.{h,m,mm}"
17 | s.dependency "React-Core"
18 | end
--------------------------------------------------------------------------------
/packages/overlay/android/build.gradle:
--------------------------------------------------------------------------------
1 | // android/build.gradle
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 |
7 | apply plugin: 'com.android.library'
8 |
9 | android {
10 | compileOptions {
11 | sourceCompatibility JavaVersion.VERSION_1_8
12 | targetCompatibility JavaVersion.VERSION_1_8
13 | }
14 |
15 | compileSdkVersion safeExtGet('compileSdkVersion', 30)
16 | buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
17 |
18 | defaultConfig {
19 | minSdkVersion safeExtGet('minSdkVersion', 21)
20 | targetSdkVersion safeExtGet('targetSdkVersion', 30)
21 | versionCode 1
22 | versionName "1.0.0"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | consumerProguardFiles 'proguard-rules.pro'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation 'com.facebook.react:react-native:+'
35 | implementation "androidx.appcompat:appcompat:1.3.1"
36 | }
37 |
--------------------------------------------------------------------------------
/packages/overlay/android/proguard-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/overlay/android/proguard-rules.pro
--------------------------------------------------------------------------------
/packages/overlay/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/overlay/android/src/main/java/com/reactnative/overlay/OverlayPackage.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.overlay;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.facebook.react.ReactInstanceManager;
6 | import com.facebook.react.ReactNativeHost;
7 | import com.facebook.react.ReactPackage;
8 | import com.facebook.react.bridge.NativeModule;
9 | import com.facebook.react.bridge.ReactApplicationContext;
10 | import com.facebook.react.uimanager.ViewManager;
11 |
12 | import java.util.Collections;
13 | import java.util.List;
14 |
15 | public class OverlayPackage implements ReactPackage {
16 |
17 | private final ReactNativeHost reactNativeHost;
18 |
19 | public OverlayPackage(ReactNativeHost reactNativeHost) {
20 | this.reactNativeHost = reactNativeHost;
21 | }
22 |
23 | @NonNull
24 | @Override
25 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
26 | return Collections.singletonList(new OverlayModule(reactContext, reactNativeHost));
27 | }
28 |
29 | @NonNull
30 | @Override
31 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
32 | return Collections.emptyList();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/overlay/ios/Overlay/RNOverlay.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface RNOverlay : NSObject
5 |
6 | - (instancetype)initWithModuleName:(NSString *)moduleName bridge:(RCTBridge *)bridge;
7 |
8 | - (void)show:(NSDictionary *)props options:(NSDictionary *)options;
9 |
10 | - (void)hide;
11 |
12 | - (void)update;
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/packages/overlay/ios/Overlay/RNOverlayModule.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | NS_ASSUME_NONNULL_BEGIN
5 |
6 | @interface RNOverlayModule : NSObject
7 |
8 | @end
9 |
10 | NS_ASSUME_NONNULL_END
11 |
--------------------------------------------------------------------------------
/packages/overlay/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sdcx/overlay",
3 | "description": "A react-native Overlay component.",
4 | "version": "0.5.0",
5 | "main": "./dist/index.js",
6 | "typings": "./dist/index.d.ts",
7 | "react-native": "src/index",
8 | "nativePackage": true,
9 | "files": [
10 | "src",
11 | "dist",
12 | "android",
13 | "ios",
14 | "RNOverlay.podspec",
15 | "!android/build",
16 | "!ios/build",
17 | "!**/__tests__"
18 | ],
19 | "repository": "https://github.com/sdcxtech/react-native-troika",
20 | "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/overlay#readme",
21 | "author": "sdcx",
22 | "license": "MIT",
23 | "keywords": [
24 | "react-native",
25 | "overlay"
26 | ],
27 | "scripts": {
28 | "build": "rm -rf ./dist && tsc -p tsconfig.json",
29 | "prepare": "npm run build",
30 | "tsc": "tsc",
31 | "test": "jest",
32 | "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
33 | },
34 | "peerDependencies": {
35 | "react": ">=16.8",
36 | "react-native": ">=0.60"
37 | },
38 | "devDependencies": {}
39 | }
40 |
--------------------------------------------------------------------------------
/packages/overlay/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | dependency: {
3 | platforms: {
4 | android: {
5 | packageInstance: 'new OverlayPackage(getReactNativeHost())',
6 | },
7 | },
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/packages/overlay/src/index.ts:
--------------------------------------------------------------------------------
1 | import {Insets, NativeModule, NativeModules} from 'react-native';
2 |
3 | export interface OverlayOptions {
4 | id: number;
5 | passThroughTouches?: boolean;
6 | }
7 |
8 | export interface OverlayProps extends OverlayOptions {
9 | insets: Insets;
10 | }
11 |
12 | interface OverlayInterface extends NativeModule {
13 | show(moduleName: string, options: OverlayOptions): void;
14 | hide(moduleName: string, id: number): void;
15 | }
16 |
17 | const OverlayHost: OverlayInterface = NativeModules.OverlayHost;
18 |
19 | function show(moduleName: string, options: OverlayOptions) {
20 | OverlayHost.show(moduleName, options);
21 | }
22 |
23 | function hide(moduleName: string, id: number) {
24 | OverlayHost.hide(moduleName, id);
25 | }
26 |
27 | const Overlay = {show, hide};
28 |
29 | export {Overlay};
30 |
--------------------------------------------------------------------------------
/packages/overlay/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./dist",
6 | "noEmit": false,
7 | "strict": true,
8 | "noImplicitAny": true,
9 | "allowImportingTsExtensions": false,
10 | "baseUrl": "./"
11 | },
12 | "include": ["./src/**/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 listenzz@163.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/RNPullToRefresh.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "RNPullToRefresh"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 |
10 | s.homepage = package["homepage"]
11 | s.license = package["license"]
12 | s.authors = package["author"]
13 | s.platforms = { :ios => "10.0" }
14 | s.source = { :git => "https://github.com/github-account/pull-to-refresh.git", :tag => "#{s.version}" }
15 |
16 | s.source_files = "ios/PullToRefresh/**/*.{h,m,mm}"
17 | s.dependency "React-Core"
18 | end
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/build.gradle:
--------------------------------------------------------------------------------
1 | // android/build.gradle
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 |
7 | apply plugin: 'com.android.library'
8 |
9 | android {
10 | compileOptions {
11 | sourceCompatibility JavaVersion.VERSION_1_8
12 | targetCompatibility JavaVersion.VERSION_1_8
13 | }
14 |
15 | compileSdkVersion safeExtGet('compileSdkVersion', 30)
16 | buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
17 |
18 | defaultConfig {
19 | minSdkVersion safeExtGet('minSdkVersion', 21)
20 | targetSdkVersion safeExtGet('targetSdkVersion', 30)
21 | versionCode 1
22 | versionName "1.0.0"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | consumerProguardFiles 'proguard-rules.pro'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation 'com.facebook.react:react-native:+'
35 | implementation 'androidx.appcompat:appcompat:1.3.1'
36 | implementation 'io.github.scwang90:refresh-layout-kernel:3.0.0-alpha'
37 | }
38 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | -keepnames class androidx.viewpager2.widget.ViewPager2*
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/src/main/java/com/reactnative/pulltorefresh/OnRefreshChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.pulltorefresh;
2 |
3 | public interface OnRefreshChangeListener {
4 | void onRefresh();
5 |
6 | void onOffsetChange(int offset);
7 |
8 | void onStateChanged(PullToRefreshState state);
9 | }
10 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/src/main/java/com/reactnative/pulltorefresh/PullToRefreshFooterLocalData.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.pulltorefresh;
2 |
3 | import android.graphics.Rect;
4 |
5 | public class PullToRefreshFooterLocalData {
6 | Rect viewRect = new Rect();
7 | }
8 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/src/main/java/com/reactnative/pulltorefresh/PullToRefreshFooterShadowNode.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.pulltorefresh;
2 |
3 | import com.facebook.react.uimanager.LayoutShadowNode;
4 | import com.facebook.react.uimanager.NativeViewHierarchyOptimizer;
5 | import com.facebook.yoga.YogaEdge;
6 | import com.facebook.yoga.YogaPositionType;
7 |
8 | public class PullToRefreshFooterShadowNode extends LayoutShadowNode {
9 | @Override
10 | public void setLocalData(Object data) {
11 | super.setLocalData(data);
12 | if (data instanceof PullToRefreshFooterLocalData) {
13 | PullToRefreshFooterLocalData footerLocalData = (PullToRefreshFooterLocalData) data;
14 | setStyleHeight(footerLocalData.viewRect.bottom - footerLocalData.viewRect.top);
15 | }
16 | }
17 |
18 | @Override
19 | public void onBeforeLayout(NativeViewHierarchyOptimizer nativeViewHierarchyOptimizer) {
20 | setPositionType(YogaPositionType.ABSOLUTE);
21 | setPosition(YogaEdge.LEFT.intValue(), 0);
22 | setPosition(YogaEdge.RIGHT.intValue(), 0);
23 | setPosition(YogaEdge.BOTTOM.intValue(), -getLayoutHeight());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/src/main/java/com/reactnative/pulltorefresh/PullToRefreshHeaderLocalData.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.pulltorefresh;
2 |
3 | import android.graphics.Rect;
4 |
5 | public class PullToRefreshHeaderLocalData {
6 | Rect viewRect = new Rect();
7 | }
8 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/src/main/java/com/reactnative/pulltorefresh/PullToRefreshHeaderShadowNode.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.pulltorefresh;
2 |
3 | import com.facebook.react.uimanager.LayoutShadowNode;
4 | import com.facebook.react.uimanager.NativeViewHierarchyOptimizer;
5 | import com.facebook.yoga.YogaEdge;
6 | import com.facebook.yoga.YogaPositionType;
7 |
8 | public class PullToRefreshHeaderShadowNode extends LayoutShadowNode {
9 | @Override
10 | public void setLocalData(Object data) {
11 | super.setLocalData(data);
12 | if (data instanceof PullToRefreshHeaderLocalData) {
13 | PullToRefreshHeaderLocalData headerLocalData = (PullToRefreshHeaderLocalData) data;
14 | setStyleHeight(headerLocalData.viewRect.bottom - headerLocalData.viewRect.top);
15 | }
16 | }
17 |
18 | @Override
19 | public void onBeforeLayout(NativeViewHierarchyOptimizer nativeViewHierarchyOptimizer) {
20 | setPositionType(YogaPositionType.ABSOLUTE);
21 | setPosition(YogaEdge.LEFT.intValue(), 0);
22 | setPosition(YogaEdge.RIGHT.intValue(), 0);
23 | setPosition(YogaEdge.TOP.intValue(), -getLayoutHeight());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/src/main/java/com/reactnative/pulltorefresh/PullToRefreshPackage.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.pulltorefresh;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.facebook.react.ReactPackage;
6 | import com.facebook.react.bridge.NativeModule;
7 | import com.facebook.react.bridge.ReactApplicationContext;
8 | import com.facebook.react.uimanager.ViewManager;
9 |
10 | import java.util.Arrays;
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | public class PullToRefreshPackage implements ReactPackage {
15 | @NonNull
16 | @Override
17 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
18 | return Collections.emptyList();
19 | }
20 |
21 | @NonNull
22 | @Override
23 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
24 | return Arrays.asList(
25 | new PullToRefreshManager(),
26 | new PullToRefreshHeaderManager(),
27 | new PullToRefreshFooterManager()
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/src/main/java/com/reactnative/pulltorefresh/PullToRefreshState.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.pulltorefresh;
2 |
3 | public enum PullToRefreshState {
4 | Idle, Coming, Refreshing
5 | }
6 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/src/main/java/com/reactnative/pulltorefresh/event/OffsetChangedEvent.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.pulltorefresh.event;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import com.facebook.react.bridge.Arguments;
6 | import com.facebook.react.bridge.WritableMap;
7 | import com.facebook.react.uimanager.PixelUtil;
8 | import com.facebook.react.uimanager.events.Event;
9 |
10 | public class OffsetChangedEvent extends Event {
11 |
12 | public static final String Name = "offsetChangedEvent";
13 | public static final String JSEventName = "onOffsetChanged";
14 |
15 | private final float offset;
16 |
17 | public OffsetChangedEvent(int surfaceId, int viewTag, float offset) {
18 | super(surfaceId, viewTag);
19 | this.offset = PixelUtil.toDIPFromPixel(offset);
20 | }
21 |
22 | @Override
23 | public String getEventName() {
24 | return Name;
25 | }
26 |
27 | @Nullable
28 | protected WritableMap getEventData() {
29 | WritableMap map = Arguments.createMap();
30 | map.putDouble("offset", offset);
31 | return map;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/src/main/java/com/reactnative/pulltorefresh/event/RefreshEvent.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.pulltorefresh.event;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import com.facebook.react.bridge.Arguments;
6 | import com.facebook.react.bridge.WritableMap;
7 | import com.facebook.react.uimanager.events.Event;
8 |
9 | public class RefreshEvent extends Event {
10 | public static final String Name = "refreshEvent";
11 | public static final String JSEventName = "onRefresh";
12 |
13 | public RefreshEvent(int surfaceId, int viewTag) {
14 | super(surfaceId, viewTag);
15 | }
16 |
17 | @Override
18 | public String getEventName() {
19 | return Name;
20 | }
21 |
22 | @Nullable
23 | protected WritableMap getEventData() {
24 | return Arguments.createMap();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/android/src/main/java/com/reactnative/pulltorefresh/event/StateChangedEvent.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.pulltorefresh.event;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import com.facebook.react.bridge.Arguments;
6 | import com.facebook.react.bridge.WritableMap;
7 | import com.facebook.react.uimanager.events.Event;
8 | import com.reactnative.pulltorefresh.PullToRefreshState;
9 |
10 | public class StateChangedEvent extends Event {
11 |
12 | public static final String Name = "stateChangedEvent";
13 | public static final String JSEventName = "onStateChanged";
14 |
15 | private final PullToRefreshState state;
16 |
17 | public StateChangedEvent(int surfaceId, int viewTag, PullToRefreshState state) {
18 | super(surfaceId, viewTag);
19 | this.state = state;
20 | }
21 |
22 | @Override
23 | public String getEventName() {
24 | return Name;
25 | }
26 |
27 | @Nullable
28 | protected WritableMap getEventData() {
29 | WritableMap map = Arguments.createMap();
30 | map.putInt("state", state.ordinal());
31 | return map;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/docs/assets/separated.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/pull-to-refresh/docs/assets/separated.gif
--------------------------------------------------------------------------------
/packages/pull-to-refresh/docs/assets/shared.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/pull-to-refresh/docs/assets/shared.gif
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Event/RNRefreshOffsetChangedEvent.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNRefreshOffsetChangedEvent : NSObject
6 |
7 | - (instancetype)initWithViewTag:(NSNumber *)viewTag offset:(CGFloat)offset;
8 |
9 | @end
10 |
11 | NS_ASSUME_NONNULL_END
12 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Event/RNRefreshOffsetChangedEvent.m:
--------------------------------------------------------------------------------
1 | #import "RNRefreshOffsetChangedEvent.h"
2 |
3 |
4 | @interface RNRefreshOffsetChangedEvent ()
5 |
6 | @property(nonatomic, assign) CGFloat offset;
7 |
8 | @end
9 |
10 | @implementation RNRefreshOffsetChangedEvent
11 |
12 | @synthesize viewTag = _viewTag;
13 |
14 | + (NSString *)moduleDotMethod {
15 | return @"RCTEventEmitter.receiveEvent";
16 | }
17 |
18 | RCT_NOT_IMPLEMENTED(- (instancetype)init)
19 | - (instancetype)initWithViewTag:(NSNumber *)viewTag offset:(CGFloat)offset {
20 | if (self = [super init]) {
21 | _viewTag = viewTag;
22 | _offset = offset;
23 | }
24 | return self;
25 | }
26 |
27 | - (NSString *)eventName {
28 | return @"onOffsetChanged";
29 | }
30 |
31 | - (BOOL)canCoalesce {
32 | return YES;
33 | }
34 |
35 | - (uint16_t)coalescingKey {
36 | return 0;
37 | }
38 |
39 | - (NSArray *)arguments {
40 | return @[self.viewTag, RCTNormalizeInputEventName(self.eventName), @{
41 | @"offset": @(self.offset),
42 | }];
43 | }
44 |
45 | - (id)coalesceWithEvent:(id)newEvent {
46 | return newEvent;
47 | }
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Event/RNRefreshStateChangedEvent.h:
--------------------------------------------------------------------------------
1 | #import "RNRefreshState.h"
2 |
3 | #import
4 |
5 | NS_ASSUME_NONNULL_BEGIN
6 |
7 | @interface RNRefreshStateChangedEvent : NSObject
8 |
9 | - (instancetype)initWithViewTag:(NSNumber *)viewTag refreshState:(RNRefreshState)refreshState;
10 |
11 | @end
12 |
13 | NS_ASSUME_NONNULL_END
14 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Event/RNRefreshingEvent.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNRefreshingEvent : NSObject
6 |
7 | - (instancetype)initWithViewTag:(NSNumber *)viewTag;
8 |
9 | @end
10 |
11 | NS_ASSUME_NONNULL_END
12 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Event/RNRefreshingEvent.m:
--------------------------------------------------------------------------------
1 | #import "RNRefreshingEvent.h"
2 |
3 | static uint16_t _coalescingKey = 0;
4 |
5 | @implementation RNRefreshingEvent
6 |
7 | @synthesize viewTag = _viewTag;
8 |
9 | + (NSString *)moduleDotMethod {
10 | return @"RCTEventEmitter.receiveEvent";
11 | }
12 |
13 | RCT_NOT_IMPLEMENTED(- (instancetype)init)
14 | - (instancetype)initWithViewTag:(NSNumber *)viewTag {
15 | if (self = [super init]) {
16 | _viewTag = viewTag;
17 | }
18 | return self;
19 | }
20 |
21 | - (NSString *)eventName {
22 | return @"onRefresh";
23 | }
24 |
25 | - (BOOL)canCoalesce {
26 | return NO;
27 | }
28 |
29 | - (uint16_t)coalescingKey {
30 | return _coalescingKey++;
31 | }
32 |
33 | - (NSArray *)arguments {
34 | return @[self.viewTag, RCTNormalizeInputEventName(self.eventName), @{}];
35 | }
36 |
37 | - (id)coalesceWithEvent:(id)newEvent {
38 | return newEvent;
39 | }
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Footer/RNRefreshFooter.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 |
5 | NS_ASSUME_NONNULL_BEGIN
6 |
7 | @interface RNRefreshFooter : UIView
8 |
9 | - (instancetype)initWithBridge:(RCTBridge *)bridge;
10 |
11 | @property (nonatomic, copy) RCTDirectEventBlock onRefresh;
12 | @property (nonatomic, copy) RCTDirectEventBlock onStateChanged;
13 | @property (nonatomic, copy) RCTDirectEventBlock onOffsetChanged;
14 | @property (nonatomic, readonly, getter=isRefreshing) BOOL refreshing;
15 |
16 | @property (nonatomic, assign) BOOL noMoreData;
17 | // 手动触发加载更多,设置为 NO,将自动触发加载更多,默认为 NO
18 | @property (nonatomic, assign) BOOL manual;
19 |
20 | @property (nonatomic, weak) UIScrollView *scrollView;
21 |
22 | @end
23 |
24 | NS_ASSUME_NONNULL_END
25 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Footer/RNRefreshFooterLocalData.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNRefreshFooterLocalData : NSObject
6 |
7 | - (instancetype)initWithScrollViewContentSize:(CGSize)size;
8 |
9 | @property(nonatomic, assign)CGSize scrollViewContentSize;
10 |
11 | @end
12 |
13 | NS_ASSUME_NONNULL_END
14 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Footer/RNRefreshFooterLocalData.m:
--------------------------------------------------------------------------------
1 | #import "RNRefreshFooterLocalData.h"
2 |
3 | @implementation RNRefreshFooterLocalData
4 |
5 | - (instancetype)initWithScrollViewContentSize:(CGSize)size {
6 | if (self = [super init]) {
7 | _scrollViewContentSize = size;
8 | }
9 | return self;
10 | }
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Footer/RNRefreshFooterManager.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNRefreshFooterManager : RCTViewManager
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Footer/RNRefreshFooterShadowView.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNRefreshFooterShadowView : RCTShadowView
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Footer/RNRefreshFooterShadowView.m:
--------------------------------------------------------------------------------
1 | #import "RNRefreshFooterShadowView.h"
2 | #import "RNRefreshFooterLocalData.h"
3 |
4 | @implementation RNRefreshFooterShadowView
5 |
6 | - (instancetype)init {
7 | if (self = [super init]) {
8 | self.top = (YGValue){1000, YGUnitPoint};
9 | self.bottom = YGValueUndefined;
10 | self.left = YGValueZero;
11 | self.right = YGValueZero;
12 | self.position = YGPositionTypeAbsolute;
13 | }
14 | return self;
15 | }
16 |
17 | - (void)setLocalData:(RNRefreshFooterLocalData *)localData {
18 | CGSize size = localData.scrollViewContentSize;
19 | self.top = (YGValue){size.height, YGUnitPoint};
20 | self.bottom = YGValueUndefined;
21 | self.left = YGValueZero;
22 | self.right = YGValueZero;
23 | self.position = YGPositionTypeAbsolute;
24 | }
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Header/RNRefreshHeader.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 |
5 | NS_ASSUME_NONNULL_BEGIN
6 |
7 | @interface RNRefreshHeader : UIView
8 |
9 | - (instancetype)initWithBridge:(RCTBridge *)bridge;
10 |
11 | @property (nonatomic, copy) RCTDirectEventBlock onRefresh;
12 | @property (nonatomic, copy) RCTDirectEventBlock onStateChanged;
13 | @property (nonatomic, copy) RCTDirectEventBlock onOffsetChanged;
14 | @property (nonatomic, readonly, getter=isRefreshing) BOOL refreshing;
15 |
16 | @property (nonatomic, weak) UIScrollView *scrollView;
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Header/RNRefreshHeaderLocalData.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNRefreshHeaderLocalData : NSObject
6 |
7 | - (instancetype)initWithHeaderHeight:(CGFloat)height;
8 |
9 | @property(nonatomic, assign)CGFloat height;
10 |
11 | @end
12 |
13 | NS_ASSUME_NONNULL_END
14 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Header/RNRefreshHeaderLocalData.m:
--------------------------------------------------------------------------------
1 | #import "RNRefreshHeaderLocalData.h"
2 |
3 | @implementation RNRefreshHeaderLocalData
4 |
5 | - (instancetype)initWithHeaderHeight:(CGFloat)height {
6 | if (self = [super init]) {
7 | _height = height;
8 | }
9 | return self;
10 | }
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Header/RNRefreshHeaderManager.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNRefreshHeaderManager : RCTViewManager
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Header/RNRefreshHeaderShadowView.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNRefreshHeaderShadowView : RCTShadowView
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/Header/RNRefreshHeaderShadowView.m:
--------------------------------------------------------------------------------
1 | #import "RNRefreshHeaderShadowView.h"
2 | #import "RNRefreshHeaderLocalData.h"
3 |
4 | #import
5 |
6 | @implementation RNRefreshHeaderShadowView
7 |
8 | - (instancetype)init {
9 | if (self = [super init]) {
10 | self.top = (YGValue){-1000, YGUnitPoint};
11 | self.bottom = YGValueUndefined;
12 | self.left = YGValueZero;
13 | self.right = YGValueZero;
14 | self.position = YGPositionTypeAbsolute;
15 | }
16 | return self;
17 | }
18 |
19 | - (void)setLocalData:(RNRefreshHeaderLocalData *)localData {
20 | self.top = (YGValue){-localData.height, YGUnitPoint};
21 | self.bottom = YGValueUndefined;
22 | self.left = YGValueZero;
23 | self.right = YGValueZero;
24 | self.position = YGPositionTypeAbsolute;
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/RNPullToRefresh.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNPullToRefresh : UIView
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/RNPullToRefreshManager.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | NS_ASSUME_NONNULL_BEGIN
4 |
5 | @interface RNPullToRefreshManager : RCTViewManager
6 |
7 | @end
8 |
9 | NS_ASSUME_NONNULL_END
10 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/RNPullToRefreshManager.m:
--------------------------------------------------------------------------------
1 | #import "RNPullToRefreshManager.h"
2 | #import "RNPullToRefresh.h"
3 |
4 | @implementation RNPullToRefreshManager
5 |
6 | RCT_EXPORT_MODULE(PullToRefresh)
7 |
8 | - (UIView *)view {
9 | return [RNPullToRefresh new];
10 | }
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/RNRefreshState.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | typedef NS_ENUM(NSInteger, RNRefreshState) {
4 | /** 普通闲置状态 */
5 | RNRefreshStateIdle,
6 | /** 松开就可以进行刷新的状态 */
7 | RNRefreshStateComing,
8 | /** 正在刷新中的状态 */
9 | RNRefreshStateRefreshing,
10 | };
11 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/ios/PullToRefresh/RNRefreshState.m:
--------------------------------------------------------------------------------
1 | #import "RNRefreshState.h"
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sdcx/pull-to-refresh",
3 | "description": "A react-native PullToRefresh component.",
4 | "version": "0.27.0",
5 | "main": "./dist/index.js",
6 | "typings": "./dist/index.d.ts",
7 | "react-native": "src/index",
8 | "nativePackage": true,
9 | "files": [
10 | "src",
11 | "dist",
12 | "android",
13 | "ios",
14 | "RNPullToRefresh.podspec",
15 | "!android/build",
16 | "!ios/build",
17 | "!**/__tests__"
18 | ],
19 | "repository": "https://github.com/sdcxtech/react-native-troika",
20 | "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/pull-to-refresh/README.md",
21 | "author": "sdcx",
22 | "license": "MIT",
23 | "keywords": [
24 | "react-native",
25 | "pull-to-refresh",
26 | "refresh-control"
27 | ],
28 | "scripts": {
29 | "build": "rm -rf ./dist && tsc -p tsconfig.json",
30 | "prepare": "npm run build",
31 | "tsc": "tsc",
32 | "test": "jest",
33 | "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
34 | },
35 | "peerDependencies": {
36 | "react": ">=16.8",
37 | "react-native": ">=0.60"
38 | },
39 | "devDependencies": {}
40 | }
41 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/src/Footer/index.ts:
--------------------------------------------------------------------------------
1 | export {DefaultPullToRefreshFooter} from './DefaultPullToRefreshFooter';
2 | export {PullToRefreshFooter} from './native';
3 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/src/Header/index.ts:
--------------------------------------------------------------------------------
1 | export {DefaultPullToRefreshHeader} from './DefaultPullToRefreshHeader';
2 | export {PullToRefreshHeader} from './native';
3 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/src/PullToRefresh/native.tsx:
--------------------------------------------------------------------------------
1 | import {requireNativeComponent, ViewProps} from 'react-native';
2 |
3 | interface NativePullToRefreshProps extends ViewProps {}
4 |
5 | export const NativePullToRefresh =
6 | requireNativeComponent('PullToRefresh');
7 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/src/RefreshControl.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Platform, RefreshControlProps} from 'react-native';
3 | import {PullToRefresh} from './PullToRefresh';
4 |
5 | export default function PullToRefreshControl(props: RefreshControlProps) {
6 | if (Platform.OS === 'android') {
7 | return ;
8 | }
9 | // @ts-ignore
10 | return ;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/src/index.ts:
--------------------------------------------------------------------------------
1 | export {PullToRefreshHeader} from './Header';
2 | export {PullToRefreshFooter} from './Footer';
3 | export {PullToRefresh} from './PullToRefresh';
4 | export {default as RefreshControl} from './RefreshControl';
5 | export * from './types';
6 |
--------------------------------------------------------------------------------
/packages/pull-to-refresh/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./dist",
6 | "noEmit": false,
7 | "strict": true,
8 | "noImplicitAny": true,
9 | "allowImportingTsExtensions": false,
10 | "baseUrl": "./"
11 | },
12 | "include": ["./src/**/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/wheel-picker/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 listenzz@163.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/wheel-picker/README.md:
--------------------------------------------------------------------------------
1 | # WheelPicker
2 |
3 | 一个非常简单的 WheelPicker
4 |
5 | 一个非常帅的 WheelPicker
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/wheel-picker/RNWheelPicker.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "RNWheelPicker"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 |
10 | s.homepage = package["homepage"]
11 | s.license = package["license"]
12 | s.authors = package["author"]
13 | s.platforms = { :ios => "10.0" }
14 | s.source = { :git => "https://github.com/github-account/wheel-picker.git", :tag => "#{s.version}" }
15 |
16 | s.source_files = "ios/WheelPicker/**/*.{h,m,mm}"
17 | s.dependency "React-Core"
18 | end
--------------------------------------------------------------------------------
/packages/wheel-picker/android/build.gradle:
--------------------------------------------------------------------------------
1 | // android/build.gradle
2 |
3 | def safeExtGet(prop, fallback) {
4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5 | }
6 |
7 | apply plugin: 'com.android.library'
8 |
9 | android {
10 | compileOptions {
11 | sourceCompatibility JavaVersion.VERSION_1_8
12 | targetCompatibility JavaVersion.VERSION_1_8
13 | }
14 |
15 | compileSdkVersion safeExtGet('compileSdkVersion', 30)
16 | buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
17 |
18 | defaultConfig {
19 | minSdkVersion safeExtGet('minSdkVersion', 21)
20 | targetSdkVersion safeExtGet('targetSdkVersion', 30)
21 | versionCode 1
22 | versionName "1.0.0"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | consumerProguardFiles 'proguard-rules.pro'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: 'libs', include: ['*.jar'])
34 | implementation 'com.facebook.react:react-native:+'
35 | }
36 |
--------------------------------------------------------------------------------
/packages/wheel-picker/android/proguard-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/wheel-picker/android/proguard-rules.pro
--------------------------------------------------------------------------------
/packages/wheel-picker/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/wheel-picker/android/src/main/java/com/reactnative/wheelpicker/WheelPickerPackage.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.wheelpicker;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import java.util.Arrays;
6 | import java.util.Collections;
7 | import java.util.List;
8 |
9 | import com.facebook.react.ReactPackage;
10 | import com.facebook.react.bridge.NativeModule;
11 | import com.facebook.react.bridge.ReactApplicationContext;
12 | import com.facebook.react.uimanager.ViewManager;
13 |
14 | public class WheelPickerPackage implements ReactPackage {
15 | @NonNull
16 | @Override
17 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
18 | return Collections.emptyList();
19 | }
20 |
21 | @NonNull
22 | @Override
23 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
24 | return Collections.singletonList(new PickerViewManager());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/wheel-picker/android/src/main/java/com/reactnative/wheelpicker/wheel/IPickerViewData.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.wheelpicker.wheel;
2 |
3 | /**
4 | * Created by Sai on 2016/7/13.
5 | */
6 | public interface IPickerViewData {
7 | String getPickerViewText();
8 | }
9 |
--------------------------------------------------------------------------------
/packages/wheel-picker/android/src/main/java/com/reactnative/wheelpicker/wheel/LoopViewGestureListener.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.wheelpicker.wheel;
2 |
3 | import android.view.MotionEvent;
4 |
5 |
6 | /**
7 | * 手势监听
8 | */
9 | public final class LoopViewGestureListener extends android.view.GestureDetector.SimpleOnGestureListener {
10 |
11 | private final WheelView wheelView;
12 |
13 |
14 | public LoopViewGestureListener(WheelView wheelView) {
15 | this.wheelView = wheelView;
16 | }
17 |
18 | @Override
19 | public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
20 | wheelView.scrollBy(velocityY);
21 | return true;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/wheel-picker/android/src/main/java/com/reactnative/wheelpicker/wheel/MessageHandler.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.wheelpicker.wheel;
2 |
3 | import android.os.Handler;
4 | import android.os.Message;
5 |
6 | /**
7 | * Handler 消息类
8 | *
9 | * @author 小嵩
10 | * date: 2017-12-23 23:20:44
11 | */
12 | public final class MessageHandler extends Handler {
13 | public static final int WHAT_INVALIDATE_LOOP_VIEW = 1000;
14 | public static final int WHAT_SMOOTH_SCROLL = 2000;
15 | public static final int WHAT_ITEM_SELECTED = 3000;
16 |
17 | private final WheelView wheelView;
18 |
19 | public MessageHandler(WheelView wheelView) {
20 | this.wheelView = wheelView;
21 | }
22 |
23 | @Override
24 | public final void handleMessage(Message msg) {
25 | switch (msg.what) {
26 | case WHAT_INVALIDATE_LOOP_VIEW:
27 | wheelView.invalidate();
28 | break;
29 |
30 | case WHAT_SMOOTH_SCROLL:
31 | wheelView.smoothScroll(WheelView.ACTION.FLING);
32 | break;
33 |
34 | case WHAT_ITEM_SELECTED:
35 | wheelView.onItemSelected();
36 | break;
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/packages/wheel-picker/android/src/main/java/com/reactnative/wheelpicker/wheel/OnItemSelectedListener.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.wheelpicker.wheel;
2 |
3 |
4 | public interface OnItemSelectedListener {
5 | void onItemSelected(int index);
6 | }
7 |
--------------------------------------------------------------------------------
/packages/wheel-picker/android/src/main/java/com/reactnative/wheelpicker/wheel/WheelAdapter.java:
--------------------------------------------------------------------------------
1 | package com.reactnative.wheelpicker.wheel;
2 |
3 |
4 | public interface WheelAdapter {
5 | /**
6 | * Gets items count
7 | *
8 | * @return the count of wheel items
9 | */
10 | int getItemsCount();
11 |
12 | /**
13 | * Gets a wheel item by index.
14 | *
15 | * @param index the item index
16 | * @return the wheel item text or null
17 | */
18 | T getItem(int index);
19 |
20 | /**
21 | * Gets maximum item length. It is used to determine the wheel width.
22 | * If -1 is returned there will be used the default wheel width.
23 | *
24 | * @param o the item object
25 | * @return the maximum item length or -1
26 | */
27 | int indexOf(T o);
28 | }
29 |
--------------------------------------------------------------------------------
/packages/wheel-picker/docs/assets/wheelpicker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdcxtech/react-native-troika/d4c478eee06564bece181f5cfbc97fc2f12b930e/packages/wheel-picker/docs/assets/wheelpicker.png
--------------------------------------------------------------------------------
/packages/wheel-picker/ios/WheelPicker/RNWheelPicker.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 |
10 | #import
11 |
12 | @interface RNWheelPicker : UIPickerView
13 |
14 | @property (nonatomic, copy) NSArray *items;
15 | @property (nonatomic, assign) NSInteger selectedIndex;
16 |
17 | @property (nonatomic, strong) UIColor *textColorCenter;
18 | @property (nonatomic, strong) UIColor *textColorOut;
19 | @property (nonatomic, strong) UIFont *font;
20 | @property (nonatomic, assign) NSTextAlignment textAlign;
21 | @property (nonatomic, assign) CGFloat itemHeight;
22 |
23 | @property (nonatomic, copy) RCTBubblingEventBlock onItemSelected;
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/packages/wheel-picker/ios/WheelPicker/RNWheelPickerManager.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 |
10 | @interface RNWheelPickerManager : RCTViewManager
11 |
12 | @end
--------------------------------------------------------------------------------
/packages/wheel-picker/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sdcx/wheel-picker",
3 | "description": "A React Native Wheel Picker ui component.",
4 | "version": "0.10.0",
5 | "main": "./dist/index.js",
6 | "typings": "./dist/index.d.ts",
7 | "react-native": "src/index",
8 | "nativePackage": true,
9 | "files": [
10 | "src",
11 | "dist",
12 | "android",
13 | "ios",
14 | "docs",
15 | "RNWheelPicker.podspec",
16 | "!android/build",
17 | "!ios/build",
18 | "!**/__tests__"
19 | ],
20 | "repository": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/wheel-picker",
21 | "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/wheel-picker#readme",
22 | "author": "sdcx",
23 | "license": "MIT",
24 | "keywords": [
25 | "react-native",
26 | "wheel-picker",
27 | "picker"
28 | ],
29 | "scripts": {
30 | "build": "rm -rf ./dist && tsc -p tsconfig.json",
31 | "prepare": "npm run build",
32 | "tsc": "tsc",
33 | "test": "jest",
34 | "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
35 | },
36 | "peerDependencies": {
37 | "react": ">=16.8",
38 | "react-native": ">=0.60"
39 | },
40 | "devDependencies": {}
41 | }
42 |
--------------------------------------------------------------------------------
/packages/wheel-picker/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/packages/wheel-picker/src/index.tsx:
--------------------------------------------------------------------------------
1 | import {PickerItem} from './typing';
2 | import WheelPicker from './WheelPicker';
3 |
4 | export type {PickerItem};
5 |
6 | export default WheelPicker;
7 |
--------------------------------------------------------------------------------
/packages/wheel-picker/src/typing.ts:
--------------------------------------------------------------------------------
1 | export interface PickerItem {
2 | value: T;
3 | label: string;
4 | }
5 |
--------------------------------------------------------------------------------
/packages/wheel-picker/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "./dist",
6 | "noEmit": false,
7 | "strict": true,
8 | "noImplicitAny": true,
9 | "allowImportingTsExtensions": false,
10 | "baseUrl": "./"
11 | },
12 | "include": ["./src/**/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "paths": {
6 | "@sdcx/activity-indicator": ["./packages/activity-indicator/src"],
7 | "@sdcx/bottom-sheet": ["./packages/bottom-sheet/src"],
8 | "@sdcx/nested-scroll": ["./packages/nested-scroll/src"],
9 | "@sdcx/pull-to-refresh": ["./packages/pull-to-refresh/src"],
10 | "@sdcx/overlay": ["./packages/overlay/src"],
11 | "@sdcx/keyboard-insets": ["./packages/keyboard-insets/src"],
12 | "@sdcx/image-crop": ["./packages/image-crop/src"],
13 | "@sdcx/wheel-picker": ["./packages/wheel-picker/src"]
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------