├── Example ├── .buckconfig ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── App.js ├── __tests__ │ └── App.js ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── build_defs.bzl │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.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-v19 │ │ │ └── styles.xml │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── keystores │ │ ├── BUCK │ │ └── debug.keystore.properties │ └── settings.gradle ├── app.json ├── babel.config.js ├── index.js ├── ios │ ├── Example-tvOS │ │ └── Info.plist │ ├── Example-tvOSTests │ │ └── Info.plist │ ├── Example.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── Example-tvOS.xcscheme │ │ │ └── Example.xcscheme │ ├── Example │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── main.m │ └── ExampleTests │ │ ├── ExampleTests.m │ │ └── Info.plist ├── package-lock.json ├── package.json ├── src │ ├── components │ │ └── StatusBarView.js │ ├── data │ │ └── img │ │ │ ├── error.png │ │ │ ├── like.png │ │ │ └── success.png │ ├── pages │ │ ├── MainPage.js │ │ ├── ModalShowToastPage.js │ │ ├── ModalToastPage.js │ │ ├── SmartToastPage.js │ │ └── SnackBarPage.js │ ├── styles │ │ └── BaseStyle.js │ └── widget │ │ ├── data │ │ └── Constants.js │ │ ├── index.js │ │ ├── modaltoast │ │ ├── ModalShowToastView.js │ │ ├── ModalToast.js │ │ └── ModalToastView.js │ │ ├── snackbar │ │ ├── SnackBar.js │ │ └── SnackView.js │ │ ├── toast │ │ ├── Toast.js │ │ └── ToastView.js │ │ └── util │ │ └── UiUtil.js └── yarn.lock ├── README.md ├── img ├── main.jpg ├── modal.gif ├── modal1.gif ├── snackbar.gif └── toast.gif └── lib ├── .idea ├── inspectionProfiles │ └── Project_Default.xml ├── jsLinters │ └── tslint.xml ├── lib.iml ├── misc.xml ├── modules.xml └── workspace.xml ├── README.md ├── data └── Constants.js ├── index.d.ts ├── index.js ├── modaltoast ├── ModalShowToastView.js ├── ModalToast.js └── ModalToastView.js ├── package.json ├── snackbar ├── SnackBar.js └── SnackView.js ├── toast ├── Toast.js └── ToastView.js └── util └── UiUtil.js /Example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /Example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | 16 | ; Ignore polyfills 17 | .*/Libraries/polyfills/.* 18 | 19 | ; Ignore metro 20 | .*/node_modules/metro/.* 21 | 22 | [include] 23 | 24 | [libs] 25 | node_modules/react-native/Libraries/react-native/react-native-interface.js 26 | node_modules/react-native/flow/ 27 | node_modules/react-native/flow-github/ 28 | 29 | [options] 30 | emoji=true 31 | 32 | esproposal.optional_chaining=enable 33 | esproposal.nullish_coalescing=enable 34 | 35 | module.system=haste 36 | module.system.haste.use_name_reducers=true 37 | # get basename 38 | module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' 39 | # strip .js or .js.flow suffix 40 | module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' 41 | # strip .ios suffix 42 | module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' 43 | module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' 44 | module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' 45 | module.system.haste.paths.blacklist=.*/__tests__/.* 46 | module.system.haste.paths.blacklist=.*/__mocks__/.* 47 | module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* 48 | module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* 49 | 50 | munge_underscores=true 51 | 52 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 53 | 54 | module.file_ext=.js 55 | module.file_ext=.jsx 56 | module.file_ext=.json 57 | module.file_ext=.native.js 58 | 59 | suppress_type=$FlowIssue 60 | suppress_type=$FlowFixMe 61 | suppress_type=$FlowFixMeProps 62 | suppress_type=$FlowFixMeState 63 | 64 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 65 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 66 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 67 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 68 | 69 | [version] 70 | ^0.86.0 71 | -------------------------------------------------------------------------------- /Example/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /Example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | -------------------------------------------------------------------------------- /Example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /Example/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {createStackNavigator,createAppContainer} from 'react-navigation' 3 | import MainPage from './src/pages/MainPage' 4 | import SmartToastPage from './src/pages/SmartToastPage' 5 | import ModalToastPage from './src/pages/ModalToastPage' 6 | import SnackBarPage from './src/pages/SnackBarPage' 7 | import ModalShowToastPage from './src/pages/ModalShowToastPage'; 8 | 9 | const App = createStackNavigator({ 10 | MainPage: {screen: MainPage}, 11 | SmartToastPage: {screen: SmartToastPage}, 12 | SnackBarPage: {screen: SnackBarPage}, 13 | ModalToastPage: {screen: ModalToastPage}, 14 | ModalShowToastPage: {screen: ModalShowToastPage} 15 | },{ 16 | navigationOptions: { 17 | gesturesEnabled: true 18 | }, 19 | headerMode: 'none' 20 | }) 21 | 22 | export default createAppContainer(App) 23 | -------------------------------------------------------------------------------- /Example/__tests__/App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @lint-ignore-every XPLATJSCOPYRIGHT1 4 | */ 5 | 6 | import 'react-native'; 7 | import React from 'react'; 8 | import App from '../App'; 9 | 10 | // Note: test renderer must be required after react-native. 11 | import renderer from 'react-test-renderer'; 12 | 13 | it('renders correctly', () => { 14 | renderer.create(); 15 | }); 16 | -------------------------------------------------------------------------------- /Example/android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.example", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.example", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /Example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // whether to bundle JS and assets in debug mode 22 | * bundleInDebug: false, 23 | * 24 | * // whether to bundle JS and assets in release mode 25 | * bundleInRelease: true, 26 | * 27 | * // whether to bundle JS and assets in another build variant (if configured). 28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 29 | * // The configuration property can be in the following formats 30 | * // 'bundleIn${productFlavor}${buildType}' 31 | * // 'bundleIn${buildType}' 32 | * // bundleInFreeDebug: true, 33 | * // bundleInPaidRelease: true, 34 | * // bundleInBeta: true, 35 | * 36 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 37 | * // for example: to disable dev mode in the staging build type (if configured) 38 | * devDisabledInStaging: true, 39 | * // The configuration property can be in the following formats 40 | * // 'devDisabledIn${productFlavor}${buildType}' 41 | * // 'devDisabledIn${buildType}' 42 | * 43 | * // the root of your project, i.e. where "package.json" lives 44 | * root: "../../", 45 | * 46 | * // where to put the JS bundle asset in debug mode 47 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 48 | * 49 | * // where to put the JS bundle asset in release mode 50 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 51 | * 52 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 53 | * // require('./image.png')), in debug mode 54 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 55 | * 56 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 57 | * // require('./image.png')), in release mode 58 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 59 | * 60 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 61 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 62 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 63 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 64 | * // for example, you might want to remove it from here. 65 | * inputExcludes: ["android/**", "ios/**"], 66 | * 67 | * // override which node gets called and with what additional arguments 68 | * nodeExecutableAndArgs: ["node"], 69 | * 70 | * // supply additional arguments to the packager 71 | * extraPackagerArgs: [] 72 | * ] 73 | */ 74 | 75 | project.ext.react = [ 76 | entryFile: "index.js" 77 | ] 78 | 79 | apply from: "../../node_modules/react-native/react.gradle" 80 | 81 | /** 82 | * Set this to true to create two separate APKs instead of one: 83 | * - An APK that only works on ARM devices 84 | * - An APK that only works on x86 devices 85 | * The advantage is the size of the APK is reduced by about 4MB. 86 | * Upload all the APKs to the Play Store and people will download 87 | * the correct one based on the CPU architecture of their device. 88 | */ 89 | def enableSeparateBuildPerCPUArchitecture = false 90 | 91 | /** 92 | * Run Proguard to shrink the Java bytecode in release builds. 93 | */ 94 | def enableProguardInReleaseBuilds = false 95 | 96 | android { 97 | compileSdkVersion rootProject.ext.compileSdkVersion 98 | buildToolsVersion rootProject.ext.buildToolsVersion 99 | 100 | defaultConfig { 101 | applicationId "com.example" 102 | minSdkVersion rootProject.ext.minSdkVersion 103 | targetSdkVersion rootProject.ext.targetSdkVersion 104 | versionCode 1 105 | versionName "1.0" 106 | } 107 | splits { 108 | abi { 109 | reset() 110 | enable enableSeparateBuildPerCPUArchitecture 111 | universalApk false // If true, also generate a universal APK 112 | include "armeabi-v7a", "x86", "arm64-v8a" 113 | } 114 | } 115 | buildTypes { 116 | release { 117 | minifyEnabled enableProguardInReleaseBuilds 118 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 119 | } 120 | } 121 | // applicationVariants are e.g. debug, release 122 | applicationVariants.all { variant -> 123 | variant.outputs.each { output -> 124 | // For each separate APK per architecture, set a unique version code as described here: 125 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 126 | def versionCodes = ["armeabi-v7a":1, "x86":2, "arm64-v8a": 3] 127 | def abi = output.getFilter(OutputFile.ABI) 128 | if (abi != null) { // null for the universal-debug, universal-release variants 129 | output.versionCodeOverride = 130 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 131 | } 132 | } 133 | } 134 | } 135 | 136 | dependencies { 137 | implementation project(':react-native-gesture-handler') 138 | implementation fileTree(dir: "libs", include: ["*.jar"]) 139 | implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" 140 | implementation "com.facebook.react:react-native:+" // From node_modules 141 | } 142 | 143 | // Run this once to be able to run the application with BUCK 144 | // puts all compile dependencies into folder libs for BUCK to use 145 | task copyDownloadableDepsToLibs(type: Copy) { 146 | from configurations.compile 147 | into 'libs' 148 | } 149 | -------------------------------------------------------------------------------- /Example/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /Example/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 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /Example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "Example"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Example/android/app/src/main/java/com/example/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.swmansion.gesturehandler.react.RNGestureHandlerPackage; 7 | import com.facebook.react.ReactNativeHost; 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.shell.MainReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | public class MainApplication extends Application implements ReactApplication { 16 | 17 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 18 | @Override 19 | public boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() { 25 | return Arrays.asList( 26 | new MainReactPackage(), 27 | new RNGestureHandlerPackage() 28 | ); 29 | } 30 | 31 | @Override 32 | protected String getJSMainModuleName() { 33 | return "index"; 34 | } 35 | }; 36 | 37 | @Override 38 | public ReactNativeHost getReactNativeHost() { 39 | return mReactNativeHost; 40 | } 41 | 42 | @Override 43 | public void onCreate() { 44 | super.onCreate(); 45 | SoLoader.init(this, /* native exopackage */ false); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/android/app/src/main/res/values-v19/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Example 3 | 4 | -------------------------------------------------------------------------------- /Example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "28.0.2" 6 | minSdkVersion = 16 7 | compileSdkVersion = 28 8 | targetSdkVersion = 27 9 | supportLibVersion = "28.0.0" 10 | } 11 | repositories { 12 | google() 13 | jcenter() 14 | } 15 | dependencies { 16 | classpath 'com.android.tools.build:gradle:3.2.1' 17 | 18 | // NOTE: Do not place your application dependencies here; they belong 19 | // in the individual module build.gradle files 20 | } 21 | } 22 | 23 | allprojects { 24 | repositories { 25 | mavenLocal() 26 | google() 27 | jcenter() 28 | maven { 29 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 30 | url "$rootDir/../node_modules/react-native/android" 31 | } 32 | } 33 | } 34 | 35 | 36 | task wrapper(type: Wrapper) { 37 | gradleVersion = '4.7' 38 | distributionUrl = distributionUrl.replace("bin", "all") 39 | } 40 | -------------------------------------------------------------------------------- /Example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | -------------------------------------------------------------------------------- /Example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip 6 | -------------------------------------------------------------------------------- /Example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /Example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /Example/android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /Example/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /Example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'Example' 2 | include ':react-native-gesture-handler' 3 | project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android') 4 | 5 | include ':app' 6 | -------------------------------------------------------------------------------- /Example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Example", 3 | "displayName": "Example" 4 | } -------------------------------------------------------------------------------- /Example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["module:metro-react-native-babel-preset"] 3 | } 4 | -------------------------------------------------------------------------------- /Example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | * @lint-ignore-every XPLATJSCOPYRIGHT1 4 | */ 5 | 6 | import {AppRegistry} from 'react-native'; 7 | import App from './App'; 8 | import {name as appName} from './app.json'; 9 | 10 | AppRegistry.registerComponent(appName, () => App); 11 | -------------------------------------------------------------------------------- /Example/ios/Example-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSExceptionDomains 45 | 46 | localhost 47 | 48 | NSExceptionAllowsInsecureHTTPLoads 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Example/ios/Example-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/ios/Example.xcodeproj/xcshareddata/xcschemes/Example-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /Example/ios/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 65 | 66 | 67 | 68 | 70 | 76 | 77 | 78 | 79 | 80 | 90 | 92 | 98 | 99 | 100 | 101 | 107 | 109 | 115 | 116 | 117 | 118 | 120 | 121 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /Example/ios/Example/AppDelegate.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 AppDelegate : UIResponder 11 | 12 | @property (nonatomic, strong) UIWindow *window; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Example/ios/Example/AppDelegate.m: -------------------------------------------------------------------------------- 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 "AppDelegate.h" 9 | 10 | #import 11 | #import 12 | 13 | @implementation AppDelegate 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 16 | { 17 | NSURL *jsCodeLocation; 18 | 19 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 20 | 21 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 22 | moduleName:@"Example" 23 | initialProperties:nil 24 | launchOptions:launchOptions]; 25 | rootView.backgroundColor = [UIColor blackColor]; 26 | 27 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 28 | UIViewController *rootViewController = [UIViewController new]; 29 | rootViewController.view = rootView; 30 | self.window.rootViewController = rootViewController; 31 | [self.window makeKeyAndVisible]; 32 | return YES; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Example/ios/Example/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Example/ios/Example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /Example/ios/Example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/ios/Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSLocationWhenInUseUsageDescription 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UIViewControllerBasedStatusBarAppearance 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Example/ios/Example/main.m: -------------------------------------------------------------------------------- 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 "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example/ios/ExampleTests/ExampleTests.m: -------------------------------------------------------------------------------- 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 | #import 10 | 11 | #import 12 | #import 13 | 14 | #define TIMEOUT_SECONDS 600 15 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 16 | 17 | @interface ExampleTests : XCTestCase 18 | 19 | @end 20 | 21 | @implementation ExampleTests 22 | 23 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 24 | { 25 | if (test(view)) { 26 | return YES; 27 | } 28 | for (UIView *subview in [view subviews]) { 29 | if ([self findSubviewInView:subview matching:test]) { 30 | return YES; 31 | } 32 | } 33 | return NO; 34 | } 35 | 36 | - (void)testRendersWelcomeScreen 37 | { 38 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 39 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 40 | BOOL foundElement = NO; 41 | 42 | __block NSString *redboxError = nil; 43 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 44 | if (level >= RCTLogLevelError) { 45 | redboxError = message; 46 | } 47 | }); 48 | 49 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 50 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 51 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 52 | 53 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 54 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 55 | return YES; 56 | } 57 | return NO; 58 | }]; 59 | } 60 | 61 | RCTSetLogFunction(RCTDefaultLogFunction); 62 | 63 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 64 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 65 | } 66 | 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /Example/ios/ExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "react": "16.6.3", 11 | "react-native": "0.58.6", 12 | "react-native-gesture-handler": "^1.1.0", 13 | "react-native-root-siblings": "^4.1.0", 14 | "react-navigation": "^3.3.2" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^7.0.0-bridge.0", 18 | "babel-jest": "24.1.0", 19 | "jest": "24.1.0", 20 | "metro-react-native-babel-preset": "0.53.0", 21 | "react-test-renderer": "16.6.3" 22 | }, 23 | "jest": { 24 | "preset": "react-native" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Example/src/components/StatusBarView.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react' 2 | import { 3 | View, 4 | Dimensions, 5 | StatusBar, 6 | Platform 7 | } from 'react-native' 8 | const PropTypes = require('prop-types') 9 | 10 | export default class StatusBarView extends Component{ 11 | 12 | render() { 13 | if (Platform.OS === 'ios') { 14 | return ( 15 | 19 | ) 20 | } else { 21 | return ( 22 | 23 | 27 | 28 | ) 29 | } 30 | 31 | } 32 | 33 | } 34 | 35 | StatusBarView.defaultProps = { 36 | backgroundColor: 'white', 37 | barStyle: 'dark-content', //'default', 'light-content', 'dark-content' 38 | } 39 | 40 | StatusBarView.propTypes = { 41 | backgroundColor: PropTypes.string, 42 | barStyle: PropTypes.string, 43 | } 44 | -------------------------------------------------------------------------------- /Example/src/data/img/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/src/data/img/error.png -------------------------------------------------------------------------------- /Example/src/data/img/like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/src/data/img/like.png -------------------------------------------------------------------------------- /Example/src/data/img/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/Example/src/data/img/success.png -------------------------------------------------------------------------------- /Example/src/pages/MainPage.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react' 2 | import { 3 | View, 4 | Text, 5 | StyleSheet, 6 | SafeAreaView, 7 | TouchableOpacity, 8 | Dimensions 9 | } from 'react-native' 10 | import StatusBarView from "../components/StatusBarView"; 11 | import SmartToastPage from "./SmartToastPage"; 12 | import ModalToastPage from "./ModalToastPage"; 13 | const {width} = Dimensions.get('window') 14 | 15 | const main_color = '#1ABc9c' 16 | export default class MainPage extends Component{ 17 | 18 | render() { 19 | return ( 20 | 21 | 22 | 23 | 24 | {'SmartTip'} 25 | 26 | 27 | {'Toast'} 28 | 29 | 30 | {'SnackBar'} 31 | 32 | 33 | {'ModalToast'} 34 | 35 | 36 | {'ModalShowToastPage'} 37 | 38 | 39 | 40 | ) 41 | } 42 | 43 | onJumpSmartToastPage = () => { 44 | this.props.navigation.navigate('SmartToastPage') 45 | } 46 | 47 | onJumpSnackBarPage = () => { 48 | this.props.navigation.navigate('SnackBarPage') 49 | } 50 | 51 | onJumpModalToastPage = () => { 52 | this.props.navigation.navigate('ModalToastPage') 53 | } 54 | 55 | onJumpModalShowToastPage = () => { 56 | this.props.navigation.navigate('ModalShowToastPage') 57 | } 58 | 59 | } 60 | 61 | const styles = StyleSheet.create({ 62 | safe_container: { 63 | flex: 1, 64 | backgroundColor: main_color 65 | }, 66 | container: { 67 | flex: 1, 68 | backgroundColor: '#e0e0e0' 69 | }, 70 | header: { 71 | height: 44, 72 | backgroundColor: main_color, 73 | justifyContent: 'center', 74 | alignItems: 'center', 75 | marginBottom: 40 76 | }, 77 | btn_view: { 78 | height: 48, 79 | backgroundColor: main_color, 80 | borderRadius: 24, 81 | width: width*0.6, 82 | alignSelf: 'center', 83 | justifyContent: 'center', 84 | alignItems: 'center', 85 | marginTop: 20 86 | }, 87 | btn_text: { 88 | fontSize: 16, 89 | color: '#fff', 90 | fontWeight: 'bold' 91 | } 92 | }) 93 | -------------------------------------------------------------------------------- /Example/src/pages/ModalShowToastPage.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react' 2 | import { 3 | View, 4 | Text, 5 | StyleSheet, 6 | SafeAreaView, 7 | TouchableOpacity, 8 | Modal, 9 | Image 10 | } from 'react-native' 11 | import StatusBarView from "../components/StatusBarView"; 12 | import ModalShowToastView from '../widget/modaltoast/ModalShowToastView' 13 | import Toast from '../widget/toast/Toast' 14 | 15 | const main_color = '#3aafff' 16 | 17 | const toastOpt1 = { 18 | data: 'Sow nothing, reap nothing', 19 | position: Toast.position.TOP, 20 | textColor: '#fff', 21 | backgroundColor: main_color, 22 | } 23 | 24 | const toastOpt2 = { 25 | data: 'Success', 26 | textColor: '#ffffff', 27 | backgroundColor: '#000', 28 | position: Toast.position.CENTER, 29 | icon: 30 | } 31 | 32 | export default class ModalShowToastPage extends Component { 33 | 34 | constructor(props) { 35 | super(props) 36 | 37 | this.state = { 38 | isShowModal: false, 39 | tipData: null, 40 | } 41 | } 42 | 43 | getToastInstance = (toastInstance) => { 44 | this.toastInstance = toastInstance; 45 | } 46 | 47 | show1 = () => this.toastInstance(toastOpt1) 48 | show2 = () => this.toastInstance(toastOpt2) 49 | 50 | render() { 51 | return ( 52 | 53 | 54 | 58 | 59 | { this.setState({isShowModal: false}) }}> 60 | Hide Modal 61 | 62 | 63 | Toast1 64 | 65 | 66 | Toast2 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | {this.props.navigation.goBack()}}> 75 | {'Back'} 76 | 77 | {'Toast'} 78 | 79 | { this.setState({isShowModal: true}) }}> 82 | Show Modal 83 | 84 | 85 | 86 | ) 87 | } 88 | 89 | _renderBtnView = (btnText,toastOpts) => { 90 | return ( 91 | {this.show(toastOpts)}} 94 | > 95 | {btnText} 96 | 97 | ) 98 | } 99 | 100 | show = (toastOpts) => { 101 | Toast.show(toastOpts) 102 | } 103 | 104 | } 105 | 106 | const styles = StyleSheet.create({ 107 | safe_container: { 108 | flex: 1, 109 | backgroundColor: main_color 110 | }, 111 | container: { 112 | flex: 1, 113 | backgroundColor: '#e0e0e0' 114 | }, 115 | header: { 116 | height: 44, 117 | backgroundColor: main_color, 118 | justifyContent: 'center', 119 | alignItems: 'center', 120 | marginBottom: 40 121 | }, 122 | header_back: { 123 | color: 14, 124 | marginLeft: 5, 125 | height: 44, 126 | justifyContent: 'center', 127 | paddingLeft: 5, 128 | paddingRight: 5, 129 | position: 'absolute', 130 | top: 0, 131 | left: 0 132 | }, 133 | item: { 134 | flexDirection: 'row', 135 | justifyContent: 'center', 136 | flexWrap: 'wrap' 137 | }, 138 | btn_view: { 139 | height: 36, 140 | backgroundColor: main_color, 141 | borderRadius: 2, 142 | paddingLeft: 10, 143 | paddingRight: 10, 144 | justifyContent: 'center', 145 | alignItems: 'center', 146 | margin: 10 147 | }, 148 | btn_text: { 149 | fontSize: 14, 150 | color: '#fff' 151 | }, 152 | modal: { 153 | flex: 1, 154 | justifyContent: 'flex-end', 155 | backgroundColor: 'rgba(0, 0, 0, .5)', 156 | paddingBottom: 20 157 | } 158 | }) 159 | -------------------------------------------------------------------------------- /Example/src/pages/ModalToastPage.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react' 2 | import { 3 | View, 4 | Text, 5 | StyleSheet, 6 | SafeAreaView, 7 | TouchableOpacity, 8 | Image, 9 | ActivityIndicator, 10 | Modal 11 | } from 'react-native' 12 | import StatusBarView from "../components/StatusBarView"; 13 | import ModalToast from '../widget/modaltoast/ModalToast' 14 | 15 | const main_color = '#3aafff' 16 | 17 | const toast1 = { 18 | data: 'Sow nothing, reap nothing', 19 | backgroundColor: '#666666', 20 | position: ModalToast.position.TOP 21 | } 22 | 23 | const toast2 = { 24 | data: '1. Sow nothing, reap nothing。\n2. Sow nothing, reap nothing。\n 3. Sow nothing reap nothing。 \n4. Sow nothing, reap nothing。 \n5.Sow nothing, reap nothing。\n6. Sow nothing, reap nothing。\n7. Sow nothing, reap nothing。 \n8. Sow nothing reap nothing。 \n9. Sow nothing, reap nothing。 \n10.Sow nothing, reap nothing。', 25 | textColor: '#fff', 26 | backgroundColor: main_color, 27 | position: ModalToast.position.CENTER 28 | } 29 | 30 | const toast3 = { 31 | data: 'Hello World' 32 | } 33 | 34 | const toast4 = { 35 | data: 'Success', 36 | textColor: '#ffffff', 37 | backgroundColor: '#444444', 38 | icon: 39 | } 40 | 41 | const toast5 = { 42 | data: 'Loading', 43 | textColor: '#fff', 44 | backgroundColor: '#444444', 45 | position: ModalToast.position.CENTER, 46 | icon: 47 | } 48 | 49 | const toast6 = { 50 | data: 'Error', 51 | textColor: '#fff', 52 | backgroundColor: '#444444', 53 | icon: 54 | } 55 | 56 | export default class ModalToastPage extends Component{ 57 | 58 | constructor(props) { 59 | super(props) 60 | 61 | this.state = { 62 | showModal: false, 63 | tip: 'start', 64 | visibleModal: false 65 | } 66 | } 67 | 68 | render() { 69 | return ( 70 | 71 | 72 | 73 | 74 | {this.props.navigation.goBack()}}> 75 | {'Back'} 76 | 77 | {'ModalToast'} 78 | 79 | 80 | {this._renderBtnView('Base Top',toast1)} 81 | {this._renderBtnView('Base Center',toast2)} 82 | {this._renderBtnView('Base Bottom',toast3)} 83 | 84 | 85 | {this._renderBtnView('Icon Top',toast4)} 86 | {this._renderBtnView('Icon Center',toast5)} 87 | {this._renderBtnView('Icon Bottom',toast6)} 88 | 89 | 90 | 94 | {'Analog network request'} 95 | 96 | 97 | {this.state.tip} 98 | { 101 | this.setState({ 102 | visibleModal: true 103 | }) 104 | }} 105 | > 106 | {'Show Common Modal'} 107 | 108 | { 113 | this.setState({ 114 | visibleModal: false 115 | }) 116 | }} 117 | > 118 | 119 | 127 | {this.setState({visibleModal: false})}}> 128 | Close Modal 129 | 130 | {this.show(toast5)}}> 131 | Show Tip 132 | 133 | 134 | 135 | 136 | 137 | 138 | ) 139 | } 140 | 141 | _renderBtnView = (btnText,toastOpts) => { 142 | return ( 143 | {this.show(toastOpts)}} 146 | > 147 | {btnText} 148 | 149 | ) 150 | } 151 | 152 | show = (toastOpts) => { 153 | 154 | if (this.testTimeOut) { 155 | clearTimeout(this.testTimeOut) 156 | this.testTimeOut = undefined 157 | ModalToast.hide() 158 | } 159 | 160 | ModalToast.show(toastOpts) 161 | 162 | this.testTimeOut = setTimeout(()=>{ 163 | ModalToast.hide() 164 | },3000) 165 | } 166 | 167 | analogNetworkRequest = () => { 168 | if (this.testInterval) { 169 | clearInterval(this.testInterval) 170 | this.testInterval = undefined 171 | ModalToast.hide() 172 | } 173 | 174 | let tempIndex = 0 175 | 176 | ModalToast.show(toast5) 177 | this.setState({tip: 'Loading'}) 178 | 179 | this.testInterval = setInterval(()=>{ 180 | if(tempIndex === 0) { 181 | const tempToast4 = {...toast4} 182 | tempToast4.position = ModalToast.position.CENTER 183 | ModalToast.update(tempToast4) 184 | this.setState({tip: 'Success'}) 185 | } else { 186 | if (this.testInterval) { 187 | clearInterval(this.testInterval) 188 | this.testInterval = undefined 189 | ModalToast.hide() 190 | } 191 | this.setState({tip: 'end'}) 192 | } 193 | tempIndex++; 194 | },3000) 195 | } 196 | 197 | } 198 | 199 | const styles = StyleSheet.create({ 200 | safe_container: { 201 | flex: 1, 202 | backgroundColor: main_color 203 | }, 204 | container: { 205 | flex: 1, 206 | backgroundColor: '#e0e0e0' 207 | }, 208 | header: { 209 | height: 44, 210 | backgroundColor: main_color, 211 | justifyContent: 'center', 212 | alignItems: 'center', 213 | marginBottom: 40 214 | }, 215 | header_back: { 216 | color: 14, 217 | marginLeft: 5, 218 | height: 44, 219 | justifyContent: 'center', 220 | paddingLeft: 5, 221 | paddingRight: 5, 222 | position: 'absolute', 223 | top: 0, 224 | left: 0 225 | }, 226 | item: { 227 | flexDirection: 'row', 228 | justifyContent: 'center', 229 | flexWrap: 'wrap' 230 | }, 231 | btn_view: { 232 | height: 36, 233 | backgroundColor: main_color, 234 | borderRadius: 2, 235 | paddingLeft: 10, 236 | paddingRight: 10, 237 | justifyContent: 'center', 238 | alignItems: 'center', 239 | margin: 10 240 | }, 241 | btn_text: { 242 | fontSize: 14, 243 | color: '#fff' 244 | } 245 | }) 246 | -------------------------------------------------------------------------------- /Example/src/pages/SmartToastPage.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react' 2 | import { 3 | View, 4 | Text, 5 | StyleSheet, 6 | SafeAreaView, 7 | TouchableOpacity, 8 | Image, 9 | ActivityIndicator, 10 | TextInput 11 | } from 'react-native' 12 | import StatusBarView from "../components/StatusBarView"; 13 | import Toast from '../widget/toast/Toast' 14 | 15 | const main_color = '#3aafff' 16 | 17 | const toast1 = { 18 | data: 'Sow nothing, reap nothing', 19 | backgroundColor: '#666666', 20 | position: Toast.position.TOP 21 | } 22 | 23 | const toast2 = { 24 | data: '1. Sow nothing, reap nothing。\n2. Sow nothing, reap nothing。\n 3. Sow nothing reap nothing。 \n4. Sow nothing, reap nothing。 \n5.Sow nothing, reap nothing。\n6. Sow nothing, reap nothing。\n7. Sow nothing, reap nothing。 \n8. Sow nothing reap nothing。 \n9. Sow nothing, reap nothing。 \n10.Sow nothing, reap nothing。', 25 | textColor: '#fff', 26 | backgroundColor: main_color, 27 | position: Toast.position.CENTER 28 | } 29 | 30 | const toast3 = { 31 | data: 'Hello World' 32 | } 33 | 34 | const toast4 = { 35 | data: 'Success', 36 | textColor: '#ffffff', 37 | backgroundColor: '#444444', 38 | position: Toast.position.TOP, 39 | icon: 40 | } 41 | 42 | const toast5 = { 43 | data: 'Loading', 44 | textColor: '#fff', 45 | backgroundColor: '#444444', 46 | position: Toast.position.CENTER, 47 | duration: Toast.duration.LONG, 48 | icon: 49 | } 50 | 51 | const toast6 = { 52 | data: 'Very Good', 53 | textColor: '#E5ECFF', 54 | backgroundColor: '#444', 55 | position: Toast.position.BOTTOM, 56 | icon: , 57 | } 58 | 59 | export default class SmartToastPage extends Component{ 60 | 61 | render() { 62 | return ( 63 | 64 | 65 | 66 | 67 | {this.props.navigation.goBack()}}> 68 | {'Back'} 69 | 70 | {'Toast'} 71 | 72 | 73 | {this._renderBtnView('Base Top',toast1)} 74 | {this._renderBtnView('Base Center',toast2)} 75 | {this._renderBtnView('Base Bottom',toast3)} 76 | 77 | 78 | {this._renderBtnView('Icon Top',toast4)} 79 | {this._renderBtnView('Icon Center',toast5)} 80 | {this._renderBtnView('Icon Bottom',toast6)} 81 | 82 | 83 | 84 | 85 | ) 86 | } 87 | 88 | _renderBtnView = (btnText,toastOpts) => { 89 | return ( 90 | {this.show(toastOpts)}} 93 | > 94 | {btnText} 95 | 96 | ) 97 | } 98 | 99 | show = (toastOpts) => { 100 | Toast.show(toastOpts) 101 | } 102 | 103 | } 104 | 105 | const styles = StyleSheet.create({ 106 | safe_container: { 107 | flex: 1, 108 | backgroundColor: main_color 109 | }, 110 | container: { 111 | flex: 1, 112 | backgroundColor: '#e0e0e0' 113 | }, 114 | header: { 115 | height: 44, 116 | backgroundColor: main_color, 117 | justifyContent: 'center', 118 | alignItems: 'center', 119 | marginBottom: 40 120 | }, 121 | header_back: { 122 | color: 14, 123 | marginLeft: 5, 124 | height: 44, 125 | justifyContent: 'center', 126 | paddingLeft: 5, 127 | paddingRight: 5, 128 | position: 'absolute', 129 | top: 0, 130 | left: 0 131 | }, 132 | item: { 133 | flexDirection: 'row', 134 | justifyContent: 'center', 135 | flexWrap: 'wrap' 136 | }, 137 | btn_view: { 138 | height: 36, 139 | backgroundColor: main_color, 140 | borderRadius: 2, 141 | paddingLeft: 10, 142 | paddingRight: 10, 143 | justifyContent: 'center', 144 | alignItems: 'center', 145 | margin: 10 146 | }, 147 | btn_text: { 148 | fontSize: 14, 149 | color: '#fff' 150 | } 151 | }) 152 | -------------------------------------------------------------------------------- /Example/src/pages/SnackBarPage.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react' 2 | import { 3 | View, 4 | Text, 5 | StyleSheet, 6 | SafeAreaView, 7 | TouchableOpacity, 8 | } from 'react-native' 9 | import StatusBarView from "../components/StatusBarView"; 10 | import SnackBar from "../widget/snackbar/SnackBar"; 11 | import Toast from "../widget/toast/Toast"; 12 | 13 | const main_color = '#3aafff' 14 | 15 | const snackBar1 = { 16 | data: 'Sow nothing, reap nothing', 17 | position: SnackBar.position.TOP, 18 | backgroundColor: '#ffcb06', 19 | textColor: '#ffffff', 20 | } 21 | 22 | const snackBar2 = { 23 | data: 'Sow nothing, reap nothing', 24 | position: SnackBar.position.TOP, 25 | duration: SnackBar.duration.INDEFINITE, 26 | backgroundColor: '#7a38ff', 27 | textColor: '#1bffce', 28 | 29 | } 30 | 31 | const snackBar3 = { 32 | data: 'Sow nothing, reap nothing', 33 | position: SnackBar.position.BOTTOM, 34 | backgroundColor: '#ff8a14', 35 | textColor: '#ffffff', 36 | } 37 | 38 | const snackBar4 = { 39 | data: 'Please check the network first.', 40 | position: SnackBar.position.BOTTOM, 41 | duration: SnackBar.duration.LONG, 42 | textColor: '#ff490b', 43 | backgroundColor: '#050405', 44 | actionText: 'Sure', 45 | height: 64, 46 | actionTextColor: '#ff490b', 47 | onActionHide: (isSlideHide) => { 48 | Toast.show({data:isSlideHide ? 'Sliding hide' : 'Click to hide'}) 49 | }, 50 | } 51 | 52 | const snackBar5 = { 53 | data: 'Hello World', 54 | position: SnackBar.position.TOP, 55 | duration: SnackBar.duration.INDEFINITE, 56 | textColor: '#000', 57 | backgroundColor: '#3aafff', 58 | height: 48, 59 | actionText: 'YES', 60 | actionTextColor: '#000', 61 | onActionHide: (isSlideHide) => { 62 | Toast.show({data:isSlideHide ? 'Sliding hide' : 'Click to hide'}) 63 | }, 64 | } 65 | 66 | export default class SnackBarPage extends Component{ 67 | 68 | render() { 69 | return ( 70 | 71 | 72 | 73 | 74 | {this.props.navigation.goBack()}}> 75 | {'Back'} 76 | 77 | {'SnackBar'} 78 | 79 | 80 | {this._renderBtnView('Top SHORT', snackBar1)} 81 | {this._renderBtnView('Top INDEFINITE', snackBar2)} 82 | {this._renderBtnView('Bottom LONG',snackBar3)} 83 | 84 | 85 | {this._renderBtnView('Button Top INDEFINITE',snackBar5)} 86 | {this._renderBtnView('Button Bottom LONG',snackBar4)} 87 | 88 | 89 | 90 | ) 91 | } 92 | 93 | _renderBtnView = (btnText,snackBarOpts) => { 94 | return ( 95 | { 98 | SnackBar.show(snackBarOpts) 99 | }} 100 | > 101 | {btnText} 102 | 103 | ) 104 | } 105 | 106 | } 107 | 108 | const styles = StyleSheet.create({ 109 | safe_container: { 110 | flex: 1, 111 | backgroundColor: main_color 112 | }, 113 | container: { 114 | flex: 1, 115 | backgroundColor: '#e0e0e0' 116 | }, 117 | header: { 118 | height: 44, 119 | backgroundColor: main_color, 120 | justifyContent: 'center', 121 | alignItems: 'center', 122 | marginBottom: 40 123 | }, 124 | header_back: { 125 | color: 14, 126 | marginLeft: 5, 127 | height: 44, 128 | justifyContent: 'center', 129 | paddingLeft: 5, 130 | paddingRight: 5, 131 | position: 'absolute', 132 | top: 0, 133 | left: 0 134 | }, 135 | item: { 136 | flexDirection: 'row', 137 | justifyContent: 'center', 138 | flexWrap: 'wrap' 139 | }, 140 | btn_view: { 141 | height: 36, 142 | backgroundColor: main_color, 143 | borderRadius: 2, 144 | paddingLeft: 10, 145 | paddingRight: 10, 146 | justifyContent: 'center', 147 | alignItems: 'center', 148 | margin: 10 149 | }, 150 | btn_text: { 151 | fontSize: 14, 152 | color: '#fff' 153 | } 154 | }) 155 | -------------------------------------------------------------------------------- /Example/src/styles/BaseStyle.js: -------------------------------------------------------------------------------- 1 | import {StatusBar} from "react-native"; 2 | 3 | export const shadow_style_top = { 4 | elevation: 2, 5 | 6 | shadowOffset:{ width: 0, height: 1}, 7 | shadowColor:'black', 8 | shadowOpacity:0.2, 9 | shadowRadius:1, 10 | } 11 | 12 | export const shadow_style_bottom = { 13 | elevation: 2, 14 | 15 | shadowOffset:{ width:0, height: -1 }, 16 | shadowColor:'black', 17 | shadowOpacity:0.2, 18 | shadowRadius:1, 19 | } 20 | 21 | export const statusBarHeight = StatusBar.currentHeight || 0 22 | -------------------------------------------------------------------------------- /Example/src/widget/data/Constants.js: -------------------------------------------------------------------------------- 1 | import {Easing} from 'react-native' 2 | 3 | const ToastDuration = { 4 | LONG: 3500, 5 | SHORT: 2000 6 | } 7 | 8 | const ToastInHeight = 120 9 | 10 | const ToastInOutDuration = 240 11 | 12 | const ToastPosition = { 13 | TOP: 1, 14 | CENTER: 0, 15 | BOTTOM: -1 16 | } 17 | 18 | const ToastDefaultOpt = { 19 | data: '', //required 20 | duration: ToastDuration.SHORT, 21 | position: ToastPosition.BOTTOM, 22 | inEasing: Easing.elastic(1), 23 | textColor: 'white', 24 | backgroundColor: 'black', 25 | icon: undefined, 26 | isShowShadow: true, 27 | } 28 | 29 | // --------- 30 | 31 | const SnackBarDuration = { 32 | LONG: 3500, 33 | SHORT: 2000, 34 | INDEFINITE: -1 35 | } 36 | 37 | const SnackBarPosition = { 38 | TOP: 1, 39 | BOTTOM: -1 40 | } 41 | 42 | 43 | const SnackBarDefaultOpt = { 44 | data: '', //required 45 | statusBarHeight: -1, 46 | height: 44, 47 | duration: SnackBarDuration.SHORT, 48 | position: SnackBarPosition.BOTTOM, 49 | inEasing: Easing.linear, 50 | textColor: 'white', 51 | backgroundColor: 'black', 52 | actionText: undefined, 53 | actionTextColor: 'white', 54 | isAllowSlideExit: true, 55 | onActionHide: undefined, 56 | isShowShadow: true, 57 | numberOfLines: 1, 58 | } 59 | 60 | const SnackBarInOutDuration = 200 61 | 62 | // -------- 63 | const ModalDefaultOpt = { 64 | data: '', //required 65 | position: ToastPosition.BOTTOM, 66 | inEasing: Easing.elastic(1), 67 | textColor: 'white', 68 | backgroundColor: 'black', 69 | icon: undefined, 70 | onRequestClose: undefined 71 | } 72 | 73 | export { 74 | ToastDuration,ToastInOutDuration,ToastInHeight,ToastPosition,ToastDefaultOpt, 75 | SnackBarDuration,SnackBarPosition,SnackBarDefaultOpt,SnackBarInOutDuration, 76 | ModalDefaultOpt 77 | } 78 | -------------------------------------------------------------------------------- /Example/src/widget/index.js: -------------------------------------------------------------------------------- 1 | import WToast from './toast/Toast' 2 | import WSnackBar from './snackbar/SnackBar' 3 | import WModal from './modaltoast/ModalToast' 4 | 5 | export {WModal,WSnackBar,WToast} 6 | -------------------------------------------------------------------------------- /Example/src/widget/modaltoast/ModalShowToastView.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ToastView from "../toast/ToastView" 3 | import {ToastInOutDuration,ToastDefaultOpt} from '../data/Constants' 4 | 5 | export default class ModalShowToastView extends React.Component { 6 | 7 | constructor(props) { 8 | super(props); 9 | 10 | props.toastInstance(this.show) 11 | this.toastTemp = null; 12 | this.state = { 13 | toastOpts: null, 14 | } 15 | } 16 | 17 | render() { 18 | const toastOpts = this.state.toastOpts; 19 | if (toastOpts == null || toastOpts.data == null) return null; 20 | 21 | return 22 | } 23 | 24 | componentWillUnmount() { 25 | if (this.liftCycleManage) { 26 | clearTimeout(this.liftCycleManage) 27 | } 28 | } 29 | 30 | show = (Opts) => { 31 | 32 | const data = Opts.data 33 | if (typeof data !== 'string' || data.length <= 0) { 34 | return 35 | } 36 | 37 | if (this.state.toastOpts != null) { 38 | this.toastTemp = Opts; 39 | this.hide(); 40 | return; 41 | } 42 | 43 | const toastOpts = Object.assign({}, ToastDefaultOpt, Opts) 44 | 45 | this.setState({ 46 | toastOpts 47 | },() => { 48 | this.liftCycleManage = setTimeout(() => { 49 | this.hide() 50 | }, toastOpts.duration + ToastInOutDuration * 2) 51 | }) 52 | } 53 | 54 | hide = () => { 55 | if (this.liftCycleManage) { 56 | clearTimeout(this.liftCycleManage); 57 | } 58 | if (this.state.toastOpts != null) { 59 | this.setState({ toastOpts: null }, () => { 60 | const toastTemp = this.toastTemp; 61 | if (toastTemp != null) { 62 | this.toastTemp = null; 63 | this.show(toastTemp); 64 | } 65 | }) 66 | } 67 | } 68 | 69 | } 70 | 71 | -------------------------------------------------------------------------------- /Example/src/widget/modaltoast/ModalToast.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import RootSiblings from 'react-native-root-siblings' 3 | import ModalToastView from "./ModalToastView" 4 | import {ToastPosition,ModalDefaultOpt} from '../data/Constants' 5 | 6 | let rootSiblings = undefined 7 | let liftCycleManage = undefined 8 | export default class ModalToast { 9 | 10 | static position = ToastPosition 11 | 12 | static show = (modalOpts) => { 13 | 14 | const data = modalOpts.data 15 | if (typeof data !== 'string' || data.length <= 0) { 16 | return 17 | } 18 | 19 | modalOpts = Object.assign({},ModalDefaultOpt,modalOpts) 20 | 21 | ModalToast.hide(); 22 | 23 | rootSiblings = new RootSiblings( 24 | 25 | ) 26 | } 27 | 28 | static update = (modalOpts) => { 29 | const data = modalOpts.data 30 | if (typeof data !== 'string' || data.length <= 0) { 31 | return 32 | } 33 | 34 | modalOpts = Object.assign({},ModalDefaultOpt,modalOpts) 35 | 36 | ModalToast.hide(); 37 | 38 | modalOpts.isUpdate = true 39 | 40 | rootSiblings = new RootSiblings( 41 | 42 | ) 43 | } 44 | 45 | static hide = () => { 46 | if (liftCycleManage) { 47 | clearTimeout(liftCycleManage) 48 | liftCycleManage = undefined 49 | } 50 | if (rootSiblings) { 51 | rootSiblings.destroy() 52 | rootSiblings = undefined 53 | } 54 | } 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /Example/src/widget/modaltoast/ModalToastView.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react' 2 | import { 3 | View, 4 | Text, 5 | StyleSheet, 6 | Dimensions, 7 | Animated, 8 | Modal 9 | } from 'react-native' 10 | import {ToastInOutDuration, ToastInHeight} from '../data/Constants' 11 | 12 | const MaxWidthRatio = 0.8 13 | export default class ModalToastView extends Component{ 14 | 15 | constructor(props) { 16 | super(props) 17 | 18 | const {width,height} = Dimensions.get('window') 19 | 20 | this.state = { 21 | deviceWidth: width, 22 | deviceHeight: height, 23 | animatedValue1: new Animated.Value( props.isUpdate ? 1 : 0), 24 | animatedValue2: new Animated.Value(props.isUpdate ? 1 : 0.2), 25 | } 26 | 27 | // React after 17 28 | Dimensions.addEventListener('change', this.onWindowChange); 29 | } 30 | 31 | componentDidMount() { 32 | this.onLifeCycleManage() 33 | } 34 | 35 | componentWillUnmount() { 36 | if (this.liftCycleAnimated) { 37 | this.liftCycleAnimated.stop() 38 | this.liftCycleAnimated = undefined 39 | } 40 | Dimensions.removeEventListener('change', this.onWindowChange); 41 | } 42 | 43 | render() { 44 | 45 | 46 | const containerStyle = {} 47 | const contentStyle = { 48 | backgroundColor: this.props.backgroundColor, 49 | opacity: this.state.animatedValue2.interpolate({ 50 | inputRange: [0, 1, 2], 51 | outputRange: [0, 1, 0] 52 | }), 53 | minWidth: this.props.icon ? this.state.deviceWidth*0.4 : 0, 54 | maxWidth: this.state.deviceWidth*MaxWidthRatio, 55 | } 56 | 57 | if (this.props.position > 0) { 58 | containerStyle.justifyContent = 'flex-start'; 59 | containerStyle.top = 40 60 | containerStyle.bottom = 0; 61 | contentStyle.marginTop = this.state.animatedValue1.interpolate({ 62 | inputRange: [0, 1], 63 | outputRange: [ToastInHeight/2, 0] 64 | }); 65 | } else if (this.props.position == 0) { 66 | containerStyle.justifyContent = 'center'; 67 | containerStyle.top = 0 68 | containerStyle.bottom = 0; 69 | contentStyle.marginTop = this.state.animatedValue1.interpolate({ 70 | inputRange: [0, 1], 71 | outputRange: [ToastInHeight, 0] 72 | }); 73 | } else if (this.props.position < 0) { 74 | containerStyle.justifyContent = 'flex-end'; 75 | containerStyle.top = 0 76 | containerStyle.bottom = 40; 77 | contentStyle.marginBottom = this.state.animatedValue1.interpolate({ 78 | inputRange: [0, 1], 79 | outputRange: [0, ToastInHeight/2] 80 | }); 81 | } 82 | 83 | 84 | 85 | return ( 86 | 92 | 93 | 94 | {this.props.icon} 95 | 96 | {this.props.data} 97 | 98 | 99 | 100 | 101 | ) 102 | } 103 | 104 | onLifeCycleManage = () => { 105 | 106 | if (this.liftCycleAnimated) { 107 | this.liftCycleAnimated.stop() 108 | this.liftCycleAnimated = undefined 109 | } 110 | 111 | if (!this.props.isUpdate) { 112 | this.liftCycleAnimated = Animated.parallel([ 113 | Animated.timing( 114 | this.state.animatedValue1, 115 | { 116 | toValue: 1, 117 | duration: ToastInOutDuration, 118 | easing: this.props.inEasing, 119 | useNativeDriver: false, 120 | } 121 | ), 122 | Animated.timing( 123 | this.state.animatedValue2, 124 | { 125 | toValue: 1, 126 | duration: ToastInOutDuration, 127 | useNativeDriver: false, 128 | } 129 | ), 130 | ]) 131 | this.liftCycleAnimated.start() 132 | } 133 | 134 | 135 | } 136 | 137 | onWindowChange = ({ window }) => { 138 | const {width,height} = window 139 | if (width != this.state.deviceWidth && height != this.state.deviceHeight) { 140 | this.setState({ 141 | deviceWidth: width, 142 | deviceHeight: height 143 | }) 144 | } 145 | } 146 | 147 | } 148 | 149 | const styles = StyleSheet.create({ 150 | container: { 151 | alignItems: 'center', 152 | position: 'absolute', 153 | left: 0, 154 | right: 0 155 | }, 156 | content: { 157 | borderRadius: 4, 158 | padding: 10, 159 | alignItems: 'center', 160 | 161 | elevation: 2, 162 | shadowOffset:{ width: 0, height: 1}, 163 | shadowColor:'gray', 164 | shadowOpacity:0.2, 165 | shadowRadius:1, 166 | }, 167 | icon: { 168 | 169 | }, 170 | text: { 171 | fontSize: 16 172 | } 173 | }) 174 | 175 | -------------------------------------------------------------------------------- /Example/src/widget/snackbar/SnackBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import RootSiblings from 'react-native-root-siblings' 3 | import SnackView from "./SnackView" 4 | import {SnackBarDefaultOpt, SnackBarDuration, SnackBarPosition,SnackBarInOutDuration} from "../data/Constants"; 5 | 6 | let rootSiblings = undefined 7 | let liftCycleManage = undefined 8 | export default class SnackBar { 9 | 10 | static duration = SnackBarDuration 11 | static position = SnackBarPosition 12 | 13 | static show = (snackBarOpts) => { 14 | 15 | const data = snackBarOpts.data 16 | if (typeof data !== 'string' || data.length <= 0) { 17 | return 18 | } 19 | 20 | snackBarOpts = Object.assign({},SnackBarDefaultOpt,snackBarOpts) 21 | 22 | SnackBar.hide(); 23 | 24 | rootSiblings = new RootSiblings( 25 | 26 | ) 27 | 28 | if (snackBarOpts.duration !== SnackBarDuration.INDEFINITE) { 29 | liftCycleManage = setTimeout(()=>{ 30 | SnackBar.hide() 31 | }, snackBarOpts.duration+SnackBarInOutDuration*2) 32 | } 33 | } 34 | 35 | static hide = () => { 36 | if (liftCycleManage) { 37 | clearTimeout(liftCycleManage) 38 | liftCycleManage = undefined 39 | } 40 | if (rootSiblings) { 41 | rootSiblings.destroy() 42 | rootSiblings = undefined 43 | } 44 | } 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Example/src/widget/snackbar/SnackView.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react' 2 | import { 3 | View, 4 | Text, 5 | StyleSheet, 6 | Dimensions, 7 | Animated, 8 | TouchableOpacity, 9 | PanResponder, 10 | } from 'react-native' 11 | import {SnackBarDuration, SnackBarInOutDuration, SnackBarPosition} from '../data/Constants' 12 | import {isIphoneX,iosStatusBarXHeight,iosBottomXHeight,defaultStatusBarHeight, shadowBlackstyleTop,shadowBlackStyleBottom} from "../util/UiUtil"; 13 | 14 | class SnackView extends Component{ 15 | 16 | constructor(props) { 17 | super(props) 18 | 19 | const {width,height} = Dimensions.get('window') 20 | this.state = { 21 | deviceWidth: width, 22 | deviceHeight: height, 23 | animatedValue1: new Animated.Value(0), 24 | animatedValue2: new Animated.Value(1), 25 | } 26 | 27 | // React after 17 28 | Dimensions.addEventListener('change', this.onWindowChange); 29 | 30 | this._panResponder = PanResponder.create({ 31 | onStartShouldSetPanResponderCapture: (evt, gestureState) => false, 32 | onMoveShouldSetPanResponderCapture: (evt, gestureState) => false, 33 | onStartShouldSetPanResponder: (evt, gestureState) => true, 34 | onMoveShouldSetPanResponder: (evt, gestureState) => { 35 | let {dx,dy} = gestureState; 36 | if((Math.abs(dx) > 5) || (Math.abs(dy) > 5)){ 37 | return true 38 | }else{ 39 | return false 40 | } 41 | }, 42 | onPanResponderGrant: (evt, gestureState) => {}, 43 | onPanResponderMove: (evt, gestureState) => this.moveTouch(evt, gestureState), 44 | // onPanResponderRelease: (evt, gestureState) => this.endTouch(evt), 45 | onPanResponderTerminationRequest: (evt, gestureState) => false, 46 | onShouldBlockNativeResponder: (evt, gestureState) => false, 47 | }) 48 | } 49 | 50 | componentDidMount() { 51 | this.onLifeCycleManage() 52 | } 53 | 54 | componentWillUnmount() { 55 | this.onRelease() 56 | } 57 | 58 | render() { 59 | 60 | let contentHeight = this.props.height 61 | 62 | const baseStyle = {} 63 | const statusBarStyle = {} 64 | const containerStyle = { 65 | backgroundColor: this.props.backgroundColor, 66 | } 67 | const contentStyles = { 68 | height: contentHeight, 69 | } 70 | 71 | let containerHeight = contentHeight 72 | if (this.props.position === SnackBarPosition.TOP) { 73 | baseStyle.top = 0 74 | 75 | const statusBarHeight = this.getStatusBarHeight() 76 | containerHeight = contentHeight + statusBarHeight 77 | statusBarStyle.height = statusBarHeight 78 | statusBarStyle.backgroundColor = this.props.backgroundColor 79 | 80 | containerStyle.height = containerHeight 81 | containerStyle.top = this.state.animatedValue1.interpolate({ 82 | inputRange: [0, 1], 83 | outputRange: [-containerHeight, 0] 84 | }) 85 | } else { 86 | 87 | if (isIphoneX()) { 88 | containerHeight = containerHeight+iosBottomXHeight 89 | } 90 | 91 | containerStyle.height = containerHeight 92 | 93 | baseStyle.bottom = 0 94 | 95 | containerStyle.bottom = this.state.animatedValue1.interpolate({ 96 | inputRange: [0, 1], 97 | outputRange: [-containerHeight, 0] 98 | }) 99 | } 100 | 101 | let actionView = undefined 102 | if (this.props.actionText) { 103 | actionView = ( 104 | this.onHideAnimated(false)} activeOpacity={0.8}> 108 | {this.props.actionText} 109 | 110 | ) 111 | } 112 | 113 | let shadowStyle = {}; 114 | if (this.props.isShowShadow) { 115 | if (this.props.position === SnackBarPosition.TOP) { 116 | shadowStyle = shadowBlackStyleBottom; 117 | } else { 118 | shadowStyle = shadowBlackstyleTop; 119 | } 120 | } 121 | 122 | return ( 123 | 124 | 125 | 126 | 127 | 131 | {this.props.data} 132 | 133 | {actionView} 134 | 135 | 136 | 137 | ) 138 | } 139 | 140 | moveTouch = (evt, gestureState) => { 141 | if (this.props.isAllowSlideExit) { 142 | if (this.props.position === SnackBarPosition.TOP && gestureState.vy < -0.2) { 143 | this.onHideAnimated(true) 144 | } else if (this.props.position === SnackBarPosition.BOTTOM && gestureState.vy > 0.2) { 145 | this.onHideAnimated(true) 146 | } 147 | } 148 | } 149 | 150 | onLifeCycleManage = () => { 151 | this.inAnimated = Animated.timing( 152 | this.state.animatedValue1, 153 | { 154 | toValue: 1, 155 | duration: SnackBarInOutDuration, 156 | easing: this.props.inEasing, 157 | useNativeDriver: false, 158 | } 159 | ) 160 | 161 | this.inAnimated.start(()=>{ 162 | if (this.props.duration !== SnackBarDuration.INDEFINITE) { 163 | this.commonAnimated = Animated.sequence([ 164 | Animated.timing( 165 | this.state.animatedValue2, 166 | { 167 | toValue: 1, 168 | duration: this.props.duration, 169 | useNativeDriver: false, 170 | } 171 | ), 172 | Animated.timing( 173 | this.state.animatedValue1, 174 | { 175 | toValue: 0, 176 | duration: SnackBarInOutDuration, 177 | useNativeDriver: false, 178 | } 179 | ) 180 | ]) 181 | this.commonAnimated.start() 182 | } 183 | }) 184 | 185 | } 186 | 187 | onHideAnimated = (isSlideHide) => { 188 | if (!this.isHasHide) { 189 | this.isHasHide = true; 190 | } else { 191 | return; 192 | } 193 | if (this.props.onActionHide) this.props.onActionHide(isSlideHide) 194 | this.hideAnimated = Animated.timing( 195 | this.state.animatedValue1, 196 | { 197 | toValue: 0, 198 | duration: SnackBarInOutDuration, 199 | useNativeDriver: false, 200 | } 201 | ) 202 | this.hideAnimated.start(()=>{ 203 | this.onRelease() 204 | }) 205 | } 206 | 207 | onWindowChange = ({ window }) => { 208 | const {width,height} = window 209 | if (width != this.state.deviceWidth && height != this.state.deviceHeight) { 210 | this.setState({ 211 | deviceWidth: width, 212 | deviceHeight: height 213 | }) 214 | } 215 | } 216 | 217 | onRelease = () => { 218 | if (this.inAnimated) { 219 | this.inAnimated.stop() 220 | this.inAnimated = undefined 221 | } 222 | if (this.commonAnimated) { 223 | this.commonAnimated.stop() 224 | this.commonAnimated = undefined 225 | } 226 | if (this.hideAnimated) { 227 | this.hideAnimated.stop() 228 | this.hideAnimated = undefined 229 | } 230 | Dimensions.removeEventListener('change', this.onWindowChange); 231 | } 232 | 233 | getStatusBarHeight = () => { 234 | if (this.props.statusBarHeight < 0) { 235 | if (isIphoneX()) { 236 | return iosStatusBarXHeight 237 | } else { 238 | return defaultStatusBarHeight 239 | } 240 | } else { 241 | return this.props.statusBarHeight 242 | } 243 | } 244 | 245 | } 246 | 247 | const styles = StyleSheet.create({ 248 | base: { 249 | position: 'absolute', 250 | left: 0, 251 | right: 0, 252 | }, 253 | container: { 254 | flexDirection: 'column' 255 | }, 256 | content: { 257 | flexDirection: 'row', 258 | justifyContent: 'space-between', 259 | alignItems: 'center', 260 | paddingLeft: 15, 261 | paddingRight: 15 262 | }, 263 | content_title: { 264 | fontSize: 16, 265 | marginRight: 10 266 | }, 267 | content_btn: { 268 | justifyContent: 'center', 269 | alignItems: 'center', 270 | } 271 | }) 272 | 273 | export default SnackView 274 | 275 | 276 | -------------------------------------------------------------------------------- /Example/src/widget/toast/Toast.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import RootSiblings from 'react-native-root-siblings' 3 | import ToastView from "./ToastView" 4 | import {ToastDuration,ToastInOutDuration,ToastPosition,ToastDefaultOpt} from '../data/Constants' 5 | 6 | let rootSiblings = undefined 7 | let liftCycleManage = undefined 8 | export default class Toast { 9 | 10 | static duration = ToastDuration 11 | static position = ToastPosition 12 | 13 | static show = (toastOpts) => { 14 | 15 | const data = toastOpts.data 16 | if (typeof data !== 'string' || data.length <= 0) { 17 | return 18 | } 19 | 20 | toastOpts = Object.assign({},ToastDefaultOpt,toastOpts) 21 | 22 | Toast.hide(); 23 | 24 | rootSiblings = new RootSiblings( 25 | 26 | ) 27 | 28 | liftCycleManage = setTimeout(()=>{ 29 | Toast.hide() 30 | }, toastOpts.duration+ToastInOutDuration*2) 31 | } 32 | 33 | static hide = () => { 34 | if (liftCycleManage) { 35 | clearTimeout(liftCycleManage) 36 | liftCycleManage = undefined 37 | } 38 | if (rootSiblings) { 39 | rootSiblings.destroy() 40 | rootSiblings = undefined 41 | } 42 | } 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Example/src/widget/toast/ToastView.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react' 2 | import { 3 | View, 4 | Text, 5 | StyleSheet, 6 | Dimensions, 7 | Animated 8 | } from 'react-native' 9 | import {ToastInOutDuration, ToastInHeight} from '../data/Constants' 10 | import { shadowBlackStyleBottom } from '../util/UiUtil' 11 | 12 | const MaxWidthRatio = 0.8 13 | export default class ToastView extends Component{ 14 | 15 | constructor(props) { 16 | super(props) 17 | 18 | const {width,height} = Dimensions.get('window') 19 | 20 | this.state = { 21 | deviceWidth: width, 22 | deviceHeight: height, 23 | animatedValue1: new Animated.Value(0), 24 | animatedValue2: new Animated.Value(0.2), 25 | } 26 | 27 | // React after 17 28 | Dimensions.addEventListener('change', this.onWindowChange); 29 | } 30 | 31 | componentDidMount() { 32 | this.onLifeCycleManage() 33 | } 34 | 35 | componentWillUnmount() { 36 | if (this.liftCycleAnimated) { 37 | this.liftCycleAnimated.stop() 38 | this.liftCycleAnimated = undefined 39 | } 40 | Dimensions.removeEventListener('change', this.onWindowChange); 41 | } 42 | 43 | render() { 44 | 45 | const containerStyle = {} 46 | const contentStyle = { 47 | backgroundColor: this.props.backgroundColor, 48 | opacity: this.state.animatedValue2.interpolate({ 49 | inputRange: [0, 1, 2], 50 | outputRange: [0, 1, 0] 51 | }), 52 | minWidth: this.props.icon ? this.state.deviceWidth*0.4 : 0, 53 | maxWidth: this.state.deviceWidth*MaxWidthRatio, 54 | } 55 | 56 | if (this.props.position > 0) { 57 | containerStyle.justifyContent = 'flex-start'; 58 | containerStyle.top = 40 59 | containerStyle.bottom = 0; 60 | contentStyle.marginTop = this.state.animatedValue1.interpolate({ 61 | inputRange: [0, 1], 62 | outputRange: [ToastInHeight/2, 0] 63 | }); 64 | } else if (this.props.position == 0) { 65 | containerStyle.justifyContent = 'center'; 66 | containerStyle.top = 0 67 | containerStyle.bottom = 0; 68 | contentStyle.marginTop = this.state.animatedValue1.interpolate({ 69 | inputRange: [0, 1], 70 | outputRange: [ToastInHeight, 0] 71 | }); 72 | } else if (this.props.position < 0) { 73 | containerStyle.justifyContent = 'flex-end'; 74 | containerStyle.top = 0 75 | containerStyle.bottom = 40; 76 | contentStyle.marginBottom = this.state.animatedValue1.interpolate({ 77 | inputRange: [0, 1], 78 | outputRange: [0, ToastInHeight/2] 79 | }); 80 | } 81 | 82 | let shadowStyle = {}; 83 | if (this.props.isShowShadow) shadowStyle = shadowBlackStyleBottom; 84 | 85 | 86 | return ( 87 | 88 | 89 | {this.props.icon} 90 | 91 | {this.props.data} 92 | 93 | 94 | 95 | ) 96 | } 97 | 98 | onLifeCycleManage = () => { 99 | 100 | if (this.liftCycleAnimated) { 101 | this.liftCycleAnimated.stop() 102 | this.liftCycleAnimated = undefined 103 | } 104 | this.liftCycleAnimated = Animated.sequence([ 105 | Animated.parallel([ 106 | Animated.timing( 107 | this.state.animatedValue1, 108 | { 109 | toValue: 1, 110 | duration: ToastInOutDuration, 111 | easing: this.props.inEasing, 112 | useNativeDriver: false, 113 | } 114 | ), 115 | Animated.timing( 116 | this.state.animatedValue2, 117 | { 118 | toValue: 1, 119 | duration: ToastInOutDuration, 120 | useNativeDriver: false, 121 | } 122 | ), 123 | ]), 124 | Animated.timing( 125 | this.state.animatedValue2, 126 | { 127 | toValue: 1, 128 | duration: this.props.duration, 129 | useNativeDriver: false, 130 | } 131 | ), 132 | Animated.timing( 133 | this.state.animatedValue2, 134 | { 135 | toValue: 2, 136 | duration: ToastInOutDuration, 137 | useNativeDriver: false, 138 | } 139 | ) 140 | ]) 141 | this.liftCycleAnimated.start() 142 | } 143 | 144 | onWindowChange = ({ window }) => { 145 | const {width,height} = window 146 | if (width != this.state.deviceWidth && height != this.state.deviceHeight) { 147 | this.setState({ 148 | deviceWidth: width, 149 | deviceHeight: height 150 | }) 151 | } 152 | } 153 | 154 | } 155 | 156 | const styles = StyleSheet.create({ 157 | container: { 158 | alignItems: 'center', 159 | position: 'absolute', 160 | left: 0, 161 | right: 0 162 | }, 163 | content: { 164 | borderRadius: 4, 165 | padding: 10, 166 | alignItems: 'center' 167 | }, 168 | icon: { 169 | 170 | }, 171 | text: { 172 | fontSize: 16 173 | } 174 | }) 175 | 176 | -------------------------------------------------------------------------------- /Example/src/widget/util/UiUtil.js: -------------------------------------------------------------------------------- 1 | import {Platform,Dimensions} from 'react-native' 2 | 3 | const screenW = Dimensions.get('window').width 4 | const screenH = Dimensions.get('window').height 5 | 6 | export const defaultStatusBarHeight = 20 7 | export const iosStatusBarXHeight = 44 8 | export const iosBottomXHeight = 34 9 | 10 | const IPHONEX_WIDTH = 375 11 | const IPHONEX_HEIGHT = 812 12 | const IPHONEX_MAX_WIDTH = 414 13 | const IPHONEX_MAX_HEIGHT = 896 14 | 15 | export const isIphoneX = () => { 16 | if (Platform.OS === 'ios') { 17 | if ( 18 | ((screenH === IPHONEX_HEIGHT && screenW === IPHONEX_WIDTH) || (screenH === IPHONEX_WIDTH && screenW === IPHONEX_HEIGHT)) || 19 | ((screenH === IPHONEX_MAX_HEIGHT && screenW === IPHONEX_MAX_WIDTH) || (screenH === IPHONEX_MAX_WIDTH && screenW === IPHONEX_MAX_HEIGHT)) 20 | ) { 21 | return true 22 | } 23 | } 24 | return false 25 | } 26 | 27 | export const shadowBlackStyleBottom = { 28 | elevation: 4, 29 | 30 | shadowOffset: { 31 | width: 0, 32 | height: -2 33 | }, 34 | shadowColor: '#000', 35 | shadowOpacity: 0.4, 36 | shadowRadius: 2 37 | } 38 | 39 | export const shadowBlackstyleTop = { 40 | elevation: 4, 41 | 42 | shadowOffset: { 43 | width: 0, 44 | height: 2 45 | }, 46 | shadowColor: '#000', 47 | shadowOpacity: 0.4, 48 | shadowRadius: 2 49 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-smart-tip 2 | React-native smart tip, including Toast、Modal、SnackBar 3 | 4 | ![GitHub license](https://img.shields.io/badge/license-MIT-green.svg) 5 | [![npm](https://img.shields.io/npm/v/react-native-smart-tip.svg?style=flat)](https://npmjs.com/package/react-native-smart-tip) 6 | 7 | ### 2021.4 8 | In react native >= 0.62, the new LogBox component would impact this component's initialization. To make it work we have to explicitly insert a mount point in your app like this 9 | 10 | ``` 11 | import { WRootToastApp } from 'react-native-smart-tip' 12 | 13 | // in your entry file like `App.js` 14 | 15 | return ( 16 | // <- use WRootToastApp to wrap your root component 17 | 18 | 19 | ); 20 | 21 | ``` 22 | 23 | ### 2020.5 24 | 1. Add property isShowShadow to WSnackBar and WToast 25 | 2. Add sliding hide function for WSnackBar 26 | 27 | ### 2020.3 Show Toast above Modal (Compatible with Android and iOS) 28 | 29 | ![](https://user-gold-cdn.xitu.io/2020/3/8/170b855830588e41?w=300&h=500&f=gif&s=489177) 30 | 31 | ``` 32 | import { ModalShowToastView } from 'react-native-smart-tip' 33 | 34 | getToastInstance = (toastInstance) => { 35 | this.toastInstance = toastInstance; 36 | } 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | this.toastInstance({data: 'toast'}) 45 | 46 | ``` 47 | 48 | ### 2019.7 Remove the method in the componentWillMount method. Compatible with future React 17 versions, React-Native@0.6 version. 49 | 50 | ### Installation 51 | ```bash 52 | yarn add react-native-smart-tip 53 | or 54 | npm i react-native-smart-tip --save 55 | ``` 56 | 57 | ![](https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/master/img/main.jpg) 58 | 59 | ### Features 60 | 61 | ##### Toast 62 | 63 | ![](https://github.com/mochixuan/react-native-smart-tip/blob/master/img/toast.gif?raw=true) 64 | 65 | ##### SnackBar 66 | ![](https://user-gold-cdn.xitu.io/2020/4/30/171c959272abe458?w=320&h=533&f=gif&s=355190) 67 | 68 | ##### Modal 69 | ![](https://github.com/mochixuan/react-native-smart-tip/blob/master/img/modal.gif?raw=true) 70 | 71 | ##### Show tips on Modal 72 | > Tip: Modal shows that modal can only be used on Android [issue](https://github.com/facebook/react-native/issues/3445) 73 | 74 | ![](https://github.com/mochixuan/react-native-smart-tip/blob/master/img/modal1.gif?raw=true) 75 | 76 | ### Usage 77 | 78 | ##### WToast 79 | ``` 80 | import {WToast} from 'react-native-smart-tip' 81 | 82 | // Base 83 | show = () => { 84 | WToast.show({data: 'hello world'}) 85 | } 86 | 87 | // Other 88 | show = () => { 89 | const toastOpts = { 90 | data: 'Success', 91 | textColor: '#ffffff', 92 | backgroundColor: '#444444', 93 | duration: WToast.duration.LONG, //1.SHORT 2.LONG 94 | position: WToast.position.TOP, // 1.TOP 2.CENTER 3.BOTTOM 95 | icon: 96 | } 97 | 98 | WToast.show(toastOpts) 99 | } 100 | 101 | WToast.hide(); // Can be hidden immediately 102 | 103 | ``` 104 | ##### WToast API 105 | Props | Type | Required | Default | Description 106 | -------| -------- | -------- | ----------- | ----------- 107 | data | String | true | ' '| Displayed content 108 | duration | Number | false | WToast.duration.SHORT | The duration of the toast 109 | position | Number | false | WToast.position.BOTTOM | Displayed position 110 | inEasing | Easing | false | Easing.elastic(1)| Admission animation 111 | textColor| String | false |'white'| font color 112 | backgroundColor| String | false | 'black' | background color 113 | icon | Component | fasse | undefined | Image to be displayed 114 | isShowShadow | boolean | false | true | Shadow effect 115 | --- 116 | 117 | ##### WSnackBar 118 | ``` 119 | import {WSnackBar} from 'react-native-smart-tip' 120 | 121 | // Base 122 | show = () => { 123 | WSnackBar.show({data: 'hello world'}) 124 | } 125 | 126 | // Other 127 | show = () => { 128 | const snackBarOpts = { 129 | data: 'Please check the network first.', 130 | position: WSnackBar.position.BOTTOM, // 1.TOP 2.CENTER 3.BOTTOM 131 | duration: WSnackBar.duration.LONG, //1.SHORT 2.LONG 3.INDEFINITE 132 | textColor: '#ff490b', 133 | backgroundColor: '#050405', 134 | actionText: 'Sure', 135 | actionTextColor: '#ff490b', 136 | onActionHide: (isSlideHide)=>{ 137 | // Click Action 138 | }, 139 | } 140 | 141 | WSnackBar.show(snackBarOpts) 142 | } 143 | 144 | ``` 145 | 146 | ##### WSnackBar API 147 | Props | Type | Required | Default | Description 148 | -------| -------- | -------- | ----------- | ----------- 149 | data | String | true | ' '| Displayed content 150 | statusBarHeight | Number | false | -1 | Prevent Android statusBar 151 | height | Number | false | 44 | Height to display 152 | duration | Number | false | WSnackBar.duration.SHORT | The duration of the toast 153 | position | Number | false | WSnackBar.position.BOTTOM | Displayed position 154 | inEasing | Easing | false | Easing.linear| Admission animation 155 | textColor| String | false |'white'| font color 156 | backgroundColor| String | false | 'black' | background color 157 | actionText | String | false | undefined | action text 158 | actionTextColor | String | false | 'white' | action text color 159 | isAllowSlideExit|boolean | false | true | Whether to run sliding hide 160 | onActionHide | Function | false | undefined | listener click 161 | isShowShadow | boolean | false | true | Shadow effect 162 | numberOfLines|number| false | 1 | Maximum number of rows 163 | --- 164 | 165 | ##### WModal 166 | ``` 167 | import {WModal} from 'react-native-smart-tip' 168 | 169 | // Base 170 | show = () => { 171 | WModal.show({data: 'hello world'}) 172 | } 173 | 174 | // Other 175 | show = () => { 176 | const modalOpts = { 177 | data: 'Loading', 178 | textColor: '#fff', 179 | backgroundColor: '#444444', 180 | position: WModal.position.CENTER, 181 | icon: 182 | } 183 | 184 | WModal.show(modalOpts) 185 | } 186 | 187 | ``` 188 | ##### WToast API 189 | Props | Type | Required | Default | Description 190 | -------| -------- | -------- | ----------- | ----------- 191 | data | String | true | ' '| Displayed content 192 | position | Number | false | WToast.position.BOTTOM | Displayed position 193 | inEasing | Easing | false | Easing.elastic(1)| Admission animation 194 | textColor| String | false |'white'| font color 195 | backgroundColor| String | false | 'black' | background color 196 | icon | Component | fasse | undefined | Image to be displayed 197 | onRequestClose|Function|false| undefined| Android Back 198 | 199 | ##### MIT Licensed 200 | -------------------------------------------------------------------------------- /img/main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/img/main.jpg -------------------------------------------------------------------------------- /img/modal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/img/modal.gif -------------------------------------------------------------------------------- /img/modal1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/img/modal1.gif -------------------------------------------------------------------------------- /img/snackbar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/img/snackbar.gif -------------------------------------------------------------------------------- /img/toast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mochixuan/react-native-smart-tip/9750ccb727857d73932bc0d9f515ed37e443e10d/img/toast.gif -------------------------------------------------------------------------------- /lib/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /lib/.idea/jsLinters/tslint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/.idea/lib.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /lib/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /lib/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | positon 88 | this.props 89 | actionTextColor 90 | height 91 | 92 | 93 | 94 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | true 110 | DEFINITION_ORDER 111 | 112 | 113 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 |