├── .ghp
└── wix.json
├── .gitignore
├── .npmignore
├── Interactable.podspec
├── LICENSE
├── PROPS.md
├── README.md
├── SUPPORT.md
├── UX-INSPIRATIONS.md
├── android
├── e2e
├── firstTest.spec.js
├── init.js
└── mocha.opts
├── index.js
├── ios
├── lib
├── android
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── proguard-rules.pro
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── wix
│ │ │ │ └── interactable
│ │ │ │ ├── Events.java
│ │ │ │ ├── Interactable.java
│ │ │ │ ├── InteractableArea.java
│ │ │ │ ├── InteractablePoint.java
│ │ │ │ ├── InteractableSpring.java
│ │ │ │ ├── InteractableView.java
│ │ │ │ ├── InteractableViewManager.java
│ │ │ │ ├── RNConvert
│ │ │ │ └── RNConvert.java
│ │ │ │ └── physics
│ │ │ │ ├── PhysicsAnchorBehavior.java
│ │ │ │ ├── PhysicsAnimator.java
│ │ │ │ ├── PhysicsArea.java
│ │ │ │ ├── PhysicsBehavior.java
│ │ │ │ ├── PhysicsBounceBehavior.java
│ │ │ │ ├── PhysicsFrictionBehavior.java
│ │ │ │ ├── PhysicsGravityWellBehavior.java
│ │ │ │ ├── PhysicsObject.java
│ │ │ │ └── PhysicsSpringBehavior.java
│ │ └── res
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── wix
│ │ └── interactable
│ │ └── ExampleUnitTest.java
├── index.js
├── ios
│ ├── Interactable-tvOS
│ │ └── Info.plist
│ ├── Interactable-tvOSTests
│ │ └── Info.plist
│ ├── Interactable.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ ├── Interactable-tvOS.xcscheme
│ │ │ └── Interactable.xcscheme
│ ├── Interactable.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── Interactable
│ │ ├── InteractableArea.h
│ │ ├── InteractableArea.m
│ │ ├── InteractablePoint.h
│ │ ├── InteractablePoint.m
│ │ ├── InteractableSpring.h
│ │ ├── InteractableSpring.m
│ │ ├── InteractableView.h
│ │ ├── InteractableView.m
│ │ ├── InteractableViewManager.h
│ │ ├── InteractableViewManager.m
│ │ ├── PhysicsAnchorBehavior.h
│ │ ├── PhysicsAnchorBehavior.m
│ │ ├── PhysicsAnimator.h
│ │ ├── PhysicsAnimator.m
│ │ ├── PhysicsArea.h
│ │ ├── PhysicsArea.m
│ │ ├── PhysicsBehavior.h
│ │ ├── PhysicsBehavior.m
│ │ ├── PhysicsBounceBehavior.h
│ │ ├── PhysicsBounceBehavior.m
│ │ ├── PhysicsFrictionBehavior.h
│ │ ├── PhysicsFrictionBehavior.m
│ │ ├── PhysicsGravityWellBehavior.h
│ │ ├── PhysicsGravityWellBehavior.m
│ │ ├── PhysicsObject.h
│ │ ├── PhysicsObject.m
│ │ ├── PhysicsSpringBehavior.h
│ │ ├── PhysicsSpringBehavior.m
│ │ ├── RCTConvert+Interactable.h
│ │ └── RCTConvert+Interactable.m
│ └── InteractableTests
│ │ ├── Info.plist
│ │ └── InteractableTests.m
└── src
│ └── InteractableView.js
├── logos
├── circle-notitle.png
├── circle-title.png
├── square-notitle.png
└── square-title.png
├── package.json
├── playground
├── .babelrc-IGNORE
├── .buckconfig
├── .flowconfig
├── android
│ ├── app
│ │ ├── BUCK
│ │ ├── build.gradle
│ │ ├── build_defs.bzl
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── androidTest
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── playground
│ │ │ │ └── DetoxTest.java
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── playground
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── MainApplication.java
│ │ │ └── res
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ └── values
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── playground.keystore
│ └── settings.gradle
├── app.json
├── assets
│ ├── airport-photo.jpg
│ ├── calendar-body.png
│ ├── calendar-header.png
│ ├── card-photo.jpg
│ ├── chatheads-delete.png
│ ├── chatheads-face1.jpg
│ ├── chatheads-face2.jpg
│ ├── icon-check.png
│ ├── icon-clock.png
│ ├── icon-menu.png
│ ├── icon-trash.png
│ ├── icon-up.png
│ ├── map-bg.jpg
│ └── tinder-photo.jpg
├── babel.config.js
├── index.js
├── ios
│ ├── Playground-tvOS
│ │ └── Info.plist
│ ├── Playground-tvOSTests
│ │ └── Info.plist
│ ├── PlaygroundTests
│ │ ├── Info.plist
│ │ └── PlaygroundTests.m
│ ├── Podfile
│ ├── Podfile.lock
│ ├── playground.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ ├── playground-tvOS.xcscheme
│ │ │ └── playground.xcscheme
│ ├── playground.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── playground
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Base.lproj
│ │ └── LaunchScreen.xib
│ │ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ │ ├── Info.plist
│ │ └── main.m
├── metro.config.js
└── src
│ ├── app.js
│ ├── examples
│ ├── AlertAreas.js
│ ├── ChangePosition.js
│ ├── ChatHeads.js
│ ├── CollapsingHeader.js
│ ├── CollapsingHeaderWithScroll.js
│ ├── HandleRelayout.js
│ ├── HandleTouches.js
│ ├── IconDrawer.js
│ ├── MoreChatHeads.js
│ ├── MoreDrawers.js
│ ├── SideMenu.js
│ ├── SnapTo.js
│ ├── SwipeableCard.js
│ ├── TouchesInside.js
│ └── TouchesInsideStatic.js
│ └── real-life-examples
│ ├── CollapsibleCalendar.js
│ ├── CollapsibleFilter.js
│ ├── Documentation.js
│ ├── MapPanel.js
│ ├── NotifPanel.js
│ ├── NowCard.js
│ ├── RealChatHeads.js
│ ├── RowActions1.js
│ ├── RowActions2.js
│ ├── TinderCard.js
│ └── UxInspirations.js
├── rn-cli.config.js
├── scripts
├── ci.android.sh
└── ci.ios.sh
└── typings
└── react-native-interactable.d.ts
/.ghp/wix.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-interactable",
3 | "description": "Declarative API for handling fluid user interactions with views at 60 FPS in React Native",
4 | "title": "React Native Interactable",
5 | "github": "https://github.com/wix/react-native-interactable",
6 | "AndroidVideoUrl": "https://github.com/wix/react-native/blob/master/src/videos/react-interactable-android.mp4?raw=true",
7 | "IOSVideoUrl": "https://github.com/wix/react-native/blob/master/src/videos/react-iphone-interactable.mp4?raw=true",
8 | "IOSDemoAppLink": "https://itunes.apple.com/us/app/react-native-interactions/id1209875831?mt=8",
9 | "image": "",
10 | "poster": "https://github.com/wix/react-native/blob/master/src/img/react-native-interactable-poster.jpg?raw=true",
11 | "size" : "normal",
12 | "AndroidDemoAppLink": "https://play.google.com/store/apps/details?id=com.wix.interactions&hl=en"
13 | }
14 |
--------------------------------------------------------------------------------
/.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/IJ
26 | #
27 | *.iml
28 | .idea
29 | .gradle
30 | local.properties
31 | android/.settings/*
32 | android/.project
33 | lib/android/.project
34 | playground/android/app/.project
35 | playground/android/.project
36 |
37 | # node.js
38 | #
39 | node_modules/
40 | npm-debug.log
41 |
42 | # npm
43 | package-lock.json
44 |
45 | # yarn
46 | yarn.lock
47 | yarn-error.log
48 |
49 | # BUCK
50 | buck-out/
51 | \.buckd/
52 | android/app/libs
53 | *.keystore
54 | !debug.keystore
55 |
56 | # Bundle artifact
57 | *.jsbundle
58 |
59 | # CocoaPods
60 | playground/ios/Pods
61 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .ghp/
2 |
3 |
4 | playground
5 | e2e
6 | logos
7 | ./index.ios.js
8 | ./index.android.js
9 | ./rn-cli.config.js
10 |
11 | #################
12 | # from .gitignore:
13 | ################
14 |
15 |
16 | ############
17 | # Node
18 | ############
19 | # Logs
20 | logs
21 | *.log
22 | npm-debug.log*
23 |
24 | # Runtime data
25 | pids
26 | *.pid
27 | *.seed
28 |
29 | # Directory for instrumented libs generated by jscoverage/JSCover
30 | lib-cov
31 |
32 | # Coverage directory used by tools like istanbul
33 | coverage
34 |
35 | # nyc test coverage
36 | .nyc_output
37 |
38 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
39 | .grunt
40 |
41 | # node-waf configuration
42 | .lock-wscript
43 |
44 | # Compiled binary addons (http://nodejs.org/api/addons.html)
45 | build/Release
46 |
47 | # Dependency directories
48 | node_modules
49 | jspm_packages
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional REPL history
55 | .node_repl_history
56 |
57 | ################
58 | # JetBrains
59 | ################
60 | .idea
61 |
62 | ## File-based project format:
63 | *.iws
64 |
65 | ## Plugin-specific files:
66 |
67 | # IntelliJ
68 | /out/
69 |
70 | # mpeltonen/sbt-idea plugin
71 | .idea_modules/
72 |
73 | # JIRA plugin
74 | atlassian-ide-plugin.xml
75 |
76 | # Crashlytics plugin (for Android Studio and IntelliJ)
77 | com_crashlytics_export_strings.xml
78 | crashlytics.properties
79 | crashlytics-build.properties
80 | fabric.properties
81 |
82 |
83 | ############
84 | # iOS
85 | ############
86 | # Xcode
87 | #
88 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
89 |
90 | #Cocoapods
91 | # *.podspec
92 |
93 | ## Build generated
94 | ios/build/
95 | ios/DerivedData/
96 |
97 | ## Various settings
98 | *.pbxuser
99 | !default.pbxuser
100 | *.mode1v3
101 | !default.mode1v3
102 | *.mode2v3
103 | !default.mode2v3
104 | *.perspectivev3
105 | !default.perspectivev3
106 | ios/xcuserdata/
107 |
108 | ## Other
109 | *.moved-aside
110 | *.xcuserstate
111 |
112 | ## Obj-C/Swift specific
113 | *.hmap
114 | *.ipa
115 | *.dSYM.zip
116 | *.dSYM
117 |
118 | # CocoaPods
119 | #
120 | # We recommend against adding the Pods directory to your .gitignore. However
121 | # you should judge for yourself, the pros and cons are mentioned at:
122 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
123 | #
124 | ios/Pods/
125 |
126 | # Carthage
127 | #
128 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
129 | # Carthage/Checkouts
130 |
131 | Carthage/Build
132 |
133 | # fastlane
134 | #
135 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
136 | # screenshots whenever they are needed.
137 | # For more information about the recommended setup visit:
138 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
139 |
140 | fastlane/report.xml
141 | fastlane/screenshots
142 |
143 |
144 | ############
145 | # Android
146 | ############
147 | # Built application files
148 | *.apk
149 | *.ap_
150 |
151 | # Files for the Dalvik VM
152 | *.dex
153 |
154 | # Java class files
155 | *.class
156 |
157 | # Generated files
158 | android/bin/
159 | android/gen/
160 | android/out/
161 | android/app/build/
162 | lib/android/gradle/
163 |
164 | # Gradle files
165 | android/.gradle/
166 | android/build/
167 |
168 | # Local configuration file (sdk path, etc)
169 | local.properties
170 |
171 | # Proguard folder generated by Eclipse
172 | android/proguard/
173 |
174 | # Log Files
175 | *.log
176 |
177 | # Android Studio Navigation editor temp files
178 | android/.navigation/
179 |
180 | # Android Studio captures folder
181 | android/captures/
182 |
183 | # Intellij
184 | *.iml
185 |
186 | # Keystore files
187 | *.jks
188 |
189 | ##################
190 | # React-Native
191 | ##################
192 | # OSX
193 | #
194 | .DS_Store
195 |
196 | # Xcode
197 | #
198 | build/
199 | *.pbxuser
200 | !default.pbxuser
201 | *.mode1v3
202 | !default.mode1v3
203 | *.mode2v3
204 | !default.mode2v3
205 | *.perspectivev3
206 | !default.perspectivev3
207 | xcuserdata
208 | *.xccheckout
209 | *.moved-aside
210 | DerivedData
211 | *.hmap
212 | *.ipa
213 | *.xcuserstate
214 | project.xcworkspace
215 |
216 | # Android/IJ
217 | #
218 | .idea
219 | .gradle
220 | local.properties
221 |
222 | # node.js
223 | #
224 | node_modules/
225 | npm-debug.log
226 |
227 | # BUCK
228 | buck-out/
229 | \.buckd/
230 | android/app/libs
231 | android/keystores/debug.keystore
232 |
233 | example
234 | real-life-example
235 | android/build
236 |
--------------------------------------------------------------------------------
/Interactable.podspec:
--------------------------------------------------------------------------------
1 | require 'json'
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "Interactable"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 |
10 | s.homepage = "https://github.com/wix/react-native-interactable"
11 |
12 | s.license = "MIT"
13 | s.authors = package["author"]
14 | s.platform = :ios, "7.0"
15 |
16 | s.source = { :git => "https://github.com/wix/react-native-interactable.git" }
17 | s.source_files = "lib/ios/Interactable/*.{h,m}"
18 |
19 | s.dependency 'React'
20 | end
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Wix.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # Support
2 |
3 | ### Issues
4 |
5 | If you have any issues with the app please open an issue [here](https://github.com/wix/react-native-interactable/issues)
6 |
7 | ### Twitter
8 |
9 | Contact the developer via twitter at `@koltal`
10 |
11 | ### Contact
12 |
13 | http://www.wix.engineering
14 |
15 | or via email academy@wix.com
16 |
--------------------------------------------------------------------------------
/UX-INSPIRATIONS.md:
--------------------------------------------------------------------------------
1 | # UX Inspirations
2 |
3 | The [showcase app](real-life-example) contains several demonstrations that draw inspiration from beautiful real life apps that we really like. The purpose of the showcase app is to learn from these use-cases and see how they could have been implemented with this library.
4 |
5 | These are the great apps that inspired the demonstrations:
6 |
7 | * **Row Actions (Google Style)**
8 |
[Inbox by Gmail](https://itunes.apple.com/us/app/inbox-by-gmail/id905060486?mt=8) app by Google Inc.
9 |
10 | * **Row Actions (Apple Style)**
11 |
The official iOS Mail app by Apple
12 |
13 | * **Google Now-Style Card**
14 |
[Google Now](https://itunes.apple.com/us/app/google-search-made-just-for-mobile/id284815942?mt=8) app by Google Inc.
15 |
16 | * **Tinder-Style Card**
17 |
[Flic](https://itunes.apple.com/us/app/flic-delete-manage-camera-roll-easily-delete-photos/id918263212?mt=8) app by Lifehack Labs
18 |
[Tinder](https://itunes.apple.com/us/app/tinder/id547702041?mt=8) app by Tinder Inc.
19 |
20 | * **Notification Panel**
21 |
The official iOS top notification panel by Apple
22 |
23 | * **Apple Maps-Style Panel**
24 |
The official iOS Maps app by Apple
25 |
26 | * **Collapsible Filter**
27 |
[Airbnb](https://itunes.apple.com/us/app/airbnb/id401626263?mt=8) app by Airbnb Inc.
28 |
29 | * **Collapsible Calendar (Any.do-Style)**
30 |
[Cal](https://itunes.apple.com/us/app/cal-shared-calendar-daily-organizer-weekly-planner/id648287824?mt=8) app by Any.DO
31 |
32 | * **Chat Heads**
33 |
[Messenger](https://play.google.com/store/apps/details?id=com.facebook.orca&hl=en) app by Facebook
34 |
35 | ### Disclaimer
36 |
37 | The showcase app is an independent learning project and has not been authorized, sponsored, or otherwise approved by the creators of the inspiring apps listed above.
38 |
--------------------------------------------------------------------------------
/android:
--------------------------------------------------------------------------------
1 | lib/android
--------------------------------------------------------------------------------
/e2e/firstTest.spec.js:
--------------------------------------------------------------------------------
1 | describe('Example', () => {
2 | beforeEach(async () => {
3 | await device.reloadReactNative();
4 | });
5 |
6 | it('should show Overview screen', async () => {
7 | await expect(element(by.id('Overview'))).toBeVisible();
8 | });
9 |
10 | it('Chat Heads is clickable', async () => {
11 | await element(by.text('Chat Heads')).tap();
12 | });
13 |
14 | })
15 |
--------------------------------------------------------------------------------
/e2e/init.js:
--------------------------------------------------------------------------------
1 | const detox = require('detox');
2 | const config = require('../package.json').detox;
3 | const adapter = require('detox/runners/mocha/adapter');
4 |
5 | before(async () => {
6 | await detox.init(config);
7 | });
8 |
9 | beforeEach(async function () {
10 | await adapter.beforeEach(this);
11 | });
12 |
13 | afterEach(async function () {
14 | await adapter.afterEach(this);
15 | });
16 |
17 | after(async () => {
18 | await detox.cleanup();
19 | });
20 |
--------------------------------------------------------------------------------
/e2e/mocha.opts:
--------------------------------------------------------------------------------
1 | --recursive --timeout 120000 --bail
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | require('./playground/index.js');
--------------------------------------------------------------------------------
/ios:
--------------------------------------------------------------------------------
1 | lib/ios
--------------------------------------------------------------------------------
/lib/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/lib/android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.has("compileSdkVersion") ? rootProject.ext.compileSdkVersion : 27
5 | buildToolsVersion rootProject.ext.has("buildToolsVersion") ? rootProject.ext.buildToolsVersion : "27.0.3"
6 | defaultConfig {
7 | minSdkVersion rootProject.ext.has("minSdkVersion") ? rootProject.ext.minSdkVersion : 16
8 | targetSdkVersion rootProject.ext.has("targetSdkVersion") ? rootProject.ext.targetSdkVersion : 26
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 |
20 | testOptions {
21 | unitTests.all { t ->
22 | reports {
23 | html.enabled true
24 | }
25 | testLogging {
26 | events "passed", "skipped", "failed", "standardOut", "standardError"
27 | }
28 | afterSuite { desc, result ->
29 | if (!desc.parent) { // will match the outermost suite
30 | def output = " ${result.resultType} (${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped) "
31 | def repeatLength = output.length()
32 | println '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength) + '\n'
33 |
34 | println "see report at file://${t.reports.html.destination}/index.html"
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
41 | dependencies {
42 | implementation fileTree(dir: 'libs', include: ['*.jar'])
43 | testImplementation 'junit:junit:4.12'
44 |
45 | // node_modules
46 | implementation 'com.facebook.react:react-native:+'
47 | }
48 |
--------------------------------------------------------------------------------
/lib/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 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/lib/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/lib/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/lib/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Mar 27 14:06:33 IDT 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip
7 |
--------------------------------------------------------------------------------
/lib/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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
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 Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/lib/android/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 /Users/rotemm/Library/Android/sdk/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 |
--------------------------------------------------------------------------------
/lib/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/Events.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable;
2 |
3 | import com.facebook.react.bridge.Arguments;
4 | import com.facebook.react.bridge.WritableMap;
5 | import com.facebook.react.uimanager.PixelUtil;
6 | import com.facebook.react.uimanager.events.Event;
7 | import com.facebook.react.uimanager.events.RCTEventEmitter;
8 |
9 | /**
10 | * Created by rotemm on 14/02/2017.
11 | */
12 |
13 | public class Events {
14 |
15 | public static class OnSnapEvent extends Event {
16 |
17 | WritableMap eventData;
18 |
19 | public OnSnapEvent(int viewTag, int indexOfSnapPoint, String snapPointId) {
20 | super(viewTag);
21 | eventData = Arguments.createMap();
22 | eventData.putInt("index",indexOfSnapPoint);
23 | eventData.putString("id", snapPointId);
24 | }
25 |
26 | @Override
27 | public String getEventName() {
28 | return "onSnap";
29 | }
30 |
31 | @Override
32 | public void dispatch(RCTEventEmitter rctEventEmitter) {
33 | rctEventEmitter.receiveEvent(getViewTag(), getEventName(), eventData);
34 | }
35 | }
36 |
37 | public static class OnSnapStartEvent extends Event {
38 |
39 | WritableMap eventData;
40 |
41 | public OnSnapStartEvent(int viewTag, int indexOfSnapPoint, String snapPointId) {
42 | super(viewTag);
43 | eventData = Arguments.createMap();
44 | eventData.putInt("index",indexOfSnapPoint);
45 | eventData.putString("id", snapPointId);
46 | }
47 |
48 | @Override
49 | public String getEventName() {
50 | return "onSnapStart";
51 | }
52 |
53 | @Override
54 | public void dispatch(RCTEventEmitter rctEventEmitter) {
55 | rctEventEmitter.receiveEvent(getViewTag(), getEventName(), eventData);
56 | }
57 | }
58 |
59 | public static class OnAnimatedEvent extends Event {
60 |
61 | WritableMap eventData;
62 |
63 | public OnAnimatedEvent(int viewTag, float x, float y) {
64 | super(viewTag);
65 | eventData = Arguments.createMap();
66 | eventData.putDouble("x", PixelUtil.toDIPFromPixel(x));
67 | eventData.putDouble("y", PixelUtil.toDIPFromPixel(y));
68 | }
69 |
70 | @Override
71 | public String getEventName() {
72 | return "onAnimatedEvent";
73 | }
74 |
75 | @Override
76 | public void dispatch(RCTEventEmitter rctEventEmitter) {
77 | rctEventEmitter.receiveEvent(getViewTag(), getEventName(), eventData);
78 | }
79 | }
80 | public static class OnAlertEvent extends Event {
81 |
82 | WritableMap eventData;
83 |
84 | public OnAlertEvent(int viewTag, String alertAreaId, String alertType) {
85 | super(viewTag);
86 | eventData = Arguments.createMap();
87 | eventData.putString(alertAreaId, alertType);
88 | }
89 |
90 | @Override
91 | public String getEventName() {
92 | return "onAlert";
93 | }
94 |
95 | @Override
96 | public void dispatch(RCTEventEmitter rctEventEmitter) {
97 | rctEventEmitter.receiveEvent(getViewTag(), getEventName(), eventData);
98 | }
99 | }
100 |
101 | public static class onDrag extends Event {
102 |
103 | WritableMap eventData;
104 |
105 | public onDrag(int viewTag, String state, float x, float y, String targetSnapPointId) {
106 | super(viewTag);
107 | eventData = Arguments.createMap();
108 | eventData.putString("state", state);
109 | eventData.putDouble("x", PixelUtil.toDIPFromPixel(x));
110 | eventData.putDouble("y", PixelUtil.toDIPFromPixel(y));
111 | eventData.putString("targetSnapPointId", targetSnapPointId);
112 | }
113 |
114 | @Override
115 | public String getEventName() {
116 | return "onDrag";
117 | }
118 |
119 | @Override
120 | public void dispatch(RCTEventEmitter rctEventEmitter) {
121 | rctEventEmitter.receiveEvent(getViewTag(), getEventName(), eventData);
122 | }
123 | }
124 |
125 | public static class onStop extends Event {
126 |
127 | WritableMap eventData;
128 |
129 | public onStop(int viewTag, float x, float y) {
130 | super(viewTag);
131 | eventData = Arguments.createMap();
132 | eventData.putDouble("x", PixelUtil.toDIPFromPixel(x));
133 | eventData.putDouble("y", PixelUtil.toDIPFromPixel(y));
134 | }
135 |
136 | @Override
137 | public String getEventName() { return "onStop"; }
138 |
139 | @Override
140 | public void dispatch(RCTEventEmitter rctEventEmitter) {
141 | rctEventEmitter.receiveEvent(getViewTag(), getEventName(), eventData);
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/Interactable.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 |
9 | import java.util.Collections;
10 | import java.util.List;
11 |
12 | public class Interactable implements ReactPackage {
13 |
14 | @Override
15 | public List createNativeModules(ReactApplicationContext reactContext) {
16 | return Collections.emptyList();
17 | }
18 |
19 | // Deprecated RN 0.47
20 | public List> createJSModules() {
21 | return Collections.emptyList();
22 | }
23 |
24 | @Override
25 | public List createViewManagers(ReactApplicationContext reactContext) {
26 | return Collections.singletonList(new InteractableViewManager());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/InteractableArea.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable;
2 | import android.graphics.PointF;
3 |
4 | public class InteractableArea {
5 | private float top;
6 | private float left;
7 | private float bottom;
8 | private float right;
9 | private float bounce;
10 | private boolean haptics;
11 |
12 | public InteractableArea(float top, float left, float bottom, float right, float bounce, boolean haptics) {
13 | this.top = top;
14 | this.left = left;
15 | this.bottom = bottom;
16 | this.right = right;
17 | this.bounce = bounce;
18 | this.haptics = haptics;
19 | }
20 |
21 | public boolean pointInside(PointF point) {
22 | float cx = point.x;
23 | float cy = point.y;
24 |
25 | if (cx < this.left) return false;
26 | if (cx > this.right) return false;
27 |
28 | if (cy < this.top) return false;
29 | if (cy > this.bottom) return false;
30 |
31 | return true;
32 | }
33 |
34 | public boolean pointInsideWithOrigin(PointF point, PointF origin) {
35 | return this.pointInside(new PointF(point.x - origin.x, point.y - origin.y));
36 | }
37 |
38 |
39 | public float getTop() {
40 | return top;
41 | }
42 |
43 | public float getLeft() {
44 | return left;
45 | }
46 |
47 | public float getBottom() {
48 | return bottom;
49 | }
50 |
51 | public float getRight() {
52 | return right;
53 | }
54 |
55 | public float getBounce() {
56 | return bounce;
57 | }
58 |
59 | public boolean isHaptic() {
60 | return haptics;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/InteractablePoint.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable;
2 |
3 | import android.graphics.Point;
4 | import android.graphics.PointF;
5 | import android.util.Log;
6 |
7 | import java.util.ArrayList;
8 |
9 | /**
10 | * Created by zachik on 12/02/2017.
11 | */
12 |
13 | public class InteractablePoint {
14 | public String id;
15 | public float x;
16 | public float y;
17 | public float damping;
18 | public float tension;
19 | public float strength;
20 | public float falloff;
21 | public InteractableArea influenceArea;
22 |
23 | public InteractablePoint(String id, float x, float y, float damping, float tension, float strength, float falloff, InteractableArea influenceArea) {
24 | this.id = id;
25 | this.x = x;
26 | this.y = y;
27 | this.damping = damping;
28 | this.tension = tension;
29 | this.influenceArea = influenceArea;
30 | this.strength = strength;
31 | this.falloff = falloff;
32 | }
33 |
34 | public PointF positionWithOrigin() {
35 | PointF res = new PointF(0,0);
36 | if (this.x != Float.MAX_VALUE) res.x += this.x;
37 | if (this.y != Float.MAX_VALUE) res.y += this.y;
38 | return res;
39 | }
40 |
41 | public float distanceFromPoint(PointF point) {
42 | float dx = Float.MAX_VALUE;
43 | float dy = Float.MAX_VALUE;
44 | if (this.x != Float.MAX_VALUE) dx = point.x - this.x;
45 | if (this.y != Float.MAX_VALUE) dy = point.y - this.y;
46 | if (dx == Float.MAX_VALUE && dy == Float.MAX_VALUE) return Float.MAX_VALUE;
47 | if (dx == Float.MAX_VALUE) return Math.abs(dy);
48 | if (dy == Float.MAX_VALUE) return Math.abs(dx);
49 | return (float) Math.sqrt(dx*dx + dy*dy);
50 | }
51 |
52 | public static Point deltaBetweenPointAndOrigin(Point point,Point origin) {
53 | return new Point(point.x - origin.x, point.y - origin.y);
54 | }
55 |
56 | public static InteractablePoint findClosestPoint(ArrayList pointArray,
57 | PointF relativePoint) {
58 | // Log.d("InteractableView","findClosestPoint size = " + pointArray.size());
59 | float minDist = Float.MAX_VALUE;
60 | InteractablePoint closestPoint = null;
61 | for (InteractablePoint point : pointArray) {
62 | // Log.d("InteractableView","findClosestPoint point x = " + point.x);
63 |
64 | float curDist = point.distanceFromPoint(relativePoint);
65 | if (curDist < minDist) {
66 | minDist = curDist;
67 | closestPoint = point;
68 | }
69 | }
70 | return closestPoint;
71 |
72 | }
73 |
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/InteractableSpring.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable;
2 |
3 | /**
4 | * Created by zachik on 12/02/2017.
5 | */
6 |
7 | public class InteractableSpring {
8 | float tension;
9 | float damping;
10 |
11 |
12 | public InteractableSpring(float tension, float damping) {
13 | this.tension = tension;
14 | this.damping = damping;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/RNConvert/RNConvert.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable.RNConvert;
2 |
3 | import android.graphics.PointF;
4 |
5 | import com.facebook.react.bridge.ReadableArray;
6 | import com.facebook.react.bridge.ReadableMap;
7 | import com.facebook.react.uimanager.PixelUtil;
8 | import com.wix.interactable.InteractableArea;
9 | import com.wix.interactable.InteractablePoint;
10 | import com.wix.interactable.InteractableSpring;
11 |
12 | import java.util.ArrayList;
13 |
14 | /**
15 | * Created by rotemm on 12/02/2017.
16 | */
17 |
18 | public class RNConvert {
19 |
20 | public static InteractableArea interactableLimit(ReadableMap params) {
21 | float top = params.hasKey("top") ? PixelUtil.toPixelFromDIP((float) params.getDouble("top")) : -Float.MAX_VALUE;
22 | float left = params.hasKey("left") ? PixelUtil.toPixelFromDIP((float) params.getDouble("left")) : -Float.MAX_VALUE;
23 | float bottom = params.hasKey("bottom") ? PixelUtil.toPixelFromDIP((float) params.getDouble("bottom")) : Float.MAX_VALUE;
24 | float right = params.hasKey("right") ? PixelUtil.toPixelFromDIP((float) params.getDouble("right")) : Float.MAX_VALUE;
25 | float bounce = params.hasKey("bounce") ? (float) params.getDouble("bounce") : 0f;
26 | boolean haptics = params.hasKey("haptics") ? params.getBoolean("haptics") : false;
27 |
28 | return new InteractableArea(top, left, bottom, right, bounce, haptics);
29 | }
30 |
31 | public static InteractablePoint interactablePoint(ReadableMap params) {
32 | String id = params.hasKey("id") ? params.getString("id") : null;
33 | float x = params.hasKey("x") ? PixelUtil.toPixelFromDIP(params.getDouble("x")) : Float.MAX_VALUE;
34 | float y = params.hasKey("y") ? PixelUtil.toPixelFromDIP(params.getDouble("y")) : Float.MAX_VALUE;
35 | float damping = params.hasKey("damping") ? (float) params.getDouble("damping") : 0f;
36 | float tension = params.hasKey("tension") ? (float) params.getDouble("tension") : 300f;
37 | float strength = params.hasKey("strength") ? (float) params.getDouble("strength") : 400f;
38 | float falloff = params.hasKey("falloff") ? PixelUtil.toPixelFromDIP( params.getDouble("falloff")) : 40f;
39 | InteractableArea influenceArea = params.hasKey("influenceArea") ? interactableLimit(params.getMap("influenceArea")) : null;
40 |
41 | return new InteractablePoint(id, x, y, damping, tension, strength, falloff, influenceArea);
42 | }
43 |
44 | public static InteractableSpring interactableDrag(ReadableMap params) {
45 | float tension = params.hasKey("tension") ? (float) params.getDouble("tension") : Float.MAX_VALUE;
46 | float damping = params.hasKey("damping") ? (float) params.getDouble("damping") : 0f;
47 |
48 | return new InteractableSpring(tension, damping);
49 | }
50 |
51 | public static ArrayList interactablePoints(ReadableArray points) {
52 | ArrayList interactablePoints = new ArrayList(points.size());
53 | for (int i = 0 ; i < points.size() ; i++) {
54 | ReadableMap rawSpring = points.getMap(i);
55 | interactablePoints.add(RNConvert.interactablePoint(rawSpring));
56 | }
57 |
58 | return interactablePoints;
59 | }
60 |
61 | public static PointF pointF(ReadableMap params) {
62 | float x = PixelUtil.toPixelFromDIP(params.hasKey("x") ? params.getDouble("x") : 0);
63 | float y = PixelUtil.toPixelFromDIP(params.hasKey("y") ? params.getDouble("y") : 0);
64 |
65 | return new PointF(x, y);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/physics/PhysicsAnchorBehavior.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable.physics;
2 |
3 | import android.graphics.PointF;
4 | import android.util.Log;
5 | import android.view.View;
6 |
7 | /**
8 | * Created by zachik on 12/02/2017.
9 | */
10 |
11 | public class PhysicsAnchorBehavior extends PhysicsBehavior {
12 |
13 |
14 | public PhysicsAnchorBehavior(View target,PointF anchorPoint) {
15 | super(target, anchorPoint);
16 | }
17 |
18 | @Override
19 | public void executeFrameWithDeltaTime(float deltaTime, PhysicsObject physicsObject) {
20 | if (deltaTime == 0.0) return;
21 |
22 | // Log.d("InteractableView"," PhysicsAnchorBehavior executeFrameWithDeltaTime: " + deltaTime);
23 |
24 | float dx = this.anchorPoint.x - this.target.getTranslationX();
25 | float vx = dx / deltaTime;
26 |
27 | float dy = this.anchorPoint.y - this.target.getTranslationY();
28 | float vy = dy / deltaTime;
29 |
30 | physicsObject.velocity = new PointF(vx, vy);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/physics/PhysicsArea.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable.physics;
2 |
3 | import android.graphics.Point;
4 | import android.graphics.PointF;
5 |
6 | /**
7 | * Created by zachik on 12/02/2017.
8 | */
9 |
10 | public class PhysicsArea {
11 | PointF minPoint;
12 | PointF maxPoint;
13 |
14 | public PhysicsArea(PointF minPoint, PointF maxPoint) {
15 | this.minPoint = minPoint;
16 | this.maxPoint = maxPoint;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/physics/PhysicsBehavior.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable.physics;
2 |
3 | import android.content.Context;
4 | import android.graphics.PointF;
5 | import android.os.Vibrator;
6 | import android.view.View;
7 |
8 |
9 | /**
10 | * Created by rotemm on 09/02/2017.
11 | */
12 |
13 | public abstract class PhysicsBehavior {
14 | private static int DURATION_BETWEEN_HAPTICS = 500;
15 |
16 | View target;
17 | long lastHapticsAction;
18 |
19 | public void setAnchorPoint(PointF anchorPoint) {
20 | this.anchorPoint = anchorPoint;
21 | }
22 | public void moveAnchorPoint(float dx,float dy) {
23 | this.anchorPoint.x += dx;
24 | this.anchorPoint.y += dy;
25 | }
26 |
27 | PointF anchorPoint;
28 |
29 | public void setInfluence(PhysicsArea influence) {
30 | this.influence = influence;
31 | }
32 |
33 | private PhysicsArea influence;
34 |
35 | boolean isTemp = false;
36 | boolean haptics = false;
37 | boolean lastIsWithinInfluenceInitialized = false;
38 | boolean lastIsWithinInfluence = false;
39 |
40 | int priority = 1;
41 |
42 | public PhysicsBehavior(View target, boolean haptics) {
43 | this.target = target;
44 | this.haptics = haptics;
45 | }
46 | public PhysicsBehavior(View target, PointF anchorPoint) {
47 | this(target, false);
48 | this.anchorPoint = anchorPoint;
49 | }
50 |
51 |
52 | public abstract void executeFrameWithDeltaTime(float timeInterval,PhysicsObject physicsObject);
53 |
54 | public boolean isWithinInfluence() {
55 | boolean res = true;
56 | if (influence == null) {
57 | return true;
58 | }
59 | if (target.getTranslationX() < influence.minPoint.x) res = false;
60 | if (target.getTranslationX() > influence.maxPoint.x) res = false;
61 |
62 | if (target.getTranslationY() < influence.minPoint.y) res = false;
63 | if (target.getTranslationY() > influence.maxPoint.y) res = false;
64 |
65 | if (lastIsWithinInfluenceInitialized) {
66 | if (res != lastIsWithinInfluence) {
67 | doHaptic();
68 | }
69 |
70 | }
71 | lastIsWithinInfluenceInitialized = true;
72 | lastIsWithinInfluence = res;
73 | return res;
74 | }
75 |
76 | protected void doHaptic() {
77 |
78 | if ( haptics && (System.currentTimeMillis() - lastHapticsAction > DURATION_BETWEEN_HAPTICS)) {
79 | Vibrator v = (Vibrator) target.getContext().getSystemService(Context.VIBRATOR_SERVICE);
80 | v.vibrate(1);
81 | lastHapticsAction = System.currentTimeMillis();
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/physics/PhysicsBounceBehavior.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable.physics;
2 |
3 | import android.graphics.PointF;
4 | import android.util.Log;
5 | import android.view.View;
6 |
7 | /**
8 | * Created by zachik on 12/02/2017.
9 | */
10 |
11 | public class PhysicsBounceBehavior extends PhysicsBehavior {
12 |
13 | private PointF minPoint;
14 | private PointF maxPoint;
15 | private float bounce = 0.5f;
16 |
17 | public PhysicsBounceBehavior(View target, PointF minPoint, PointF maxPoint,float bounce, boolean haptics) {
18 | super(target, haptics);
19 | this.minPoint = minPoint;
20 | this.maxPoint = maxPoint;
21 | this.bounce = bounce;
22 | this.priority = 3;
23 | }
24 |
25 | @Override
26 | public void executeFrameWithDeltaTime(float timeInterval, PhysicsObject physicsObject) {
27 | // Log.d("InteractableView"," PhysicsBounceBehavior executeFrameWithDeltaTime: " + timeInterval + " minX = " + minPoint.x);
28 | applyLimits();
29 | if (this.minPoint.x == this.target.getTranslationX() && physicsObject.velocity.x < 0.0)
30 | {
31 | float vx = -physicsObject.velocity.x * this.bounce;
32 | float vy = physicsObject.velocity.y;
33 | physicsObject.velocity = new PointF(vx, vy);
34 | doHaptic();
35 | }
36 | if (this.minPoint.y == this.target.getTranslationY() && physicsObject.velocity.y < 0.0)
37 | {
38 | float vx = physicsObject.velocity.x;
39 | float vy = -physicsObject.velocity.y * this.bounce;
40 | physicsObject.velocity = new PointF(vx, vy);
41 | doHaptic();
42 | }
43 | if (this.maxPoint.x == this.target.getTranslationX() && physicsObject.velocity.x > 0.0)
44 | {
45 | float vx = -physicsObject.velocity.x * this.bounce;
46 | float vy = physicsObject.velocity.y;
47 | physicsObject.velocity = new PointF(vx, vy);
48 | doHaptic();
49 | }
50 | if (this.maxPoint.y == this.target.getTranslationY() && physicsObject.velocity.y > 0.0)
51 | {
52 | float vx = physicsObject.velocity.x;
53 | float vy = -physicsObject.velocity.y * this.bounce;
54 | physicsObject.velocity = new PointF(vx, vy);
55 | doHaptic();
56 | }
57 | }
58 |
59 | private void applyLimits() {
60 | if (this.minPoint.x > this.target.getTranslationX()) {
61 | this.target.setTranslationX(this.minPoint.x);
62 | }
63 | if (this.minPoint.y > this.target.getTranslationY()) {
64 | this.target.setTranslationY(this.minPoint.y);
65 | }
66 | if (this.maxPoint.x < this.target.getTranslationX()) {
67 | this.target.setTranslationX(this.maxPoint.x);
68 | }
69 | if (this.maxPoint.y < this.target.getTranslationY()) {
70 | this.target.setTranslationY(this.maxPoint.y);
71 | }
72 | }
73 |
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/physics/PhysicsFrictionBehavior.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable.physics;
2 |
3 | import android.graphics.PointF;
4 | import android.util.Log;
5 | import android.view.View;
6 |
7 | /**
8 | * Created by zachik on 12/02/2017.
9 | */
10 |
11 | public class PhysicsFrictionBehavior extends PhysicsBehavior {
12 |
13 | private float friction;
14 | public PhysicsFrictionBehavior(View target, float friction) {
15 | super(target, false);
16 | this.friction = friction;
17 | this.priority = 2;
18 | }
19 |
20 | @Override
21 | public void executeFrameWithDeltaTime(float deltaTime, PhysicsObject physicsObject) {
22 | // Log.d("InteractableView"," PhysicsFrictionBehavior executeFrameWithDeltaTime friction = " + friction);
23 | if (!isWithinInfluence()) {
24 | return;
25 | }
26 |
27 | float vx = (float) (Math.pow(this.friction, 60.0 * deltaTime) * physicsObject.velocity.x);
28 | float vy = (float) (Math.pow(this.friction, 60.0 * deltaTime) * physicsObject.velocity.y);
29 |
30 | // Log.d("InteractableView"," vx = " + vx + " cur vx = " + physicsObject.velocity.x);
31 |
32 | physicsObject.velocity = new PointF(vx,vy);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/physics/PhysicsGravityWellBehavior.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable.physics;
2 |
3 | import android.graphics.PointF;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by zachik on 12/02/2017.
8 | */
9 |
10 | public class PhysicsGravityWellBehavior extends PhysicsBehavior {
11 |
12 | public void setStrength(float strength) {
13 | this.strength = strength;
14 | }
15 |
16 | public void setFalloff(float falloff) {
17 | this.falloff = falloff;
18 | }
19 |
20 | private float strength = 400f;
21 | private float falloff = 40f;
22 |
23 | public PhysicsGravityWellBehavior(View target, PointF anchorPoint) {
24 | super(target, anchorPoint);
25 | }
26 |
27 | @Override
28 | public void executeFrameWithDeltaTime(float deltaTime, PhysicsObject physicsObject) {
29 | if (!isWithinInfluence()) {
30 | return;
31 | }
32 |
33 | float dx = this.target.getTranslationX() - this.anchorPoint.x;
34 | float dy = this.target.getTranslationY() - this.anchorPoint.y;
35 | double dr = Math.sqrt(dx*dx + dy*dy);
36 | if (dr == 0.0) return;
37 |
38 | double a = (-this.strength * dr * Math.exp(-0.5f * (dr * dr) / (this.falloff * this.falloff))) / physicsObject.mass;
39 |
40 | double ax = dx / dr * a;
41 | float vx = (float) (physicsObject.velocity.x + deltaTime * ax);
42 |
43 |
44 | double ay = dy / dr * a;
45 | float vy = (float) (physicsObject.velocity.y + deltaTime * ay);
46 |
47 | // Log.d("InteractableView"," PhysicsGravityWellBehavior executeFrameWithDeltaTime: " + deltaTime
48 | // + " vx = " + vx + " cur vx = " + physicsObject.velocity.x);
49 |
50 | physicsObject.velocity = new PointF(vx, vy);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/physics/PhysicsObject.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable.physics;
2 |
3 | import android.graphics.Point;
4 | import android.graphics.PointF;
5 |
6 | /**
7 | * Created by rotemm on 09/02/2017.
8 | */
9 |
10 | public class PhysicsObject {
11 |
12 | PointF velocity;
13 | float mass;
14 |
15 | public PhysicsObject() {
16 | this.velocity = new PointF(0,0);
17 | this.mass = 1.0f;
18 | }
19 |
20 | public PhysicsObject(PointF velocity, float mass) {
21 | this.velocity = velocity;
22 | this.mass = mass;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/android/src/main/java/com/wix/interactable/physics/PhysicsSpringBehavior.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable.physics;
2 |
3 | import android.graphics.Point;
4 | import android.graphics.PointF;
5 | import android.util.Log;
6 | import android.view.View;
7 |
8 | /**
9 | * Created by zachik on 12/02/2017.
10 | */
11 |
12 | public class PhysicsSpringBehavior extends PhysicsBehavior {
13 | public float tension = 300f;
14 | public PhysicsSpringBehavior(View target, PointF anchorPoint) {
15 | super(target, anchorPoint);
16 | }
17 |
18 | @Override
19 | public void executeFrameWithDeltaTime(float deltaTime, PhysicsObject physicsObject) {
20 | if (!isWithinInfluence()) {
21 | return;
22 | }
23 |
24 | float dx = this.target.getTranslationX() - this.anchorPoint.x;
25 | float ax = (-this.tension * dx) / physicsObject.mass;
26 | float vx = physicsObject.velocity.x + deltaTime * ax;
27 |
28 | float dy = this.target.getTranslationY() - this.anchorPoint.y;
29 | float ay = (-this.tension * dy) / physicsObject.mass;
30 | float vy = physicsObject.velocity.y + deltaTime * ay;
31 |
32 | // Log.d("InteractableView"," PhysicsSpringBehavior executeFrameWithDeltaTime cur Vx = " + physicsObject.velocity.x + " vx = " + vx);
33 |
34 | physicsObject.velocity = new PointF(vx, vy);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/android/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/lib/android/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | react-native-interactable
3 |
4 |
--------------------------------------------------------------------------------
/lib/android/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/lib/android/src/test/java/com/wix/interactable/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.wix.interactable;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | import InteractableView from './src/InteractableView';
2 |
3 | module.exports = {
4 | View: InteractableView
5 | };
6 |
--------------------------------------------------------------------------------
/lib/ios/Interactable-tvOS/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 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSExceptionDomains
28 |
29 | localhost
30 |
31 | NSExceptionAllowsInsecureHTTPLoads
32 |
33 |
34 |
35 |
36 | NSLocationWhenInUseUsageDescription
37 |
38 | UILaunchStoryboardName
39 | LaunchScreen
40 | UIRequiredDeviceCapabilities
41 |
42 | armv7
43 |
44 | UISupportedInterfaceOrientations
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationLandscapeLeft
48 | UIInterfaceOrientationLandscapeRight
49 |
50 | UIViewControllerBasedStatusBarAppearance
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/lib/ios/Interactable-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 |
--------------------------------------------------------------------------------
/lib/ios/Interactable.xcodeproj/xcshareddata/xcschemes/Interactable.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
46 |
47 |
53 |
54 |
55 |
56 |
57 |
58 |
68 |
70 |
76 |
77 |
78 |
79 |
80 |
81 |
87 |
89 |
95 |
96 |
97 |
98 |
100 |
101 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/lib/ios/Interactable.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/lib/ios/Interactable.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/InteractableArea.h:
--------------------------------------------------------------------------------
1 | //
2 | // InteractableArea.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/6/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface InteractableArea : NSObject
13 |
14 | @property (nonatomic, assign) CGFloat top;
15 | @property (nonatomic, assign) CGFloat left;
16 | @property (nonatomic, assign) CGFloat bottom;
17 | @property (nonatomic, assign) CGFloat right;
18 | @property (nonatomic, assign) CGFloat bounce;
19 | @property (nonatomic, assign) BOOL haptics;
20 |
21 | - (BOOL)pointInside:(CGPoint)point withOrigin:(CGPoint)origin;
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/InteractableArea.m:
--------------------------------------------------------------------------------
1 | //
2 | // InteractableArea.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/6/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "InteractableArea.h"
10 |
11 | @implementation InteractableArea
12 |
13 | - (id)copyWithZone:(__unused NSZone *)zone
14 | {
15 | return self;
16 | }
17 |
18 | - (BOOL)pointInside:(CGPoint)point withOrigin:(CGPoint)origin
19 | {
20 | CGFloat cx = point.x - origin.x;
21 | CGFloat cy = point.y - origin.y;
22 |
23 | if (cx < self.left) return NO;
24 | if (cx > self.right) return NO;
25 |
26 | if (cy < self.top) return NO;
27 | if (cy > self.bottom) return NO;
28 |
29 | return YES;
30 | }
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/InteractablePoint.h:
--------------------------------------------------------------------------------
1 | //
2 | // InteractablePoint.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 1/27/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "InteractableArea.h"
12 |
13 | @interface InteractablePoint : NSObject
14 |
15 | @property (nonatomic, assign) CGFloat x;
16 | @property (nonatomic, assign) CGFloat y;
17 | @property (nonatomic, assign) CGFloat damping;
18 | @property (nonatomic, assign) CGFloat tension;
19 | @property (nonatomic, assign) CGFloat strength;
20 | @property (nonatomic, assign) CGFloat falloff;
21 | @property (nonatomic, copy) id id;
22 | @property (nonatomic, copy) InteractableArea *influenceArea;
23 | @property (nonatomic, assign) BOOL haptics;
24 |
25 | - (CGPoint)positionWithOrigin:(CGPoint)origin;
26 | - (CGFloat)distanceFromPoint:(CGPoint)point withOrigin:(CGPoint)origin;
27 | + (CGPoint)deltaBetweenPoint:(CGPoint)point andOrigin:(CGPoint)origin;
28 | + (InteractablePoint*)findClosestPoint:(NSArray*)points toPoint:(CGPoint)relativeToPoint withOrigin:(CGPoint)origin;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/InteractablePoint.m:
--------------------------------------------------------------------------------
1 | //
2 | // InteractablePoint.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 1/27/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "InteractablePoint.h"
10 |
11 | @implementation InteractablePoint
12 |
13 | - (id)copyWithZone:(__unused NSZone *)zone
14 | {
15 | return self;
16 | }
17 |
18 | - (CGPoint)positionWithOrigin:(CGPoint)origin
19 | {
20 | CGPoint res = origin;
21 | if (self.x != CGFLOAT_MAX) res.x += self.x;
22 | if (self.y != CGFLOAT_MAX) res.y += self.y;
23 | return res;
24 | }
25 |
26 | - (CGFloat)distanceFromPoint:(CGPoint)point withOrigin:(CGPoint)origin
27 | {
28 | CGFloat dx = CGFLOAT_MAX;
29 | CGFloat dy = CGFLOAT_MAX;
30 | if (self.x != CGFLOAT_MAX) dx = point.x - (origin.x + self.x);
31 | if (self.y != CGFLOAT_MAX) dy = point.y - (origin.y + self.y);
32 | if (dx == CGFLOAT_MAX && dy == CGFLOAT_MAX) return CGFLOAT_MAX;
33 | if (dx == CGFLOAT_MAX) return ABS(dy);
34 | if (dy == CGFLOAT_MAX) return ABS(dx);
35 | return sqrt(dx*dx + dy*dy);
36 | }
37 |
38 | + (CGPoint)deltaBetweenPoint:(CGPoint)point andOrigin:(CGPoint)origin
39 | {
40 | return CGPointMake(point.x - origin.x, point.y - origin.y);
41 | }
42 |
43 | + (InteractablePoint*)findClosestPoint:(NSArray*)points toPoint:(CGPoint)relativeToPoint withOrigin:(CGPoint)origin
44 | {
45 | InteractablePoint *res = nil;
46 | CGFloat minDistance = CGFLOAT_MAX;
47 | for (InteractablePoint *point in points)
48 | {
49 | CGFloat distance = [point distanceFromPoint:relativeToPoint withOrigin:origin];
50 | if (distance < minDistance)
51 | {
52 | minDistance = distance;
53 | res = point;
54 | }
55 | }
56 | return res;
57 | }
58 |
59 | @end
60 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/InteractableSpring.h:
--------------------------------------------------------------------------------
1 | //
2 | // InteractableSpring.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/9/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface InteractableSpring : NSObject
13 |
14 | @property (nonatomic, assign) CGFloat tension;
15 | @property (nonatomic, assign) CGFloat damping;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/InteractableSpring.m:
--------------------------------------------------------------------------------
1 | //
2 | // InteractableSpring.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/9/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "InteractableSpring.h"
10 |
11 | @implementation InteractableSpring
12 |
13 | - (id)copyWithZone:(__unused NSZone *)zone
14 | {
15 | return self;
16 | }
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/InteractableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // InteractableView.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 1/27/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import
12 | #import "InteractablePoint.h"
13 | #import "InteractableArea.h"
14 | #import "InteractableSpring.h"
15 | #import "PhysicsAnimator.h"
16 |
17 | @interface InteractableView : UIView
18 |
19 | @property (nonatomic, assign) BOOL verticalOnly;
20 | @property (nonatomic, assign) BOOL horizontalOnly;
21 | @property (nonatomic, assign) BOOL dragEnabled;
22 | @property (nonatomic, strong) RCTBridge *bridge;
23 | @property (nonatomic, copy) NSArray *snapPoints;
24 | @property (nonatomic, copy) NSArray *springPoints;
25 | @property (nonatomic, copy) NSArray *gravityPoints;
26 | @property (nonatomic, copy) NSArray *frictionAreas;
27 | @property (nonatomic, copy) NSArray *alertAreas;
28 | @property (nonatomic, copy) InteractableArea *boundaries;
29 | @property (nonatomic, copy) InteractableSpring *dragWithSpring;
30 | @property (nonatomic, assign) CGFloat dragToss;
31 | @property (nonatomic, copy) RCTDirectEventBlock onSnap;
32 | @property (nonatomic, copy) RCTDirectEventBlock onSnapStart;
33 | @property (nonatomic, copy) RCTDirectEventBlock onStop;
34 | @property (nonatomic, copy) RCTDirectEventBlock onAlert;
35 | @property (nonatomic, copy) RCTDirectEventBlock onDrag;
36 | @property (nonatomic, assign) CGPoint initialPosition;
37 | @property (nonatomic, copy) RCTDirectEventBlock onAnimatedEvent;
38 | @property (nonatomic, assign) BOOL reportOnAnimatedEvents;
39 |
40 | - (instancetype)initWithBridge:(RCTBridge*)bridge;
41 | - (void)setVelocity:(NSDictionary*)params;
42 | - (void)snapTo:(NSDictionary*)params;
43 | - (void)changePosition:(NSDictionary*)params;
44 | - (void)bringToFront:(NSDictionary*)params;
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/InteractableViewManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // InteractableViewManager.h
3 | // InteractableViewManager
4 | //
5 | // Created by Tal Kol on 1/27/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface InteractableViewManager : RCTViewManager
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/InteractableViewManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // InteractableViewManager.m
3 | // InteractableViewManager
4 | //
5 | // Created by Tal Kol on 1/27/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "InteractableViewManager.h"
10 | #import "InteractableView.h"
11 | #import "InteractablePoint.h"
12 | #import "InteractableArea.h"
13 | #import "InteractableSpring.h"
14 | #import "RCTConvert+Interactable.h"
15 | #import
16 | #import
17 |
18 | @implementation InteractableViewManager
19 |
20 | RCT_EXPORT_MODULE()
21 |
22 | - (UIView *)view
23 | {
24 | return [[InteractableView alloc] initWithBridge:self.bridge];
25 | }
26 |
27 | RCT_EXPORT_VIEW_PROPERTY(verticalOnly, BOOL)
28 | RCT_EXPORT_VIEW_PROPERTY(horizontalOnly, BOOL)
29 | RCT_EXPORT_VIEW_PROPERTY(dragEnabled, BOOL)
30 | RCT_EXPORT_VIEW_PROPERTY(snapPoints, NSArray)
31 | RCT_EXPORT_VIEW_PROPERTY(springPoints, NSArray)
32 | RCT_EXPORT_VIEW_PROPERTY(gravityPoints, NSArray)
33 | RCT_EXPORT_VIEW_PROPERTY(frictionAreas, NSArray)
34 | RCT_EXPORT_VIEW_PROPERTY(alertAreas, NSArray)
35 | RCT_EXPORT_VIEW_PROPERTY(boundaries, InteractableArea)
36 | RCT_EXPORT_VIEW_PROPERTY(dragWithSpring, InteractableSpring)
37 | RCT_EXPORT_VIEW_PROPERTY(dragToss, CGFloat)
38 | RCT_EXPORT_VIEW_PROPERTY(onSnap, RCTDirectEventBlock)
39 | RCT_EXPORT_VIEW_PROPERTY(onSnapStart, RCTDirectEventBlock)
40 | RCT_EXPORT_VIEW_PROPERTY(onStop, RCTDirectEventBlock)
41 | RCT_EXPORT_VIEW_PROPERTY(onAlert, RCTDirectEventBlock)
42 | RCT_EXPORT_VIEW_PROPERTY(onDrag, RCTDirectEventBlock)
43 | RCT_EXPORT_VIEW_PROPERTY(initialPosition, CGPoint)
44 | RCT_EXPORT_VIEW_PROPERTY(onAnimatedEvent, RCTDirectEventBlock)
45 | RCT_EXPORT_VIEW_PROPERTY(reportOnAnimatedEvents, BOOL)
46 |
47 | RCT_EXPORT_METHOD(setVelocity:(nonnull NSNumber *)reactTag
48 | params:(NSDictionary*)params)
49 | {
50 | [self.bridge.uiManager addUIBlock:
51 | ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry)
52 | {
53 | UIView *view = viewRegistry[reactTag];
54 | if ([view isKindOfClass:[InteractableView class]])
55 | {
56 | [(InteractableView*)view setVelocity:params];
57 | }
58 | else
59 | {
60 | RCTLogError(@"tried to setVelocity: on non-InteractableView view %@ "
61 | "with tag #%@", view, reactTag);
62 | }
63 | }];
64 | }
65 |
66 | RCT_EXPORT_METHOD(bringToFront:(nonnull NSNumber *)reactTag
67 | params:(NSDictionary*)params)
68 | {
69 |
70 | }
71 |
72 | RCT_EXPORT_METHOD(snapTo:(nonnull NSNumber *)reactTag
73 | params:(NSDictionary*)params)
74 | {
75 | [self.bridge.uiManager addUIBlock:
76 | ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry)
77 | {
78 | UIView *view = viewRegistry[reactTag];
79 | if ([view isKindOfClass:[InteractableView class]])
80 | {
81 | [(InteractableView*)view snapTo:params];
82 | }
83 | else
84 | {
85 | RCTLogError(@"tried to snapTo: on non-InteractableView view %@ "
86 | "with tag #%@", view, reactTag);
87 | }
88 | }];
89 | }
90 |
91 | RCT_EXPORT_METHOD(changePosition:(nonnull NSNumber *)reactTag
92 | params:(NSDictionary*)params)
93 | {
94 | [self.bridge.uiManager addUIBlock:
95 | ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry)
96 | {
97 | UIView *view = viewRegistry[reactTag];
98 | if ([view isKindOfClass:[InteractableView class]])
99 | {
100 | [(InteractableView*)view changePosition:params];
101 | }
102 | else
103 | {
104 | RCTLogError(@"tried to changePosition: on non-InteractableView view %@ "
105 | "with tag #%@", view, reactTag);
106 | }
107 | }];
108 | }
109 |
110 | @end
111 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsAnchorBehavior.h:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsAnchorBehavior.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/9/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "PhysicsBehavior.h"
10 |
11 | @interface PhysicsAnchorBehavior : PhysicsBehavior
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsAnchorBehavior.m:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsAnchorBehavior.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/9/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "PhysicsAnchorBehavior.h"
10 |
11 | @implementation PhysicsAnchorBehavior
12 |
13 | - (instancetype)initWithTarget:(UIView*)target anchorPoint:(CGPoint)anchorPoint
14 | {
15 | if ((self = [super initWithTarget:target anchorPoint:anchorPoint]))
16 | {
17 | self.priority = 3;
18 | }
19 | return self;
20 | }
21 |
22 | - (void)executeFrameWithDeltaTime:(CFTimeInterval)deltaTime onObject:(PhysicsObject*)object
23 | {
24 | if (deltaTime == 0.0) return;
25 |
26 | CGFloat dx = self.anchorPoint.x - self.target.center.x;
27 | CGFloat vx = dx / deltaTime;
28 |
29 | CGFloat dy = self.anchorPoint.y - self.target.center.y;
30 | CGFloat vy = dy / deltaTime;
31 |
32 | object.velocity = CGPointMake(vx, vy);
33 | }
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsAnimator.h:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsAnimator.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/4/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "PhysicsSpringBehavior.h"
12 | #import "PhysicsBounceBehavior.h"
13 | #import "PhysicsFrictionBehavior.h"
14 | #import "PhysicsAnchorBehavior.h"
15 | #import "PhysicsGravityWellBehavior.h"
16 |
17 | @protocol PhysicsAnimatorDelegate;
18 |
19 | @interface PhysicsAnimator : NSObject
20 |
21 | @property (nonatomic, assign) id delegate;
22 |
23 | - (void)addBehavior:(PhysicsBehavior*)behavior;
24 | - (void)addTempBehavior:(PhysicsBehavior*)behavior;
25 | - (void)removeAllBehaviors;
26 | - (void)removeTempBehaviors;
27 | - (void)ensureRunning;
28 | - (void)stopRunning;
29 | - (void)setTarget:(UIView*)target mass:(CGFloat)mass;
30 | - (void)setTarget:(UIView*)target velocity:(CGPoint)velocity;
31 | - (CGPoint)getTargetVelocity:(UIView*)target;
32 |
33 | @end
34 |
35 | @protocol PhysicsAnimatorDelegate
36 |
37 | - (void)physicsAnimatorDidPause:(PhysicsAnimator *)animator;
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsArea.h:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsArea.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/11/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface PhysicsArea : NSObject
13 |
14 | @property (nonatomic, assign) CGPoint minPoint;
15 | @property (nonatomic, assign) CGPoint maxPoint;
16 |
17 | - (instancetype)initWithMinPoint:(CGPoint)minPoint maxPoint:(CGPoint)maxPoint;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsArea.m:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsArea.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/11/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "PhysicsArea.h"
10 |
11 | @implementation PhysicsArea
12 |
13 | - (instancetype)init
14 | {
15 | if ((self = [super init]))
16 | {
17 | self.minPoint = CGPointMake(-CGFLOAT_MAX, -CGFLOAT_MAX);
18 | self.maxPoint = CGPointMake(CGFLOAT_MAX, CGFLOAT_MAX);
19 | }
20 | return self;
21 | }
22 |
23 | - (instancetype)initWithMinPoint:(CGPoint)minPoint maxPoint:(CGPoint)maxPoint
24 | {
25 | if ((self = [super init]))
26 | {
27 | self.minPoint = minPoint;
28 | self.maxPoint = maxPoint;
29 | }
30 | return self;
31 | }
32 |
33 | - (id)copyWithZone:(__unused NSZone *)zone
34 | {
35 | return self;
36 | }
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsBehavior.h:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsBehavior.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/4/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "PhysicsObject.h"
12 | #import "PhysicsArea.h"
13 |
14 | @interface PhysicsBehavior : NSObject
15 |
16 | @property (nonatomic) UIView *target;
17 | @property (nonatomic, assign) int priority; // priority 1 is handled first, 2 handled 2nd
18 | @property (nonatomic, assign) BOOL temp;
19 | @property (nonatomic, assign) CGPoint anchorPoint;
20 | @property (nonatomic, copy) PhysicsArea *influence;
21 | @property (nonatomic, assign) BOOL haptics;
22 |
23 | - (instancetype)initWithTarget:(UIView*)target;
24 | - (instancetype)initWithTarget:(UIView*)target anchorPoint:(CGPoint)anchorPoint;
25 | - (void)executeFrameWithDeltaTime:(CFTimeInterval)deltaTime onObject:(PhysicsObject*)object;
26 | - (NSUInteger)findSortIndexInArray:(NSArray*)array;
27 | - (BOOL)isWithinInfluence;
28 | - (void)doHaptics;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsBehavior.m:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsBehavior.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/4/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "PhysicsBehavior.h"
10 |
11 | const CFTimeInterval DURATION_BETWEEN_HAPTICS = 0.5;
12 |
13 | @interface PhysicsBehavior()
14 | @property (nonatomic) UIImpactFeedbackGenerator *hapticsEngine;
15 | @property (nonatomic, assign) CFTimeInterval lastHapticsAction;
16 | @property (nonatomic, assign) BOOL lastIsWithinInfluence;
17 | @property (nonatomic, assign) BOOL lastIsWithinInfluenceInitialized;
18 | @end
19 |
20 |
21 | @implementation PhysicsBehavior
22 |
23 | - (void)setup:(UIView*)target
24 | {
25 | self.target = target;
26 | self.temp = NO;
27 | self.haptics = NO;
28 | self.hapticsEngine = nil;
29 | self.lastHapticsAction = 0.0;
30 | self.lastIsWithinInfluence = NO;
31 | self.lastIsWithinInfluenceInitialized = NO;
32 | self.priority = 1;
33 | }
34 |
35 | - (instancetype)initWithTarget:(UIView*)target
36 | {
37 | if ((self = [super init]))
38 | {
39 | [self setup:target];
40 | }
41 | return self;
42 | }
43 |
44 | - (instancetype)initWithTarget:(UIView*)target anchorPoint:(CGPoint)anchorPoint
45 | {
46 | if ((self = [super init]))
47 | {
48 | [self setup:target];
49 | self.anchorPoint = anchorPoint;
50 | }
51 | return self;
52 | }
53 |
54 | - (void)dealloc
55 | {
56 | self.hapticsEngine = nil;
57 | }
58 |
59 | - (void)setHaptics:(BOOL)haptics
60 | {
61 | if (haptics)
62 | {
63 | Class kUIImpactFeedbackGenerator = NSClassFromString(@"UIImpactFeedbackGenerator");
64 | if (kUIImpactFeedbackGenerator) self.hapticsEngine = [[kUIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
65 | }
66 | _haptics = haptics;
67 | }
68 |
69 | - (NSUInteger)findSortIndexInArray:(NSArray*)array
70 | {
71 | return [array indexOfObject:self
72 | inSortedRange:(NSRange){0, [array count]}
73 | options:NSBinarySearchingInsertionIndex
74 | usingComparator:^NSComparisonResult(id obja, id objb)
75 | {
76 | PhysicsBehavior *a = (PhysicsBehavior*)obja;
77 | PhysicsBehavior *b = (PhysicsBehavior*)objb;
78 | if (a.priority == b.priority) return (NSComparisonResult)NSOrderedSame;
79 | if (a.priority > b.priority) return (NSComparisonResult)NSOrderedDescending;
80 | else return (NSComparisonResult)NSOrderedAscending;
81 | }];
82 | }
83 |
84 | - (BOOL)isWithinInfluence
85 | {
86 | BOOL res = YES;
87 |
88 | if (self.influence)
89 | {
90 | if (self.target.center.x < self.influence.minPoint.x) res = NO;
91 | if (self.target.center.x > self.influence.maxPoint.x) res = NO;
92 |
93 | if (self.target.center.y < self.influence.minPoint.y) res = NO;
94 | if (self.target.center.y > self.influence.maxPoint.y) res = NO;
95 |
96 | // haptics
97 | if (self.lastIsWithinInfluenceInitialized)
98 | {
99 | if (self.lastIsWithinInfluence != res) [self doHaptics];
100 | }
101 | else
102 | {
103 | self.lastIsWithinInfluenceInitialized = YES;
104 | }
105 | self.lastIsWithinInfluence = res;
106 | }
107 |
108 | return res;
109 | }
110 |
111 | - (void)doHaptics
112 | {
113 | if (!self.hapticsEngine) return;
114 | CFTimeInterval now = CFAbsoluteTimeGetCurrent();
115 | if (self.lastHapticsAction == 0.0 || (now - self.lastHapticsAction > DURATION_BETWEEN_HAPTICS))
116 | {
117 | [self.hapticsEngine impactOccurred];
118 | }
119 | self.lastHapticsAction = now;
120 | }
121 |
122 | @end
123 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsBounceBehavior.h:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsBounceBehavior.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/8/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "PhysicsBehavior.h"
11 |
12 | @interface PhysicsBounceBehavior : PhysicsBehavior
13 |
14 | @property (nonatomic, assign) CGPoint minPoint;
15 | @property (nonatomic, assign) CGPoint maxPoint;
16 | @property (nonatomic, assign) CGFloat bounce;
17 |
18 | - (instancetype)initWithTarget:(UIView*)target minPoint:(CGPoint)minPoint maxPoint:(CGPoint)maxPoint;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsBounceBehavior.m:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsBounceBehavior.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/8/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "PhysicsBounceBehavior.h"
10 |
11 | @implementation PhysicsBounceBehavior
12 |
13 | - (instancetype)initWithTarget:(UIView*)target minPoint:(CGPoint)minPoint maxPoint:(CGPoint)maxPoint
14 | {
15 | if ((self = [super initWithTarget:target]))
16 | {
17 | self.minPoint = minPoint;
18 | self.maxPoint = maxPoint;
19 | self.bounce = 0.5;
20 | }
21 | return self;
22 | }
23 |
24 | - (void)executeFrameWithDeltaTime:(CFTimeInterval)deltaTime onObject:(PhysicsObject*)object
25 | {
26 | if (self.minPoint.x == self.target.center.x && object.velocity.x < 0.0)
27 | {
28 | CGFloat vx = -object.velocity.x * self.bounce;
29 | CGFloat vy = object.velocity.y;
30 | object.velocity = CGPointMake(vx, vy);
31 | [self doHaptics];
32 | }
33 | if (self.minPoint.y == self.target.center.y && object.velocity.y < 0.0)
34 | {
35 | CGFloat vx = object.velocity.x;
36 | CGFloat vy = -object.velocity.y * self.bounce;
37 | object.velocity = CGPointMake(vx, vy);
38 | [self doHaptics];
39 | }
40 | if (self.maxPoint.x == self.target.center.x && object.velocity.x > 0.0)
41 | {
42 | CGFloat vx = -object.velocity.x * self.bounce;
43 | CGFloat vy = object.velocity.y;
44 | object.velocity = CGPointMake(vx, vy);
45 | [self doHaptics];
46 | }
47 | if (self.maxPoint.y == self.target.center.y && object.velocity.y > 0.0)
48 | {
49 | CGFloat vx = object.velocity.x;
50 | CGFloat vy = -object.velocity.y * self.bounce;
51 | object.velocity = CGPointMake(vx, vy);
52 | [self doHaptics];
53 | }
54 | }
55 |
56 |
57 | @end
58 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsFrictionBehavior.h:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsFrictionBehavior.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/9/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "PhysicsBehavior.h"
10 |
11 | @interface PhysicsFrictionBehavior : PhysicsBehavior
12 |
13 | @property (nonatomic, assign) CGFloat friction;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsFrictionBehavior.m:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsFrictionBehavior.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/9/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "PhysicsFrictionBehavior.h"
10 |
11 | @implementation PhysicsFrictionBehavior
12 |
13 | - (instancetype)initWithTarget:(UIView*)target
14 | {
15 | if ((self = [super initWithTarget:target]))
16 | {
17 | self.priority = 2;
18 | self.friction = 0.7;
19 | }
20 | return self;
21 | }
22 |
23 | - (void)executeFrameWithDeltaTime:(CFTimeInterval)deltaTime onObject:(PhysicsObject*)object
24 | {
25 | if (![self isWithinInfluence]) return;
26 |
27 | CGFloat vx = pow(self.friction, 60.0 * deltaTime) * object.velocity.x;
28 | CGFloat vy = pow(self.friction, 60.0 * deltaTime) * object.velocity.y;
29 |
30 | object.velocity = CGPointMake(vx, vy);
31 | }
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsGravityWellBehavior.h:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsGravityWellBehavior.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/4/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "PhysicsBehavior.h"
11 |
12 | @interface PhysicsGravityWellBehavior : PhysicsBehavior
13 |
14 | @property (nonatomic, assign) CGFloat strength;
15 | @property (nonatomic, assign) CGFloat falloff;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsGravityWellBehavior.m:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsGravityWellBehavior.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/4/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "PhysicsGravityWellBehavior.h"
10 |
11 | @implementation PhysicsGravityWellBehavior
12 |
13 | - (instancetype)initWithTarget:(UIView*)target anchorPoint:(CGPoint)anchorPoint
14 | {
15 | if ((self = [super initWithTarget:target anchorPoint:anchorPoint]))
16 | {
17 | self.strength = 400.0;
18 | self.falloff = 40.0;
19 | }
20 | return self;
21 | }
22 |
23 | // regular gravitational force is proportional to 1/r^2 which is instable when r -> 0
24 | // instead, we're using a potential that looks like an inverted gaussian which behaves much better
25 | // its deriviative (the force) looks like this:
26 | // https://www.wolframalpha.com/input/?i=max+-5%2F(7)*sqrt(e)x*e%5E(-1%2F2%2F7%5E2*x%5E2)
27 |
28 | - (void)executeFrameWithDeltaTime:(CFTimeInterval)deltaTime onObject:(PhysicsObject*)object
29 | {
30 | if (![self isWithinInfluence]) return;
31 |
32 | CGFloat dx = self.target.center.x - self.anchorPoint.x;
33 | CGFloat dy = self.target.center.y - self.anchorPoint.y;
34 | CGFloat dr = sqrt(dx*dx + dy*dy);
35 | if (dr == 0.0) return;
36 |
37 | CGFloat a = (-self.strength * dr * exp(-0.5 * dr * dr / self.falloff / self.falloff)) / object.mass;
38 |
39 | CGFloat ax = dx / dr * a;
40 | CGFloat vx = object.velocity.x + deltaTime * ax;
41 |
42 |
43 | CGFloat ay = dy / dr * a;
44 | CGFloat vy = object.velocity.y + deltaTime * ay;
45 |
46 | object.velocity = CGPointMake(vx, vy);
47 | }
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsObject.h:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsTarget.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/4/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface PhysicsObject : NSObject
13 |
14 | @property (nonatomic, assign) CGPoint velocity;
15 | @property (nonatomic, assign) CGFloat mass;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsObject.m:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsObject.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/4/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "PhysicsObject.h"
10 |
11 | @implementation PhysicsObject
12 |
13 | - (instancetype)init
14 | {
15 | if ((self = [super init]))
16 | {
17 | self.velocity = CGPointZero;
18 | self.mass = 1.0;
19 | }
20 | return self;
21 | }
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsSpringBehavior.h:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsSnapBehavior.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/4/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "PhysicsBehavior.h"
11 |
12 | @interface PhysicsSpringBehavior : PhysicsBehavior
13 |
14 | @property (nonatomic, assign) CGFloat tension;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/PhysicsSpringBehavior.m:
--------------------------------------------------------------------------------
1 | //
2 | // PhysicsSnapBehavior.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 2/4/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "PhysicsSpringBehavior.h"
10 |
11 | @implementation PhysicsSpringBehavior
12 |
13 | - (instancetype)initWithTarget:(UIView*)target anchorPoint:(CGPoint)anchorPoint
14 | {
15 | if ((self = [super initWithTarget:target anchorPoint:anchorPoint]))
16 | {
17 | self.tension = 300.0;
18 | }
19 | return self;
20 | }
21 |
22 | - (void)executeFrameWithDeltaTime:(CFTimeInterval)deltaTime onObject:(PhysicsObject*)object
23 | {
24 | if (![self isWithinInfluence]) return;
25 |
26 | CGFloat dx = self.target.center.x - self.anchorPoint.x;
27 | CGFloat ax = (-self.tension * dx) / object.mass;
28 | CGFloat vx = object.velocity.x + deltaTime * ax;
29 |
30 | CGFloat dy = self.target.center.y - self.anchorPoint.y;
31 | CGFloat ay = (-self.tension * dy) / object.mass;
32 | CGFloat vy = object.velocity.y + deltaTime * ay;
33 |
34 | object.velocity = CGPointMake(vx, vy);
35 | }
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/RCTConvert+Interactable.h:
--------------------------------------------------------------------------------
1 | //
2 | // RCTConvert+Interactable.h
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 1/27/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @class InteractablePoint;
13 |
14 | @interface RCTConvert (Interactable)
15 |
16 | + (NSArray *)InteractablePointArray:(id)json;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/lib/ios/Interactable/RCTConvert+Interactable.m:
--------------------------------------------------------------------------------
1 | //
2 | // RCTConvert+Interactable.m
3 | // Interactable
4 | //
5 | // Created by Tal Kol on 1/27/17.
6 | // Copyright © 2017 Wix. All rights reserved.
7 | //
8 |
9 | #import "RCTConvert+Interactable.h"
10 | #import "InteractablePoint.h"
11 | #import "InteractableArea.h"
12 | #import "InteractableSpring.h"
13 |
14 | @implementation RCTConvert(Interactable)
15 |
16 | + (InteractablePoint *)InteractablePoint:(id)json
17 | {
18 | json = [self NSDictionary:json];
19 | InteractablePoint *point = [InteractablePoint new];
20 | point.x = [self CGFloat:json[@"x"] ?: @(CGFLOAT_MAX)];
21 | point.y = [self CGFloat:json[@"y"] ?: @(CGFLOAT_MAX)];
22 | point.damping = [self CGFloat:json[@"damping"] ?: @(0.0)];
23 | point.tension = [self CGFloat:json[@"tension"] ?: @(300.0)];
24 | point.strength = [self CGFloat:json[@"strength"] ?: @(400.0)];
25 | point.falloff = [self CGFloat:json[@"falloff"] ?: @(40.0)];
26 | point.id = [self id:json[@"id"] ?: [NSNull null]];
27 | point.influenceArea = [self InteractableArea:json[@"influenceArea"] ?: nil];
28 | point.haptics = [self BOOL:json[@"haptics"] ?: @(NO)];
29 | return point;
30 | }
31 |
32 | + (InteractableArea *)InteractableArea:(id)json
33 | {
34 | json = [self NSDictionary:json];
35 | if (json == nil) return nil;
36 | InteractableArea *area = [InteractableArea new];
37 | area.top = [self CGFloat:json[@"top"] ?: @(-CGFLOAT_MAX)];
38 | area.left = [self CGFloat:json[@"left"] ?: @(-CGFLOAT_MAX)];
39 | area.bottom = [self CGFloat:json[@"bottom"] ?: @(CGFLOAT_MAX)];
40 | area.right = [self CGFloat:json[@"right"] ?: @(CGFLOAT_MAX)];
41 | area.bounce = [self CGFloat:json[@"bounce"] ?: @(0.0)];
42 | area.haptics = [self BOOL:json[@"haptics"] ?: @(NO)];
43 | return area;
44 | }
45 |
46 | + (InteractableSpring *)InteractableSpring:(id)json
47 | {
48 | json = [self NSDictionary:json];
49 | if (json == nil) return nil;
50 | InteractableSpring *spring = [InteractableSpring new];
51 | spring.tension = [self CGFloat:json[@"tension"] ?: @(CGFLOAT_MAX)];
52 | spring.damping = [self CGFloat:json[@"damping"] ?: @(0.0)];
53 | return spring;
54 | }
55 |
56 | RCT_ARRAY_CONVERTER(InteractablePoint)
57 |
58 | @end
59 |
--------------------------------------------------------------------------------
/lib/ios/InteractableTests/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 |
--------------------------------------------------------------------------------
/lib/ios/InteractableTests/InteractableTests.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"
16 |
17 | @interface InteractableTests : XCTestCase
18 |
19 | @end
20 |
21 | @implementation InteractableTests
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 | #ifdef DEBUG
44 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
45 | if (level >= RCTLogLevelError) {
46 | redboxError = message;
47 | }
48 | });
49 | #endif
50 |
51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
54 |
55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
57 | return YES;
58 | }
59 | return NO;
60 | }];
61 | }
62 |
63 | #ifdef DEBUG
64 | RCTSetLogFunction(RCTDefaultLogFunction);
65 | #endif
66 |
67 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
68 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
69 | }
70 |
71 |
72 | @end
73 |
--------------------------------------------------------------------------------
/lib/src/InteractableView.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import ReactNative, { requireNativeComponent, Animated, NativeModules, UIManager, Platform } from 'react-native';
3 |
4 | // this is required in order to perform imperative commands
5 | const NativeViewManager = NativeModules.InteractableViewManager;
6 |
7 | const NativeInteractableView = requireNativeComponent('InteractableView', null);
8 |
9 | class WrappedInteractableView extends Component {
10 | render() {
11 | return (
12 | this._nativeViewRef = ref}
15 | />
16 | );
17 | }
18 |
19 | getScrollableNode() {
20 | return ReactNative.findNodeHandle(this._nativeViewRef);
21 | }
22 | }
23 |
24 | // this is required in order to support native events:
25 | const AnimatedInteractableView = Animated.createAnimatedComponent(WrappedInteractableView);
26 |
27 | class WrappedAnimatedInteractableView extends Component {
28 | constructor(props) {
29 | super(props);
30 | if (this.props.animatedValueX || this.props.animatedValueY) {
31 | this._animatedEvent = Animated.event(
32 | [{
33 | nativeEvent: {
34 | x: this.props.animatedValueX,
35 | y: this.props.animatedValueY
36 | }
37 | }],
38 | { useNativeDriver: !!this.props.animatedNativeDriver }
39 | );
40 | }
41 | }
42 |
43 | // this helps us verify that useNativeDriver actually works and we don't rely on the bridge - should be called in the constructor
44 | /*
45 | chokeTheBridge() {
46 | let j = 0;
47 | setInterval(() => {
48 | for (var index = 0; index < 1e9; index++) {
49 | j++;
50 | }
51 | }, 500);
52 | }
53 | */
54 |
55 | render() {
56 | return (
57 |
65 | );
66 | }
67 |
68 | setVelocity(params) {
69 | if (Platform.OS === 'ios') {
70 | NativeViewManager.setVelocity(ReactNative.findNodeHandle(this), params);
71 | } else if (Platform.OS === 'android') {
72 | UIManager.dispatchViewManagerCommand(
73 | ReactNative.findNodeHandle(this),
74 | UIManager.InteractableView.Commands.setVelocity,
75 | [params],
76 | );
77 | }
78 | }
79 |
80 | snapTo(params) {
81 | if (Platform.OS === 'ios') {
82 | NativeViewManager.snapTo(ReactNative.findNodeHandle(this), params);
83 | } else if (Platform.OS === 'android') {
84 | UIManager.dispatchViewManagerCommand(
85 | ReactNative.findNodeHandle(this),
86 | UIManager.InteractableView.Commands.snapTo,
87 | [params],
88 | );
89 | }
90 | }
91 |
92 | changePosition(params) {
93 | if (Platform.OS === 'ios') {
94 | NativeViewManager.changePosition(ReactNative.findNodeHandle(this), params);
95 | } else if (Platform.OS === 'android') {
96 | UIManager.dispatchViewManagerCommand(
97 | ReactNative.findNodeHandle(this),
98 | UIManager.InteractableView.Commands.changePosition,
99 | [params],
100 | );
101 | }
102 | }
103 |
104 | bringToFront() {
105 | if (Platform.OS === 'android') {
106 | UIManager.dispatchViewManagerCommand(
107 | ReactNative.findNodeHandle(this),
108 | UIManager.InteractableView.Commands.bringToFront,
109 | [],
110 | );
111 | }
112 | }
113 | }
114 |
115 | export default WrappedAnimatedInteractableView;
116 |
--------------------------------------------------------------------------------
/logos/circle-notitle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/logos/circle-notitle.png
--------------------------------------------------------------------------------
/logos/circle-title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/logos/circle-title.png
--------------------------------------------------------------------------------
/logos/square-notitle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/logos/square-notitle.png
--------------------------------------------------------------------------------
/logos/square-title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/logos/square-title.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-interactable",
3 | "version": "2.0.1",
4 | "description": "High performance interactable views in React Native",
5 | "license": "MIT",
6 | "author": "Tal Kol ",
7 | "publishConfig": {
8 | "registry": "https://registry.npmjs.org/"
9 | },
10 | "bugs": {
11 | "url": "https://github.com/wix/react-native-interactable/issues"
12 | },
13 | "homepage": "https://github.com/wix/react-native-interactable",
14 | "readme": "https://github.com/wix/react-native-interactable#readme",
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/wix/react-native-interactable.git"
18 | },
19 | "main": "lib/index.js",
20 | "scripts": {
21 | "start": "node ./node_modules/react-native/local-cli/cli.js start",
22 | "ios": "react-native run-ios --project-path playground/ios/",
23 | "android": "react-native run-android --root playground",
24 | "podinstall": "cd playground/ios && pod install",
25 | "test": ":",
26 | "e2e:ios": "detox test --configuration ios.sim.release --debug-synchronization",
27 | "e2e:ios-multi": "npm run e2e:ios -- -w 2",
28 | "e2e:android": "npm run build:android && detox test --configuration android.emu.release --loglevel verbose",
29 | "e2e:android:ci": "npm run build:android && detox test --configuration android.emu.release --loglevel verbose --headless",
30 | "build:ios": "detox build --configuration ios.sim.release",
31 | "build:android": "detox build --configuration android.emu.release"
32 | },
33 | "peerDependencies": {
34 | "react": ">= 15.4.1",
35 | "react-native": ">= 0.40.0"
36 | },
37 | "jest": {
38 | "preset": "react-native"
39 | },
40 | "devDependencies": {
41 | "@babel/core": "7.6.0",
42 | "@babel/runtime": "^7.4.2",
43 | "@react-native-community/eslint-config": "^0.0.3",
44 | "@types/react-native": ">= 0.52.2",
45 | "babel-jest": "^24.5.0",
46 | "detox": "^18.0.0",
47 | "eslint": "^5.16.0",
48 | "jest": "^24.5.0",
49 | "metro-react-native-babel-preset": "^0.53.1",
50 | "mocha": "^6.0.0",
51 | "react": "16.9.0",
52 | "react-native": "0.61.4",
53 | "react-test-renderer": "16.8.3"
54 | },
55 | "typings": "typings/react-native-interactable.d.ts",
56 | "babel": {
57 | "env": {
58 | "test": {
59 | "presets": [
60 | "react-native"
61 | ]
62 | }
63 | }
64 | },
65 | "detox_ignore": {
66 | "specs": "e2e",
67 | "configurations": {
68 | "ios.sim.debug": {
69 | "binaryPath": "playground/ios/DerivedData/playground/Build/Products/Debug-iphonesimulator/playground.app",
70 | "build": "RCT_NO_LAUNCH_PACKAGER=true xcodebuild build -scheme playground -project playground/ios/playground.xcodeproj -sdk iphonesimulator -configuration Debug -derivedDataPath playground/ios/DerivedData/playground ONLY_ACTIVE_ARCH=YES -quiet -UseModernBuildSystem=NO",
71 | "type": "ios.simulator",
72 | "name": "iPhone 11 Pro"
73 | },
74 | "ios.sim.release": {
75 | "binaryPath": "playground/ios/DerivedData/playground/Build/Products/Release-iphonesimulator/playground.app",
76 | "build": "RCT_NO_LAUNCH_PACKAGER=true xcodebuild build -scheme playground -project playground/ios/playground.xcodeproj -sdk iphonesimulator -configuration Release -derivedDataPath playground/ios/DerivedData/playground ONLY_ACTIVE_ARCH=YES -quiet -UseModernBuildSystem=NO",
77 | "type": "ios.simulator",
78 | "name": "iPhone 11 Pro"
79 | },
80 | "android.emu.debug": {
81 | "binaryPath": "playground/android/app/build/outputs/apk/debug/app-debug.apk",
82 | "build": "cd playground/android && ./gradlew :app:assembleDebug :app:assembleAndroidTest -DtestBuildType=debug",
83 | "type": "android.emulator",
84 | "name": "Pixel_2_API_26"
85 | },
86 | "android.emu.release": {
87 | "binaryPath": "playground/android/app/build/outputs/apk/release/app-release.apk",
88 | "build": "cd playground/android && ./gradlew :app:assembleRelease :app:assembleAndroidTest -DtestBuildType=release",
89 | "type": "android.emulator",
90 | "name": "Pixel_2_API_26"
91 | }
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/playground/.babelrc-IGNORE:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["module:metro-react-native-babel-preset"]
3 | }
4 |
--------------------------------------------------------------------------------
/playground/.buckconfig:
--------------------------------------------------------------------------------
1 |
2 | [android]
3 | target = Google Inc.:Google APIs:23
4 |
5 | [maven_repositories]
6 | central = https://repo1.maven.org/maven2
7 |
--------------------------------------------------------------------------------
/playground/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | ; We fork some components by platform
3 | .*/*[.]android.js
4 |
5 | ; Ignore "BUCK" generated dirs
6 | /\.buckd/
7 |
8 | ; Ignore polyfills
9 | node_modules/react-native/Libraries/polyfills/.*
10 |
11 | ; These should not be required directly
12 | ; require from fbjs/lib instead: require('fbjs/lib/warning')
13 | node_modules/warning/.*
14 |
15 | ; Flow doesn't support platforms
16 | .*/Libraries/Utilities/LoadingView.js
17 |
18 | [untyped]
19 | .*/node_modules/@react-native-community/cli/.*/.*
20 |
21 | [include]
22 |
23 | [libs]
24 | node_modules/react-native/Libraries/react-native/react-native-interface.js
25 | node_modules/react-native/flow/
26 |
27 | [options]
28 | emoji=true
29 |
30 | esproposal.optional_chaining=enable
31 | esproposal.nullish_coalescing=enable
32 |
33 | module.file_ext=.js
34 | module.file_ext=.json
35 | module.file_ext=.ios.js
36 |
37 | munge_underscores=true
38 |
39 | module.name_mapper='^react-native$' -> '/node_modules/react-native/Libraries/react-native/react-native-implementation'
40 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1'
41 | 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\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub'
42 |
43 | suppress_type=$FlowIssue
44 | suppress_type=$FlowFixMe
45 | suppress_type=$FlowFixMeProps
46 | suppress_type=$FlowFixMeState
47 |
48 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
49 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
50 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
51 |
52 | [lints]
53 | sketchy-null-number=warn
54 | sketchy-null-mixed=warn
55 | sketchy-number=warn
56 | untyped-type-import=warn
57 | nonstrict-import=warn
58 | deprecated-type=warn
59 | unsafe-getters-setters=warn
60 | inexact-spread=warn
61 | unnecessary-invariant=warn
62 | signature-verification-failure=warn
63 | deprecated-utility=error
64 |
65 | [strict]
66 | deprecated-type
67 | nonstrict-import
68 | sketchy-null
69 | unclear-type
70 | unsafe-getters-setters
71 | untyped-import
72 | untyped-type-import
73 |
74 | [version]
75 | ^0.105.0
76 |
--------------------------------------------------------------------------------
/playground/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.playground",
39 | )
40 |
41 | android_resource(
42 | name = "res",
43 | package = "com.playground",
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 |
--------------------------------------------------------------------------------
/playground/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 | )
--------------------------------------------------------------------------------
/playground/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 | # Disabling obfuscation is useful if you collect stack traces from production crashes
13 | # (unless you are using a system that supports de-obfuscate the stack traces).
14 | -dontobfuscate
15 |
16 | # React Native
17 |
18 | # Keep our interfaces so they can be used by other ProGuard rules.
19 | # See http://sourceforge.net/p/proguard/bugs/466/
20 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
21 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
22 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
23 |
24 | # Do not strip any method/class that is annotated with @DoNotStrip
25 | -keep @com.facebook.proguard.annotations.DoNotStrip class *
26 | -keep @com.facebook.common.internal.DoNotStrip class *
27 | -keepclassmembers class * {
28 | @com.facebook.proguard.annotations.DoNotStrip *;
29 | @com.facebook.common.internal.DoNotStrip *;
30 | }
31 |
32 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
33 | void set*(***);
34 | *** get*();
35 | }
36 |
37 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
38 | -keep class * extends com.facebook.react.bridge.NativeModule { *; }
39 | -keepclassmembers,includedescriptorclasses class * { native ; }
40 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; }
41 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; }
42 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; }
43 |
44 | -dontwarn com.facebook.react.**
45 |
46 | # TextLayoutBuilder uses a non-public Android constructor within StaticLayout.
47 | # See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details.
48 | -dontwarn android.text.StaticLayout
49 |
50 | # okhttp
51 |
52 | -keepattributes Signature
53 | -keepattributes *Annotation*
54 | -keep class okhttp3.** { *; }
55 | -keep interface okhttp3.** { *; }
56 | -dontwarn okhttp3.**
57 |
58 | # okio
59 |
60 | -keep class sun.misc.Unsafe { *; }
61 | -dontwarn java.nio.file.*
62 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
63 | -dontwarn okio.**
64 |
--------------------------------------------------------------------------------
/playground/android/app/src/androidTest/java/com/playground/DetoxTest.java:
--------------------------------------------------------------------------------
1 | package com.playground;
2 |
3 | import com.wix.detox.Detox;
4 |
5 | import org.junit.Rule;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 |
9 | import androidx.test.ext.junit.runners.AndroidJUnit4;
10 | import androidx.test.filters.LargeTest;
11 | import androidx.test.rule.ActivityTestRule;
12 |
13 | @RunWith(AndroidJUnit4.class)
14 | @LargeTest
15 | public class DetoxTest {
16 |
17 | @Rule
18 | public ActivityTestRule mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
19 |
20 | @Test
21 | public void runDetoxTests() {
22 | Detox.runTests(mActivityRule);
23 | }
24 | }
--------------------------------------------------------------------------------
/playground/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/playground/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
12 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/playground/android/app/src/main/java/com/playground/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.playground;
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 "playground";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/playground/android/app/src/main/java/com/playground/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.playground;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import com.facebook.react.PackageList;
6 | import com.facebook.react.ReactApplication;
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 | import com.wix.interactable.Interactable;
12 |
13 | import java.lang.reflect.InvocationTargetException;
14 | import java.util.Arrays;
15 | import java.util.List;
16 |
17 | public class MainApplication extends Application implements ReactApplication {
18 |
19 | private final ReactNativeHost mReactNativeHost =
20 | new ReactNativeHost(this) {
21 | @Override
22 | public boolean getUseDeveloperSupport() {
23 | return BuildConfig.DEBUG;
24 | }
25 |
26 | @Override
27 | protected List getPackages() {
28 | return Arrays.asList(
29 | new MainReactPackage(),
30 | new Interactable()
31 | );
32 | }
33 |
34 | @Override
35 | protected String getJSMainModuleName() {
36 | return "index";
37 | }
38 | };
39 |
40 | @Override
41 | public ReactNativeHost getReactNativeHost() {
42 | return mReactNativeHost;
43 | }
44 |
45 | @Override
46 | public void onCreate() {
47 | super.onCreate();
48 | SoLoader.init(this, /* native exopackage */ false);
49 | initializeFlipper(this); // Remove this line if you don't want Flipper enabled
50 | }
51 | /**
52 | * Loads Flipper in React Native templates.
53 | *
54 | * @param context
55 | */
56 | private static void initializeFlipper(Context context) {
57 | if (BuildConfig.DEBUG) {
58 | try {
59 | /*
60 | We use reflection here to pick up the class that initializes Flipper,
61 | since Flipper library is not available in release mode
62 | */
63 | Class> aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper");
64 | aClass.getMethod("initializeFlipper", Context.class).invoke(null, context);
65 | } catch (ClassNotFoundException e) {
66 | e.printStackTrace();
67 | } catch (NoSuchMethodException e) {
68 | e.printStackTrace();
69 | } catch (IllegalAccessException e) {
70 | e.printStackTrace();
71 | } catch (InvocationTargetException e) {
72 | e.printStackTrace();
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/playground/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/playground/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/playground/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/playground/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/playground/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | playground
3 |
4 |
--------------------------------------------------------------------------------
/playground/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/playground/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.3"
6 | minSdkVersion = 18
7 | compileSdkVersion = 28
8 | targetSdkVersion = 28
9 | supportLibVersion = "28.0.0"
10 | kotlinVersion = '1.3.31'
11 | }
12 | repositories {
13 | google()
14 | mavenLocal()
15 | jcenter()
16 | }
17 | dependencies {
18 | classpath('com.android.tools.build:gradle:3.4.2')
19 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
20 |
21 | // NOTE: Do not place your application dependencies here; they belong
22 | // in the individual module build.gradle files
23 | }
24 | }
25 |
26 | allprojects {
27 | repositories {
28 | mavenLocal()
29 | maven {
30 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
31 | url "$rootDir/../../node_modules/react-native/android"
32 | }
33 | maven {
34 | // Android JSC is installed from npm
35 | url("$rootDir/../../node_modules/jsc-android/dist")
36 | }
37 | jcenter()
38 | google()
39 | maven { url 'https://jitpack.io' }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/playground/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 |
20 | android.useAndroidX=true
21 | android.enableJetifier=true
22 |
--------------------------------------------------------------------------------
/playground/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/playground/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/playground/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/playground/android/playground.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/android/playground.keystore
--------------------------------------------------------------------------------
/playground/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'playground'
2 | include ':app'
3 |
4 | include ':react-native-interactable'
5 | project(':react-native-interactable').projectDir = new File(rootProject.projectDir, '../../lib/android')
6 |
7 | include ':detox'
8 | project(':detox').projectDir = new File(rootProject.projectDir, '../../`node_modules/detox/android/detox')
--------------------------------------------------------------------------------
/playground/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "playground",
3 | "displayName": "playground"
4 | }
--------------------------------------------------------------------------------
/playground/assets/airport-photo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/airport-photo.jpg
--------------------------------------------------------------------------------
/playground/assets/calendar-body.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/calendar-body.png
--------------------------------------------------------------------------------
/playground/assets/calendar-header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/calendar-header.png
--------------------------------------------------------------------------------
/playground/assets/card-photo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/card-photo.jpg
--------------------------------------------------------------------------------
/playground/assets/chatheads-delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/chatheads-delete.png
--------------------------------------------------------------------------------
/playground/assets/chatheads-face1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/chatheads-face1.jpg
--------------------------------------------------------------------------------
/playground/assets/chatheads-face2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/chatheads-face2.jpg
--------------------------------------------------------------------------------
/playground/assets/icon-check.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/icon-check.png
--------------------------------------------------------------------------------
/playground/assets/icon-clock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/icon-clock.png
--------------------------------------------------------------------------------
/playground/assets/icon-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/icon-menu.png
--------------------------------------------------------------------------------
/playground/assets/icon-trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/icon-trash.png
--------------------------------------------------------------------------------
/playground/assets/icon-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/icon-up.png
--------------------------------------------------------------------------------
/playground/assets/map-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/map-bg.jpg
--------------------------------------------------------------------------------
/playground/assets/tinder-photo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wix-incubator/react-native-interactable/5ea6b0f3d13b3a9169fab58e84c6736471f0b83a/playground/assets/tinder-photo.jpg
--------------------------------------------------------------------------------
/playground/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/playground/index.js:
--------------------------------------------------------------------------------
1 | import { AppRegistry } from 'react-native';
2 | import App from './src/app';
3 |
4 | AppRegistry.registerComponent('playground', () => App);
5 |
--------------------------------------------------------------------------------
/playground/ios/Playground-tvOS/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 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSExceptionDomains
28 |
29 | localhost
30 |
31 | NSExceptionAllowsInsecureHTTPLoads
32 |
33 |
34 |
35 |
36 | NSLocationWhenInUseUsageDescription
37 |
38 | UILaunchStoryboardName
39 | LaunchScreen
40 | UIRequiredDeviceCapabilities
41 |
42 | armv7
43 |
44 | UISupportedInterfaceOrientations
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationLandscapeLeft
48 | UIInterfaceOrientationLandscapeRight
49 |
50 | UIViewControllerBasedStatusBarAppearance
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/playground/ios/Playground-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 |
--------------------------------------------------------------------------------
/playground/ios/PlaygroundTests/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 |
--------------------------------------------------------------------------------
/playground/ios/PlaygroundTests/PlaygroundTests.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"
16 |
17 | @interface PlaygroundTests : XCTestCase
18 |
19 | @end
20 |
21 | @implementation PlaygroundTests
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 | #ifdef DEBUG
44 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
45 | if (level >= RCTLogLevelError) {
46 | redboxError = message;
47 | }
48 | });
49 | #endif
50 |
51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
54 |
55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
57 | return YES;
58 | }
59 | return NO;
60 | }];
61 | }
62 |
63 | #ifdef DEBUG
64 | RCTSetLogFunction(RCTDefaultLogFunction);
65 | #endif
66 |
67 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
68 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
69 | }
70 |
71 |
72 | @end
73 |
--------------------------------------------------------------------------------
/playground/ios/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '9.0'
2 | require_relative '../../node_modules/@react-native-community/cli-platform-ios/native_modules'
3 |
4 | target 'playground' do
5 | # Pods for Playground
6 | pod 'Interactable', :path => "../../Interactable.podspec"
7 | pod 'FBLazyVector', :path => "../../node_modules/react-native/Libraries/FBLazyVector"
8 | pod 'FBReactNativeSpec', :path => "../../node_modules/react-native/Libraries/FBReactNativeSpec"
9 | pod 'RCTRequired', :path => "../../node_modules/react-native/Libraries/RCTRequired"
10 | pod 'RCTTypeSafety', :path => "../../node_modules/react-native/Libraries/TypeSafety"
11 | pod 'React', :path => '../../node_modules/react-native/'
12 | pod 'React-Core', :path => '../../node_modules/react-native/'
13 | pod 'React-CoreModules', :path => '../../node_modules/react-native/React/CoreModules'
14 | pod 'React-Core/DevSupport', :path => '../../node_modules/react-native/'
15 | pod 'React-RCTActionSheet', :path => '../../node_modules/react-native/Libraries/ActionSheetIOS'
16 | pod 'React-RCTAnimation', :path => '../../node_modules/react-native/Libraries/NativeAnimation'
17 | pod 'React-RCTBlob', :path => '../../node_modules/react-native/Libraries/Blob'
18 | pod 'React-RCTImage', :path => '../../node_modules/react-native/Libraries/Image'
19 | pod 'React-RCTLinking', :path => '../../node_modules/react-native/Libraries/LinkingIOS'
20 | pod 'React-RCTNetwork', :path => '../../node_modules/react-native/Libraries/Network'
21 | pod 'React-RCTSettings', :path => '../../node_modules/react-native/Libraries/Settings'
22 | pod 'React-RCTText', :path => '../../node_modules/react-native/Libraries/Text'
23 | pod 'React-RCTVibration', :path => '../../node_modules/react-native/Libraries/Vibration'
24 | pod 'React-Core/RCTWebSocket', :path => '../../node_modules/react-native/'
25 | pod 'React-cxxreact', :path => '../../node_modules/react-native/ReactCommon/cxxreact'
26 | pod 'React-jsi', :path => '../../node_modules/react-native/ReactCommon/jsi'
27 | pod 'React-jsiexecutor', :path => '../../node_modules/react-native/ReactCommon/jsiexecutor'
28 | pod 'React-jsinspector', :path => '../../node_modules/react-native/ReactCommon/jsinspector'
29 | pod 'ReactCommon/jscallinvoker', :path => "../../node_modules/react-native/ReactCommon"
30 | pod 'ReactCommon/turbomodule/core', :path => "../../node_modules/react-native/ReactCommon"
31 | pod 'Yoga', :path => '../../node_modules/react-native/ReactCommon/yoga'
32 |
33 | pod 'DoubleConversion', :podspec => '../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
34 | pod 'glog', :podspec => '../../node_modules/react-native/third-party-podspecs/glog.podspec'
35 | pod 'Folly', :podspec => '../../node_modules/react-native/third-party-podspecs/Folly.podspec'
36 |
37 | use_native_modules!
38 |
39 | pre_install do |installer|
40 | Pod::Installer::Xcode::TargetValidator.send(:define_method, :verify_no_static_framework_transitive_dependencies) {}
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/playground/ios/playground.xcodeproj/xcshareddata/xcschemes/playground.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
60 |
61 |
67 |
68 |
69 |
70 |
71 |
72 |
82 |
84 |
90 |
91 |
92 |
93 |
94 |
95 |
101 |
103 |
109 |
110 |
111 |
112 |
114 |
115 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/playground/ios/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/playground/ios/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/playground/ios/playground/AppDelegate.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 | #import
12 |
13 | @interface AppDelegate : UIResponder
14 |
15 | @property (nonatomic, strong) UIWindow *window;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/playground/ios/playground/AppDelegate.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "AppDelegate.h"
11 |
12 | #import
13 | #import
14 | #import
15 |
16 | @implementation AppDelegate
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
19 | {
20 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
21 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
22 | moduleName:@"playground"
23 | initialProperties:nil];
24 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
25 |
26 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
27 | UIViewController *rootViewController = [UIViewController new];
28 | rootViewController.view = rootView;
29 | self.window.rootViewController = rootViewController;
30 | [self.window makeKeyAndVisible];
31 | return YES;
32 | }
33 |
34 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
35 | {
36 | #if DEBUG
37 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
38 | #else
39 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
40 | #endif
41 | }
42 |
43 | @end
44 |
--------------------------------------------------------------------------------
/playground/ios/playground/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 |
--------------------------------------------------------------------------------
/playground/ios/playground/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 | }
--------------------------------------------------------------------------------
/playground/ios/playground/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/playground/ios/playground/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | playground
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
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 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UIViewControllerBasedStatusBarAppearance
40 |
41 | NSLocationWhenInUseUsageDescription
42 |
43 | NSAppTransportSecurity
44 |
45 |
46 | NSExceptionDomains
47 |
48 | localhost
49 |
50 | NSExceptionAllowsInsecureHTTPLoads
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/playground/ios/playground/main.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import "AppDelegate.h"
13 |
14 | int main(int argc, char * argv[]) {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/playground/metro.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Metro configuration for React Native
3 | * https://github.com/facebook/react-native
4 | *
5 | * @format
6 | */
7 |
8 | module.exports = {
9 | transformer: {
10 | getTransformOptions: async () => ({
11 | transform: {
12 | experimentalImportSupport: false,
13 | inlineRequires: false,
14 | },
15 | }),
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/playground/src/examples/AlertAreas.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Dimensions, Text } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | const Screen = {
6 | width: Dimensions.get('window').width,
7 | height: Dimensions.get('window').height - 75
8 | };
9 |
10 | export default class AlertAreas extends Component {
11 |
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | dragEnabled: true
16 | };
17 | }
18 | render() {
19 | return (
20 |
21 |
22 |
23 |
24 |
25 | Non Draggable Area
26 |
27 |
28 |
29 |
40 |
41 |
42 |
43 | );
44 | }
45 | onAlert(event) {
46 | console.log('alert:', event.nativeEvent);
47 | if(JSON.stringify(event.nativeEvent).includes("\"blue\":\"enter\"")){
48 | this.setState({dragEnabled: false});
49 | }
50 | if(JSON.stringify(event.nativeEvent).includes("\"blue\":\"leave\"")){
51 | this.setState({dragEnabled: true});
52 | }
53 | }
54 | onDrag(event) {
55 | console.log('drag:', event.nativeEvent);
56 | }
57 | }
58 |
59 | const styles = StyleSheet.create({
60 | container: {
61 | flex: 1,
62 | justifyContent: 'center',
63 | alignItems: 'center',
64 | backgroundColor: 'white',
65 | },
66 | markerContainer: {
67 | flex: 1,
68 | justifyContent: 'center',
69 | alignItems: 'center',
70 | position: 'absolute',
71 | left: 0,
72 | right: 0,
73 | top: 0,
74 | bottom: 0
75 | },
76 | });
77 |
--------------------------------------------------------------------------------
/playground/src/examples/ChangePosition.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, TouchableOpacity, Text } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | export default class ChangePosition extends Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | };
10 | }
11 | render() {
12 | const snapPoints = [
13 | {x: -140, y: -250},
14 | {x: 140, y: -250},
15 | {x: -140, y: -120},
16 | {x: 140, y: -120},
17 | {x: -140, y: 0},
18 | {x: 140, y: 0},
19 | {x: -140, y: 120},
20 | {x: 140, y: 120},
21 | {x: -140, y: 250},
22 | {x: 140, y: 250}
23 | ];
24 | const blueDestination = snapPoints[3];
25 | return (
26 |
27 |
33 |
34 |
35 |
41 |
42 |
43 |
49 | {
51 | this.refs['blue'].changePosition(blueDestination)
52 | }}
53 | >
54 | {"ChangePosition to " + JSON.stringify(blueDestination)}
55 |
56 | {
58 | this.refs['green'].changePosition({
59 | x: (Math.random() - 0.5) * 280,
60 | y: (Math.random() - 0.5) * 500}
61 | )
62 | }}
63 | >
64 | ChangePosition to random
65 |
66 |
67 |
68 | );
69 | }
70 | }
71 |
72 | const styles = StyleSheet.create({
73 | container: {
74 | flex: 1,
75 | justifyContent: 'center',
76 | alignItems: 'center',
77 | backgroundColor: 'white',
78 | }
79 | });
80 |
--------------------------------------------------------------------------------
/playground/src/examples/ChatHeads.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | export default class ChatHeads extends Component {
6 | render() {
7 | return (
8 |
9 |
23 |
24 |
25 |
26 | );
27 | }
28 | }
29 |
30 | const styles = StyleSheet.create({
31 | container: {
32 | flex: 1,
33 | justifyContent: 'center',
34 | alignItems: 'center',
35 | backgroundColor: 'white',
36 | }
37 | });
38 |
--------------------------------------------------------------------------------
/playground/src/examples/CollapsingHeader.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Animated } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | export default class CollapsingHeader extends Component {
6 | constructor(props) {
7 | super(props);
8 | this._deltaY = new Animated.Value(0);
9 | }
10 | render() {
11 | return (
12 |
13 |
14 |
15 |
31 |
32 |
33 |
34 |
35 |
40 |
41 |
42 |
43 |
44 | );
45 | }
46 | }
47 |
48 | const styles = StyleSheet.create({
49 | container: {
50 | flex: 1,
51 | backgroundColor: '#e0e0e0',
52 | }
53 | });
54 |
--------------------------------------------------------------------------------
/playground/src/examples/CollapsingHeaderWithScroll.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Animated, ScrollView, Dimensions } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | const Screen = {
6 | height: Dimensions.get('window').height
7 | };
8 |
9 | export default class CollapsingHeaderWithScroll extends Component {
10 | constructor(props) {
11 | super(props);
12 | this._deltaY = new Animated.Value(0);
13 | this.state = {
14 | canScroll: false
15 | };
16 | }
17 | render() {
18 | return (
19 |
20 |
21 |
22 |
38 |
39 |
40 |
41 |
42 |
50 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | );
68 | }
69 | onSnap(event) {
70 | const { id } = event.nativeEvent;
71 | if (id === 'bottom') {
72 | this.setState({ canScroll: true });
73 | }
74 | }
75 | onScroll(event) {
76 | const { contentOffset } = event.nativeEvent;
77 | if (contentOffset.y <= 0) {
78 | this.setState({ canScroll: false });
79 | }
80 | }
81 | }
82 |
83 | const styles = StyleSheet.create({
84 | container: {
85 | flex: 1,
86 | backgroundColor: '#e0e0e0',
87 | },
88 | placeholder: {
89 | backgroundColor: '#65C888',
90 | flex: 1,
91 | height: 120,
92 | marginHorizontal: 20,
93 | marginTop: 20
94 | }
95 | });
96 |
--------------------------------------------------------------------------------
/playground/src/examples/HandleRelayout.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, TouchableOpacity, Text } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | export default class HandleRelayout extends Component {
6 | constructor() {
7 | super();
8 | this.state = {
9 | collapsed: false
10 | };
11 | }
12 | render() {
13 | return (
14 |
15 |
16 |
17 |
22 | Tap to {this.state.collapsed ? 'expand' : 'collapse'}
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 | );
34 | }
35 | onChangeLayoutPress() {
36 | this.setState({
37 | collapsed: !this.state.collapsed
38 | })
39 | }
40 | }
41 |
42 | const styles = StyleSheet.create({
43 | container: {
44 | flex: 1,
45 | justifyContent: 'center',
46 | alignItems: 'center',
47 | backgroundColor: 'white',
48 | },
49 | card: {
50 | width: 300,
51 | height: 180,
52 | backgroundColor: '#542790',
53 | borderRadius: 8,
54 | marginVertical: 6
55 | },
56 | label: {
57 | textAlign: 'center',
58 | fontSize: 24
59 | }
60 | });
61 |
--------------------------------------------------------------------------------
/playground/src/examples/HandleTouches.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, FlatList, TouchableOpacity, Text } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | export default class HandleTouches extends Component {
6 |
7 | render() {
8 | return (
9 | item}
13 | renderItem={this.renderRow}
14 | />
15 | );
16 | }
17 | renderRow = (data) => {
18 | return (
19 |
22 |
23 |
24 | Button A
25 |
26 |
27 | Button B
28 |
29 |
30 |
31 | );
32 | }
33 | onCardPress() {
34 | alert('Card was pressed');
35 | }
36 | onButtonPress(type) {
37 | alert(`Button ${type} was pressed`);
38 | }
39 | }
40 |
41 | const styles = StyleSheet.create({
42 | container: {
43 | alignItems: 'center',
44 | backgroundColor: 'white'
45 | },
46 | card: {
47 | width: 300,
48 | height: 180,
49 | backgroundColor: '#32B76C',
50 | borderRadius: 8,
51 | marginVertical: 6
52 | },
53 | button: {
54 | width: 80,
55 | height: 40,
56 | marginLeft: 30,
57 | marginTop: 30,
58 | justifyContent: 'center',
59 | backgroundColor: '#b5d9f8'
60 | }
61 | });
62 |
--------------------------------------------------------------------------------
/playground/src/examples/IconDrawer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Animated } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | export default class IconDrawer extends Component {
6 | constructor(props) {
7 | super(props);
8 | this._deltaX = new Animated.Value(0);
9 | }
10 | render() {
11 | return (
12 |
13 |
14 |
15 |
16 |
30 |
44 |
58 |
59 |
60 |
65 |
66 |
67 |
68 |
69 |
70 | );
71 | }
72 | onDrawerSnap(event) {
73 | const snapPointId = event.nativeEvent.id;
74 | console.log(`drawer state is ${snapPointId}`);
75 | }
76 | }
77 |
78 | const styles = StyleSheet.create({
79 | container: {
80 | flex: 1,
81 | justifyContent: 'center',
82 | backgroundColor: 'white',
83 | },
84 | button: {
85 | width: 40,
86 | height: 40,
87 | marginRight: 25,
88 | backgroundColor: '#EE2C38'
89 | }
90 | });
91 |
--------------------------------------------------------------------------------
/playground/src/examples/MoreDrawers.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Text } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | export default class MoreDrawers extends Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 |
14 |
15 | Default drawer
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 | Drawer with limits
27 |
28 |
29 |
30 |
31 |
32 |
36 |
37 | Limits with bounce
38 |
39 |
40 |
41 |
42 |
43 |
47 |
48 | Drag via spring
49 |
50 |
51 |
52 |
53 |
54 |
59 |
60 | Drag with spring resistance
61 |
62 |
63 |
64 |
65 |
66 | );
67 | }
68 | }
69 |
70 | const styles = StyleSheet.create({
71 | container: {
72 | flex: 1,
73 | justifyContent: 'center',
74 | backgroundColor: 'white',
75 | paddingTop: 80,
76 | paddingBottom: 40
77 | },
78 | cover: {
79 | left: 0,
80 | right: 0,
81 | height: 75,
82 | backgroundColor: '#e0e0e0',
83 | justifyContent: 'center'
84 | },
85 | label: {
86 | textAlign: 'center',
87 | fontSize: 18
88 | }
89 | });
90 |
--------------------------------------------------------------------------------
/playground/src/examples/SideMenu.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, TouchableOpacity, Image, Text, Button, Dimensions } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | const Screen = Dimensions.get('window');
6 | const SideMenuWidth = 280;
7 | const RemainingWidth = Screen.width - SideMenuWidth;
8 |
9 | export default class SideMenu extends Component {
10 | render() {
11 | return (
12 |
13 |
14 |
15 |
21 |
22 | Menu
23 | alert('Button 1 pressed')}>
24 | Button 1
25 |
26 | alert('Button 2 pressed')}>
27 | Button 2
28 |
29 | alert('Button 3 pressed')}>
30 | Button 3
31 |
32 |
33 | Cancel
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | Side Menu Example
44 |
45 |
46 |
47 | Some Content Here
48 |
49 |
50 |
51 | );
52 | }
53 | onMenuPress() {
54 | this.refs['menuInstance'].setVelocity({x: 2000});
55 | }
56 | onClosePress() {
57 | this.refs['menuInstance'].setVelocity({x: -2000});
58 | }
59 | }
60 |
61 | const styles = StyleSheet.create({
62 | container: {
63 | flex: 1,
64 | alignItems: 'stretch',
65 | backgroundColor: 'white',
66 | },
67 | sideMenuContainer: {
68 | position: 'absolute',
69 | top: 0,
70 | left: -RemainingWidth,
71 | right: 0,
72 | bottom: 0,
73 | flexDirection: 'row',
74 | zIndex: 1002
75 | },
76 | sideMenu: {
77 | left: 0,
78 | width: Screen.width,
79 | paddingLeft: RemainingWidth,
80 | flex: 1,
81 | backgroundColor: '#e0e0e0',
82 | paddingTop: 20
83 | },
84 | sideMenuTitle: {
85 | fontSize: 22,
86 | fontWeight: 'bold',
87 | textAlign: 'center',
88 | marginBottom: 20
89 | },
90 | button: {
91 | color: '#542790',
92 | fontSize: 20,
93 | fontWeight: 'bold',
94 | textAlign: 'center',
95 | margin: 20
96 | },
97 | header: {
98 | height: 60,
99 | paddingHorizontal: 20,
100 | flexDirection: 'row',
101 | backgroundColor: '#32B76C',
102 | alignItems: 'center',
103 | zIndex: 1001
104 | },
105 | body: {
106 | flex: 1,
107 | zIndex: 1000,
108 | alignItems: 'center',
109 | justifyContent: 'center'
110 | },
111 | menuIcon: {
112 | width: 30,
113 | height: 30
114 | },
115 | headerTitle: {
116 | marginLeft: 24,
117 | color: 'white',
118 | fontSize: 20
119 | },
120 | content: {
121 | fontSize: 18
122 | }
123 | });
124 |
--------------------------------------------------------------------------------
/playground/src/examples/SnapTo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Button } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | export default class SnapTo extends Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | snapToIndex: 0
10 | };
11 | }
12 | render() {
13 | return (
14 |
15 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | );
37 | }
38 | onButtonPress() {
39 | this.refs['headInstance'].snapTo({index: this.state.snapToIndex});
40 | this.setState({
41 | snapToIndex: (this.state.snapToIndex + 1)%10
42 | });
43 | }
44 | }
45 |
46 | const styles = StyleSheet.create({
47 | container: {
48 | flex: 1,
49 | justifyContent: 'center',
50 | alignItems: 'center',
51 | backgroundColor: 'white',
52 | },
53 | button: {
54 | position: 'absolute',
55 | left: 110,
56 | backgroundColor: '#459FED'
57 | }
58 | });
59 |
--------------------------------------------------------------------------------
/playground/src/examples/SwipeableCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | export default class SwipeableCard extends Component {
6 | render() {
7 | return (
8 |
9 |
10 |
18 |
19 |
20 |
21 |
29 |
30 |
31 |
32 |
40 |
41 |
42 |
43 |
44 | );
45 | }
46 | }
47 |
48 | const styles = StyleSheet.create({
49 | container: {
50 | flex: 1,
51 | justifyContent: 'center',
52 | alignItems: 'center',
53 | backgroundColor: 'white',
54 | },
55 | card : {
56 | width: 300,
57 | height: 180,
58 | backgroundColor: '#32B76C',
59 | borderRadius: 8,
60 | marginVertical: 6
61 | }
62 | });
63 |
--------------------------------------------------------------------------------
/playground/src/examples/TouchesInside.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Text, Switch, Button, Picker, Slider, WebView } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | export default class TouchesInside extends Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | vertical: true,
10 | dragEnabled: true,
11 | language: 'java',
12 | switch: true
13 | };
14 | }
15 | render() {
16 | return (
17 |
18 |
19 |
20 | Vertical:
21 | this.setState({vertical: value})}
24 | />
25 | Can drag:
26 | this.setState({dragEnabled: value})}
29 | />
30 |
31 |
32 |
39 |
59 |
60 |
61 | );
62 | }
63 | }
64 |
65 | const styles = StyleSheet.create({
66 | container: {
67 | flex: 1,
68 | justifyContent: 'center',
69 | alignItems: 'center',
70 | backgroundColor: 'white',
71 | },
72 | direction: {
73 | flexDirection: 'row',
74 | alignItems: 'center',
75 | marginBottom: 20
76 | }
77 | });
78 |
--------------------------------------------------------------------------------
/playground/src/examples/TouchesInsideStatic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Text, Switch, ActivityIndicator, Image, TextInput } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | export default class TouchesInside extends Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | vertical: true,
10 | dragEnabled: true,
11 | language: 'java',
12 | switch: true
13 | };
14 | }
15 | render() {
16 | return (
17 |
18 |
19 |
20 | Vertical:
21 | this.setState({vertical: value})} />
24 | Can drag:
25 | this.setState({dragEnabled: value})} />
28 |
29 |
30 |
37 | Hello world
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | );
46 | }
47 | }
48 |
49 | const styles = StyleSheet.create({
50 | container: {
51 | flex: 1,
52 | justifyContent: 'center',
53 | alignItems: 'center',
54 | backgroundColor: 'white',
55 | },
56 | direction: {
57 | flexDirection: 'row',
58 | alignItems: 'center',
59 | marginBottom: 20
60 | }
61 | });
62 |
--------------------------------------------------------------------------------
/playground/src/real-life-examples/CollapsibleCalendar.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Dimensions, Image, Text, Animated } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | const Screen = Dimensions.get('window');
6 | const Calendar = {
7 | width: Screen.width - 16,
8 | height: (Screen.width -16) / 944 * 793
9 | }
10 |
11 | export default class CollapsibleFilter extends Component {
12 | constructor(props) {
13 | super(props);
14 | this._deltaY = new Animated.Value(0);
15 | }
16 | render() {
17 | return (
18 |
19 |
20 |
21 |
22 | FEBRUARY 2017
36 | THIS WEEK
50 |
51 |
52 |
53 |
54 |
64 |
65 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | );
79 | }
80 | }
81 |
82 | class Row extends Component {
83 | render() {
84 | return (
85 |
86 | {this.props.hour}
87 | {this.props.text}
88 |
89 | );
90 | }
91 | }
92 |
93 | const styles = StyleSheet.create({
94 | container: {
95 | flex: 1,
96 | alignItems: 'center',
97 | backgroundColor: 'white',
98 | },
99 | top: {
100 | backgroundColor: 'white',
101 | width: Screen.width,
102 | alignItems: 'center',
103 | zIndex: 1000
104 | },
105 | header: {
106 | marginTop: 15,
107 | height: 40,
108 | width: Screen.width,
109 | paddingLeft: 20
110 | },
111 | month: {
112 | position: 'absolute',
113 | left: 20,
114 | color: '#e33534',
115 | fontSize: 24,
116 | fontWeight: 'bold'
117 | },
118 | days: {
119 | width: Screen.width - 16,
120 | height: (Screen.width -16) / 944 * 65
121 | },
122 | calendar: {
123 | width: Calendar.width,
124 | height: Calendar.height
125 | },
126 | content: {
127 | backgroundColor: 'white'
128 | },
129 | row: {
130 | flexDirection: 'row',
131 | width: Screen.width,
132 | borderBottomWidth: 1,
133 | borderColor: '#eeeeee',
134 | height: 80,
135 | alignItems: 'center'
136 | },
137 | hour: {
138 | width: 80,
139 | textAlign: 'center',
140 | color: '#b0b0b0',
141 | fontSize: 14,
142 | fontWeight: 'bold'
143 | },
144 | text: {
145 | flex: 1,
146 | fontSize: 24
147 | }
148 | });
149 |
--------------------------------------------------------------------------------
/playground/src/real-life-examples/Documentation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, WebView, ActivityIndicator } from 'react-native';
3 |
4 | const injectedJs =
5 | '[].forEach.call(document.querySelectorAll("img"),function(e){e.parentNode.removeChild(e);});' +
6 | '[].forEach.call(document.querySelectorAll("div.breadcrumb"),function(e){e.parentNode.removeChild(e);});' +
7 | '[].forEach.call(document.querySelectorAll("div.reponav-wrapper"),function(e){e.parentNode.removeChild(e);});' +
8 | '[].forEach.call(document.querySelectorAll("footer"),function(e){e.parentNode.removeChild(e);});' +
9 | '[].forEach.call(document.querySelectorAll("button.header-button"),function(e){e.parentNode.removeChild(e);});';
10 |
11 | export default class Documentation extends Component {
12 | render() {
13 | return (
14 |
21 | );
22 | }
23 | renderLoading() {
24 | return (
25 |
30 | );
31 | }
32 | }
33 |
34 | const styles = StyleSheet.create({
35 | container: {
36 | flex: 1
37 | }
38 | });
39 |
--------------------------------------------------------------------------------
/playground/src/real-life-examples/MapPanel.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Dimensions, Image, Text, Animated, TouchableOpacity } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | const Screen = {
6 | width: Dimensions.get('window').width,
7 | height: Dimensions.get('window').height - 75
8 | }
9 |
10 | export default class MapPanel extends Component {
11 | constructor(props) {
12 | super(props);
13 | this._deltaY = new Animated.Value(Screen.height-100);
14 | }
15 | render() {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
33 |
39 |
40 |
41 |
42 |
43 | San Francisco Airport
44 | International Airport - 40 miles away
45 |
46 | Directions
47 |
48 |
49 | Search Nearby
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | );
58 | }
59 | }
60 |
61 | const styles = StyleSheet.create({
62 | container: {
63 | flex: 1,
64 | justifyContent: 'center',
65 | alignItems: 'center',
66 | backgroundColor: '#efefef',
67 | },
68 | panelContainer: {
69 | position: 'absolute',
70 | top: 0,
71 | bottom: 0,
72 | left: 0,
73 | right: 0
74 | },
75 | panel: {
76 | height: Screen.height + 300,
77 | padding: 20,
78 | backgroundColor: '#f7f5eee8',
79 | borderTopLeftRadius: 20,
80 | borderTopRightRadius: 20,
81 | shadowColor: 'black',
82 | shadowOffset: {width: 0, height: 0},
83 | shadowRadius: 5,
84 | shadowOpacity: 0.4
85 | },
86 | panelHeader: {
87 | alignItems: 'center'
88 | },
89 | panelHandle: {
90 | width: 40,
91 | height: 8,
92 | borderRadius: 4,
93 | backgroundColor: '#00000040',
94 | marginBottom: 10
95 | },
96 | panelTitle: {
97 | fontSize: 27,
98 | height: 35
99 | },
100 | panelSubtitle: {
101 | fontSize: 14,
102 | color: 'gray',
103 | height: 30,
104 | marginBottom: 10
105 | },
106 | panelButton: {
107 | padding: 20,
108 | borderRadius: 10,
109 | backgroundColor: '#459FED',
110 | alignItems: 'center',
111 | marginVertical: 10
112 | },
113 | panelButtonTitle: {
114 | fontSize: 17,
115 | fontWeight: 'bold',
116 | color: 'white'
117 | },
118 | photo: {
119 | width: Screen.width-40,
120 | height: 225,
121 | marginTop: 30
122 | },
123 | map: {
124 | height: Screen.height,
125 | width: Screen.width
126 | }
127 | });
128 |
--------------------------------------------------------------------------------
/playground/src/real-life-examples/NotifPanel.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Dimensions, Image, Text, Platform } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | const Screen = {
6 | width: Dimensions.get('window').width,
7 | height: Dimensions.get('window').height - 75
8 | }
9 |
10 | export default class NotifPanel extends Component {
11 | render() {
12 | return (
13 |
14 |
15 |
16 | Content Title
17 |
18 | This is the content body
19 |
20 |
21 |
22 |
28 |
29 | Today
30 |
31 |
32 |
33 | {Screen.height <= 500-75 ? false :
34 |
35 | Yesterday
36 |
37 |
38 | }
39 |
40 | 4 NOTIFICATIONS
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | );
50 | }
51 | }
52 |
53 | class Notification extends Component {
54 | render() {
55 | return (
56 |
57 |
58 | {this.props.title}
59 |
60 | {this.props.body}
61 |
62 | );
63 | }
64 | }
65 |
66 | const styles = StyleSheet.create({
67 | container: {
68 | flex: 1,
69 | justifyContent: 'center',
70 | alignItems: 'center',
71 | backgroundColor: '#efefef',
72 | },
73 | panelContainer: {
74 | position: 'absolute',
75 | top: 0,
76 | bottom: 0,
77 | left: 0,
78 | right: 0
79 | },
80 | panel: {
81 | height: Screen.height + 2,
82 | backgroundColor: '#182e43f0',
83 | padding: 15,
84 | paddingTop: 30,
85 | flexDirection: 'column'
86 | },
87 | contentTitle: {
88 | fontSize: 20,
89 | marginBottom: 10
90 | },
91 | contentImage: {
92 | width: Screen.width-50,
93 | height: Screen.width-50
94 | },
95 | contentBody: {
96 | fontSize: 18,
97 | color: 'gray',
98 | marginTop: 10
99 | },
100 | panelHeader: {
101 | fontSize: 24,
102 | color: 'white',
103 | marginTop: 15,
104 | marginBottom: 10,
105 | marginLeft: 10,
106 | justifyContent: 'flex-start'
107 | },
108 | panelFooterIos: {
109 | flex: 1,
110 | alignItems: 'center',
111 | justifyContent: 'flex-end'
112 | },
113 | panelFooterAndroid: {
114 | flex: 1,
115 | paddingTop: 15,
116 | alignItems: 'center',
117 | justifyContent: 'center'
118 | },
119 | panelFooterText: {
120 | fontSize: 13,
121 | color: '#ffffff80',
122 | marginBottom: 10
123 | },
124 | panelHandle: {
125 | width: 40,
126 | height: 8,
127 | borderRadius: 4,
128 | backgroundColor: '#ffffff80'
129 | },
130 | notificationContainer: {
131 | backgroundColor: '#b0cbdd',
132 | borderRadius: 14,
133 | marginBottom: 10
134 | },
135 | notificationHeader: {
136 | height: 30,
137 | backgroundColor: '#c3d6e1',
138 | borderTopLeftRadius: 14,
139 | borderTopRightRadius: 14,
140 | paddingHorizontal: 20
141 | },
142 | notificationTitle: {
143 | marginTop: 5,
144 | fontSize: 16,
145 | color: '#1c5675'
146 | },
147 | notificationBody: {
148 | marginVertical: 14,
149 | marginHorizontal: 20
150 | }
151 | });
152 |
--------------------------------------------------------------------------------
/playground/src/real-life-examples/NowCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Dimensions, Image, Text, Animated, Slider } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | const Screen = Dimensions.get('window');
6 |
7 | export default class NowCard extends Component {
8 | constructor(props) {
9 | super(props);
10 | this._deltaX = new Animated.Value(0);
11 | this.state = {
12 | damping: 1-0.7,
13 | tension: 300
14 | };
15 | }
16 | render() {
17 | return (
18 |
19 |
20 |
28 |
36 | Info for you
37 |
38 | Card Title
39 | This is the card body, it can be long
40 |
41 |
42 |
43 |
44 | Change spring damping:
45 | this.setState({damping: value})}
55 | />
56 | Change spring tension:
57 | this.setState({tension: value})}
67 | />
68 |
69 |
70 |
71 | );
72 | }
73 | }
74 |
75 | const styles = StyleSheet.create({
76 | container: {
77 | flex: 1,
78 | justifyContent: 'center',
79 | alignItems: 'center',
80 | backgroundColor: '#efefef',
81 | },
82 | card: {
83 | width: Screen.width - 40,
84 | backgroundColor: 'white',
85 | borderRadius: 6,
86 | marginHorizontal: 20,
87 | marginVertical: 10,
88 | shadowColor: '#7f7f7f',
89 | shadowOffset: {width: 0, height: 0},
90 | shadowRadius: 2,
91 | shadowOpacity: 0.4,
92 | elevation: 4
93 | },
94 | image: {
95 | width: Screen.width - 40,
96 | height: Screen.height <= 500 ? 70 : 150
97 | },
98 | header: {
99 | marginTop: 8,
100 | marginLeft: 20,
101 | height: 22,
102 | fontSize: 12,
103 | color: '#7f7f7f',
104 | overflow: 'hidden'
105 | },
106 | title: {
107 | fontSize: 18,
108 | marginTop: 15,
109 | marginBottom: 10,
110 | marginLeft: 15
111 | },
112 | body: {
113 | marginBottom: 20,
114 | fontSize: 15,
115 | marginLeft: 15,
116 | color: '#7f7f7f'
117 | },
118 | playground: {
119 | marginTop: Screen.height <= 500 ? 10 : 40,
120 | padding: 20,
121 | width: Screen.width - 40,
122 | backgroundColor: '#459FED',
123 | alignItems: 'stretch'
124 | },
125 | playgroundLabel: {
126 | color: 'white',
127 | fontSize: 14,
128 | fontWeight: 'bold',
129 | marginBottom: 15
130 | },
131 | slider: {
132 | height: 40
133 | }
134 | });
135 |
--------------------------------------------------------------------------------
/playground/src/real-life-examples/TinderCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Dimensions, Image, Text, Animated } from 'react-native';
3 | import Interactable from 'react-native-interactable';
4 |
5 | const Screen = Dimensions.get('window');
6 |
7 | export default class TinderCard extends Component {
8 | constructor(props) {
9 | super(props);
10 | this._deltaX = new Animated.Value(0);
11 | }
12 | render() {
13 | return (
14 |
15 |
16 |
24 |
32 |
33 |
34 |
35 |
43 | Trash
44 |
45 |
46 |
54 | Keep
55 |
56 |
57 |
58 |
59 |
60 |
61 | Swipe LEFT to trash
62 | or RIGHT to keep
63 |
64 |
65 |
66 | );
67 | }
68 | }
69 |
70 | const styles = StyleSheet.create({
71 | container: {
72 | flex: 1,
73 | justifyContent: 'center',
74 | alignItems: 'center',
75 | backgroundColor: '#efefef',
76 | width: Screen.width + 80,
77 | alignSelf: 'center'
78 | },
79 | card: {
80 | width: Screen.width - 40,
81 | marginHorizontal: 20,
82 | borderColor: 'white',
83 | borderWidth: 3
84 | },
85 | image: {
86 | width: Screen.width - 40 - 6,
87 | height: Screen.width - 40 - 6
88 | },
89 | overlay: {
90 | position: 'absolute',
91 | left: 0,
92 | right: 0,
93 | top: 0,
94 | bottom: 0,
95 | justifyContent: 'center',
96 | alignItems: 'center'
97 | },
98 | overlayText: {
99 | fontSize: 60,
100 | color: 'white'
101 | },
102 | text: {
103 | textAlign: 'center',
104 | marginTop: 4,
105 | fontSize: 18,
106 | fontWeight: 'bold',
107 | color: '#aaaaaa'
108 | }
109 | });
110 |
--------------------------------------------------------------------------------
/playground/src/real-life-examples/UxInspirations.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, WebView, ActivityIndicator } from 'react-native';
3 |
4 | const injectedJs =
5 | '[].forEach.call(document.querySelectorAll("img"),function(e){e.parentNode.removeChild(e);});' +
6 | '[].forEach.call(document.querySelectorAll("div.breadcrumb"),function(e){e.parentNode.removeChild(e);});' +
7 | '[].forEach.call(document.querySelectorAll("div.reponav-wrapper"),function(e){e.parentNode.removeChild(e);});' +
8 | '[].forEach.call(document.querySelectorAll("footer"),function(e){e.parentNode.removeChild(e);});' +
9 | '[].forEach.call(document.querySelectorAll("button.header-button"),function(e){e.parentNode.removeChild(e);});';
10 |
11 | export default class UxInspirations extends Component {
12 | render() {
13 | return (
14 |
21 | );
22 | }
23 | renderLoading() {
24 | return (
25 |
30 | );
31 | }
32 | }
33 |
34 | const styles = StyleSheet.create({
35 | container: {
36 | flex: 1
37 | }
38 | });
39 |
--------------------------------------------------------------------------------
/rn-cli.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | getProjectRoots() {
3 | return [__dirname, `${__dirname}/playground`];
4 | }
5 | };
--------------------------------------------------------------------------------
/scripts/ci.android.sh:
--------------------------------------------------------------------------------
1 | yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses
2 |
3 | npm install
4 | # npm run e2e:android:ci
5 |
--------------------------------------------------------------------------------
/scripts/ci.ios.sh:
--------------------------------------------------------------------------------
1 | npm install
2 | # npm run podinstall
3 | # npm run build:ios
4 | # npm run e2e:ios
5 |
--------------------------------------------------------------------------------
/typings/react-native-interactable.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'react-native-interactable' {
2 |
3 | import {Animated, ViewProperties, ViewStyle} from 'react-native';
4 |
5 | namespace Interactable {
6 | interface ISnapPoint {
7 | x?: number;
8 | y?: number;
9 | damping?: number;
10 | tension?: number;
11 | id?: string;
12 | }
13 |
14 | interface IInfluenceArea {
15 | left?: number;
16 | right?: number;
17 | top?: number;
18 | bottom?: number;
19 | }
20 |
21 | interface ISpringPoint {
22 | x?: number;
23 | y?: number;
24 | damping?: number;
25 | tension?: number;
26 | influenceArea?: IInfluenceArea;
27 | haptics?: boolean;
28 | }
29 |
30 | interface IGravityPoints {
31 | x?: number;
32 | y?: number;
33 | strength?: number;
34 | falloff?: number;
35 | damping?: number;
36 | influenceArea?: IInfluenceArea;
37 | haptics?: boolean;
38 | }
39 |
40 | interface IFrictionArea {
41 | damping?: number;
42 | influenceArea?: IInfluenceArea;
43 | haptics?: boolean;
44 | }
45 |
46 | interface IAlertArea {
47 | id: string;
48 | influenceArea: IInfluenceArea;
49 | }
50 |
51 | interface IBoundaries {
52 | left?: number;
53 | right?: number;
54 | top?: number;
55 | bottom?: number;
56 | bounce?: number;
57 | haptics?: boolean;
58 | }
59 |
60 | interface IDragWithSpring {
61 | tension?: number;
62 | damping?: number;
63 | }
64 |
65 | interface IInitialPosition {
66 | x?: number;
67 | y?: number;
68 | }
69 |
70 | interface INativeSnapEvent {
71 | index: number;
72 | id: string;
73 | }
74 |
75 | interface ISnapEvent {
76 | nativeEvent: INativeSnapEvent;
77 | }
78 |
79 | interface INativeStopEvent {
80 | x: number;
81 | y: number;
82 | }
83 |
84 | interface IStopEvent {
85 | nativeEvent: INativeStopEvent;
86 | }
87 |
88 | type NativeDragEventStartStateType = 'start';
89 | type NativeDragEventEndStateType = 'end';
90 | type NativeDragEventState = NativeDragEventStartStateType | NativeDragEventEndStateType;
91 |
92 | interface INativeDragEvent {
93 | state: NativeDragEventState;
94 | x: number;
95 | y: number;
96 | }
97 |
98 | interface IDragEvent {
99 | nativeEvent: INativeDragEvent;
100 | }
101 |
102 | type NativeAlertEventEnterValueType = 'enter';
103 | type NativeAlertEventLeaveValueType = 'leave';
104 | type NativeAlertEventValue = NativeAlertEventEnterValueType | NativeAlertEventLeaveValueType;
105 |
106 | interface INativeAlertEvent { [id: string]: NativeAlertEventValue }
107 |
108 | interface IAlertEvent {
109 | nativeEvent: INativeAlertEvent;
110 | }
111 |
112 | interface IInteractableView extends ViewProperties {
113 | snapPoints?: ISnapPoint[];
114 | springPoints?: ISpringPoint[];
115 | gravityPoints?: IGravityPoints[];
116 | frictionAreas?: IFrictionArea[];
117 | alertAreas?: IAlertArea[];
118 | horizontalOnly?: boolean;
119 | verticalOnly?: boolean;
120 | boundaries?: IBoundaries;
121 | onSnap?: (event: ISnapEvent) => void;
122 | onSnapStart?: (event: ISnapEvent) => void;
123 | onStop?: (event: IStopEvent) => void;
124 | onDrag?: (event: IDragEvent) => void;
125 | onAlert?: (event: IAlertEvent) => void;
126 | dragEnabled?: boolean;
127 | dragWithSpring?: IDragWithSpring;
128 | dragToss?: number;
129 | animatedValueX?: Animated.Value;
130 | animatedValueY?: Animated.Value;
131 | animatedNativeDriver?: boolean;
132 | initialPosition?: IInitialPosition;
133 | style?: ViewStyle;
134 | }
135 |
136 | interface IInteractable {
137 | View: new () => React.Component;
138 | }
139 | }
140 |
141 | const Interactable: Interactable.IInteractable;
142 |
143 | export = Interactable;
144 | }
145 |
--------------------------------------------------------------------------------