├── RNExample ├── .watchmanconfig ├── .gitattributes ├── index.js ├── .babelrc ├── app.json ├── android │ ├── app │ │ ├── src │ │ │ └── main │ │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── strings.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ └── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── rnexample │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ │ └── AndroidManifest.xml │ │ ├── BUCK │ │ ├── proguard-rules.pro │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── keystores │ │ ├── debug.keystore.properties │ │ └── BUCK │ ├── settings.gradle │ ├── gradle.properties │ ├── build.gradle │ ├── gradlew.bat │ └── gradlew ├── ios │ ├── RNExample │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── AppDelegate.m │ │ ├── Info.plist │ │ └── Base.lproj │ │ │ └── LaunchScreen.xib │ ├── RNExampleTests │ │ ├── Info.plist │ │ └── RNExampleTests.m │ ├── RNExample-tvOSTests │ │ └── Info.plist │ ├── RNExample-tvOS │ │ └── Info.plist │ └── RNExample.xcodeproj │ │ └── xcshareddata │ │ └── xcschemes │ │ ├── RNExample.xcscheme │ │ └── RNExample-tvOS.xcscheme ├── .buckconfig ├── __tests__ │ └── App.js ├── .vscode │ └── launch.json ├── src │ ├── App.js │ ├── RowTemplate.js │ └── ListExample.js ├── package.json ├── .gitignore └── .flowconfig ├── lib ├── android │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── src │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── com │ │ │ └── sudoplz │ │ │ └── rnsynchronouslistmanager │ │ │ ├── Sync │ │ │ ├── Instructions │ │ │ │ ├── Instruction.java │ │ │ │ ├── SetViewInstruction.java │ │ │ │ └── CreateViewInstruction.java │ │ │ ├── SyncRegistry.java │ │ │ └── Recipe.java │ │ │ ├── Utils │ │ │ ├── FrameUtils.java │ │ │ ├── SPGlobals.java │ │ │ ├── WritableAdvancedMap.java │ │ │ └── WritableAdvancedArray.java │ │ │ ├── SynchronousListPackage.java │ │ │ ├── SynchronousListModule.java │ │ │ ├── SynchronousListManager.java │ │ │ └── Views │ │ │ ├── List │ │ │ ├── SPAdapter.java │ │ │ └── SPRecyclerView.java │ │ │ └── SyncRootView.java │ ├── build.gradle │ ├── gradlew.bat │ └── gradlew ├── ios │ ├── RCTSynchronousListManager │ │ ├── Sync │ │ │ ├── RCCSyncRootView.h │ │ │ ├── RCCSyncRegistry.h │ │ │ ├── RCCSyncRegistry.m │ │ │ └── RCCSyncRootView.m │ │ ├── RCTSynchronousListManager.h │ │ ├── Bind │ │ │ ├── NoLoopBinder.h │ │ │ ├── RepeatEdgeBinder.h │ │ │ ├── RepeatEmptyBinder.h │ │ │ ├── ScrollViewBindFactory.h │ │ │ ├── ScrollViewBindFactory.m │ │ │ ├── NoLoopBinder.m │ │ │ ├── RepeatEmptyBinder.m │ │ │ └── RepeatEdgeBinder.m │ │ ├── RCTSynchronousList.h │ │ └── RCTSynchronousListManager.m │ └── RCTSynchronousListManager.xcodeproj │ │ └── project.pbxproj └── js │ ├── components │ └── SynchronousList.js │ └── utils │ └── SyncRegistry.js ├── jsconfig.json ├── index.js ├── .npmignore ├── RNKeychain.podspec ├── .gitignore ├── LICENSE ├── package.json └── README.md /RNExample/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /RNExample/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /RNExample/index.js: -------------------------------------------------------------------------------- 1 | import App from './src/App'; -------------------------------------------------------------------------------- /RNExample/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } 4 | -------------------------------------------------------------------------------- /RNExample/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RNExample", 3 | "displayName": "RNExample" 4 | } -------------------------------------------------------------------------------- /RNExample/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RNExample 3 | 4 | -------------------------------------------------------------------------------- /RNExample/ios/RNExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /RNExample/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /lib/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudoPlz/react-native-synchronous-list/HEAD/lib/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /RNExample/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudoPlz/react-native-synchronous-list/HEAD/RNExample/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /RNExample/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /lib/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /RNExample/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudoPlz/react-native-synchronous-list/HEAD/RNExample/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /RNExample/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudoPlz/react-native-synchronous-list/HEAD/RNExample/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true 5 | }, 6 | "exclude": [ 7 | "node_modules" 8 | ] 9 | } -------------------------------------------------------------------------------- /RNExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudoPlz/react-native-synchronous-list/HEAD/RNExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /RNExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudoPlz/react-native-synchronous-list/HEAD/RNExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import SynchronousList from './lib/js/components/SynchronousList'; 2 | import SyncRegistry from './lib/js/utils/SyncRegistry'; 3 | 4 | module.exports = { 5 | SynchronousList, 6 | SyncRegistry, 7 | }; -------------------------------------------------------------------------------- /RNExample/android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | .DS_Store 4 | 5 | android/.DS_Store 6 | android/android.iml 7 | android/gradlew 8 | android/gradlew.bat 9 | android/local.properties 10 | lib/android/build/ 11 | 12 | ios/.DS_Store 13 | 14 | RNExample 15 | .RNExample/ -------------------------------------------------------------------------------- /RNExample/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RNExample/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 6 | -------------------------------------------------------------------------------- /RNExample/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'RNExample' 2 | include ':react-native-synchronous-list' 3 | project(':react-native-synchronous-list').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-synchronous-list/lib/android') 4 | 5 | include ':app' 6 | -------------------------------------------------------------------------------- /lib/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Feb 14 19:32:49 EET 2018 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-4.1-all.zip 7 | -------------------------------------------------------------------------------- /RNExample/__tests__/App.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import App from '../App'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/Sync/RCCSyncRootView.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | #if __has_include() 5 | #import 6 | #else 7 | #import "RCTRootView.h" 8 | #endif 9 | 10 | @interface RCCSyncRootView : RCTRootView 11 | 12 | - (void)updateProps:(NSDictionary *)newProps; 13 | 14 | @property (nonatomic) int boundToIndex; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/RCTSynchronousListManager.h: -------------------------------------------------------------------------------- 1 | #if __has_include() 2 | #import 3 | #else 4 | #import "RCTViewManager.h" 5 | #endif 6 | 7 | #import "RCTSynchronousList.h" 8 | 9 | @interface RCTSynchronousListManager : RCTViewManager 10 | 11 | @property (nonatomic, strong) RCTSynchronousList * _Nullable scrollView; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/Bind/NoLoopBinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // NoLoopBinder.h 3 | // example 4 | // 5 | // Created by John Kokkinidis on 12/09/2017. 6 | // Copyright © 2017 Facebook. All rights reserved. 7 | // 8 | 9 | #import "ScrollViewBindFactory.h" 10 | 11 | @interface NoLoopBinder : ScrollViewBindFactory 12 | 13 | - (NSDictionary*) getValueForRow: (int)rowIndex andDatasource: (NSMutableArray*) data; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/Bind/RepeatEdgeBinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // RepeatEdgeBinder.h 3 | // example 4 | // 5 | // Created by John Kokkinidis on 12/09/2017. 6 | // Copyright © 2017 Facebook. All rights reserved. 7 | // 8 | 9 | #import "ScrollViewBindFactory.h" 10 | 11 | @interface RepeatEdgeBinder : ScrollViewBindFactory 12 | - (NSDictionary*) getValueForRow: (int)rowIndex andDatasource: (NSMutableArray*) data; 13 | @end 14 | -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/Bind/RepeatEmptyBinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // RepeatEmptyBinder.h 3 | // example 4 | // 5 | // Created by John Kokkinidis on 12/09/2017. 6 | // Copyright © 2017 Facebook. All rights reserved. 7 | // 8 | 9 | #import "ScrollViewBindFactory.h" 10 | 11 | @interface RepeatEmptyBinder : ScrollViewBindFactory 12 | - (NSDictionary*) getValueForRow: (int)rowIndex andDatasource: (NSMutableArray*) data; 13 | @end 14 | -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/Sync/RCCSyncRegistry.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #if __has_include() 4 | #import 5 | #else 6 | #import "RCTBridgeModule.h" 7 | #endif 8 | 9 | @interface RCCSyncRegistry : NSObject 10 | 11 | @property (nonatomic, strong) NSMutableDictionary *registry; 12 | @property (nonatomic, strong) NSNumber *lastTag; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/Bind/ScrollViewBindFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // ScrollViewBindFactory.h 3 | // example 4 | // 5 | // Created by John Kokkinidis on 12/09/2017. 6 | // Copyright © 2017 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ScrollViewBindFactory : NSObject 12 | 13 | - (NSDictionary*) getValueForRow: (int)rowIndex andDatasource: (NSMutableArray*) data; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/Bind/ScrollViewBindFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // ScrollViewBindFactory.m 3 | // example 4 | // 5 | // Created by John Kokkinidis on 12/09/2017. 6 | // Copyright © 2017 Facebook. All rights reserved. 7 | // 8 | 9 | #import "ScrollViewBindFactory.h" 10 | 11 | @implementation ScrollViewBindFactory 12 | 13 | - (NSDictionary*) getValueForRow: (int)rowIndex andDatasource: (NSMutableArray*) data { 14 | return nil; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /RNExample/android/app/src/main/java/com/rnexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.rnexample; 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 "RNExample"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /RNExample/ios/RNExample/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 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/Sync/RCCSyncRegistry.m: -------------------------------------------------------------------------------- 1 | #import "RCCSyncRegistry.h" 2 | 3 | @implementation RCCSyncRegistry 4 | 5 | RCT_EXPORT_MODULE(); 6 | 7 | -(instancetype)init 8 | { 9 | self = [super init]; 10 | self.registry = [NSMutableDictionary new]; 11 | self.lastTag = @(1000001); 12 | return self; 13 | } 14 | 15 | RCT_EXPORT_METHOD(registerRecipe:(NSString*)registeredName props:(NSDictionary*)props recipe:(NSArray*)recipe) 16 | { 17 | [self.registry setObject:@{@"props": props, @"recipe": recipe} forKey:registeredName]; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /RNExample/ios/RNExample/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 | -------------------------------------------------------------------------------- /lib/android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion "26.0.2" 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | } 17 | } 18 | } 19 | 20 | dependencies { 21 | compile 'com.android.support:appcompat-v7:26.0.2' 22 | compile "com.facebook.react:react-native:+" // From node_modules 23 | compile 'com.android.support:recyclerview-v7:26.0.2' 24 | 25 | } 26 | -------------------------------------------------------------------------------- /lib/android/src/main/java/com/sudoplz/rnsynchronouslistmanager/Sync/Instructions/Instruction.java: -------------------------------------------------------------------------------- 1 | package com.sudoplz.rnsynchronouslistmanager.Sync.Instructions; 2 | 3 | import com.facebook.react.bridge.ReadableArray; 4 | import com.facebook.react.bridge.ReadableMap; 5 | import com.sudoplz.rnsynchronouslistmanager.Utils.WritableAdvancedMap; 6 | 7 | /** 8 | * Created by SudoPlz on 02/11/2017. 9 | */ 10 | 11 | public interface Instruction { 12 | 13 | public int getInitialRootTag(); 14 | public int getTag(); 15 | public String getModuleName(); 16 | public WritableAdvancedMap getProps(); 17 | public ReadableArray getHierarchy(); 18 | public String getInstructionType(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /RNExample/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Attach to packager", 9 | "program": "${workspaceRoot}/.vscode/launchReactNative.js", 10 | "type": "reactnative", 11 | "request": "attach", 12 | "sourceMaps": true, 13 | "outDir": "${workspaceRoot}/.vscode/.react" 14 | }, 15 | { 16 | "type": "node", 17 | "request": "launch", 18 | "name": "Launch Program", 19 | "program": "${workspaceFolder}/start" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /RNExample/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | AppRegistry, 4 | StyleSheet, 5 | View, 6 | Text, 7 | } from 'react-native'; 8 | 9 | import { TemplateName } from './RowTemplate'; // must run before rendering anything else 10 | import ListExample from './ListExample'; 11 | 12 | 13 | 14 | const styles = StyleSheet.create({ 15 | container: { 16 | flex: 1, 17 | alignItems: 'center', 18 | justifyContent: 'center', 19 | backgroundColor: 'blue' 20 | }, 21 | }); 22 | 23 | 24 | export default class App extends React.Component { 25 | render() { 26 | return ( 27 | 28 | 29 | 30 | ); 31 | } 32 | } 33 | AppRegistry.registerComponent('RNExample', () => App); 34 | -------------------------------------------------------------------------------- /RNExample/ios/RNExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /RNKeychain.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | version = JSON.parse(File.read('package.json'))["version"] 3 | 4 | Pod::Spec.new do |s| 5 | 6 | s.name = "SPRNSynchronousList" 7 | s.version = version 8 | s.summary = "A list that renders synchronously on React Native." 9 | s.homepage = "https://github.com/SudoPlz/react-native-synchronous-list" 10 | s.license = "MIT" 11 | s.author = { "Ioannis Kokkinidis" => "sudoplz@gmail.com" } 12 | s.ios.deployment_target = '7.0' 13 | s.tvos.deployment_target = '9.0' 14 | s.source = { :git => "https://github.com/SudoPlz/react-native-synchronous-list.git", :tag => "v#{s.version}" } 15 | s.source_files = 'SPRNSynchronousListManager/**/*.{h,m}' 16 | s.preserve_paths = "**/*.js" 17 | s.dependency 'React' 18 | 19 | end 20 | -------------------------------------------------------------------------------- /RNExample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RNExample", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest", 8 | "forceClean": "watchman watch-del-all;rm -rf ./node_modules;rm -rf $TMPDIR/react-*;yarn install", 9 | "copyToLib": "cp -R node_modules/react-native-synchronous-list/lib/ ../lib/" 10 | }, 11 | "dependencies": { 12 | "react": "16.2.0", 13 | "react-native": "0.53.0" 14 | }, 15 | "devDependencies": { 16 | "babel-jest": "22.2.2", 17 | "babel-preset-react-native": "4.0.0", 18 | "jest": "22.3.0", 19 | "react-test-renderer": "16.2.0", 20 | "react-native-synchronous-list": "github:SudoPlz/react-native-synchronous-list" 21 | }, 22 | "jest": { 23 | "preset": "react-native" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/Bind/NoLoopBinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // NoLoopBinder.m 3 | // example 4 | // 5 | // Created by John Kokkinidis on 12/09/2017. 6 | // Copyright © 2017 Facebook. All rights reserved. 7 | // 8 | 9 | #import "NoLoopBinder.h" 10 | 11 | @implementation NoLoopBinder 12 | 13 | - (NSDictionary*) getValueForRow: (int)rowIndex andDatasource: (NSMutableArray*) data { 14 | if (data != nil) { 15 | // NSLog(@"\n\n******* Binding data row %d.", rowIndex); 16 | if (rowIndex >= 0 && rowIndex < data.count) { // if the data index is within our data bounds 17 | NSMutableDictionary* rowData = [[data objectAtIndex:rowIndex] mutableCopy]; 18 | rowData[@"index"] = [NSNumber numberWithInt:rowIndex]; 19 | return rowData; 20 | } 21 | } 22 | return nil; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /RNExample/ios/RNExampleTests/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 | -------------------------------------------------------------------------------- /RNExample/ios/RNExample-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/RCTSynchronousListManager/Bind/RepeatEmptyBinder.m: -------------------------------------------------------------------------------- 1 | // 2 | // RepeatEmptyBinder.m 3 | // example 4 | // 5 | // Created by John Kokkinidis on 12/09/2017. 6 | // Copyright © 2017 Facebook. All rights reserved. 7 | // 8 | 9 | #import "RepeatEmptyBinder.h" 10 | 11 | @implementation RepeatEmptyBinder 12 | 13 | - (NSDictionary*) getValueForRow: (int)rowIndex andDatasource: (NSMutableArray*) data { 14 | if (data != nil) { 15 | // NSLog(@"******* Binding childIndex %d to data row %d.", childIndex, rowIndex); 16 | 17 | if (rowIndex >= 0 && rowIndex < data.count) { // if the data index is within our data bounds 18 | NSMutableDictionary* rowData = [[data objectAtIndex:rowIndex] mutableCopy]; 19 | rowData[@"index"] = [NSNumber numberWithInt:rowIndex]; 20 | return rowData; 21 | } 22 | } 23 | return nil; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /lib/android/src/main/java/com/sudoplz/rnsynchronouslistmanager/Utils/FrameUtils.java: -------------------------------------------------------------------------------- 1 | package com.sudoplz.rnsynchronouslistmanager.Utils; 2 | 3 | import android.content.res.Resources; 4 | 5 | import com.facebook.react.bridge.ReadableMap; 6 | 7 | /** 8 | * Created by idynopia on 23/02/2018. 9 | */ 10 | 11 | public class FrameUtils { 12 | public static int extractItemWidth(ReadableMap props) { 13 | if (props != null && props.hasKey("width")) { 14 | return dpToPx(props.getInt("width")); 15 | } 16 | return 0; 17 | } 18 | 19 | public static int extractItemHeight(ReadableMap props) { 20 | if (props != null && props.hasKey("height")) { 21 | return dpToPx(props.getInt("height")); 22 | } 23 | return 0; 24 | } 25 | 26 | public static int dpToPx(int dp) { 27 | return (int) (dp * Resources.getSystem().getDisplayMetrics().density); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /RNExample/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.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /RNExample/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | maven { 7 | url 'https://maven.google.com/' 8 | name 'Google' 9 | } 10 | } 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:2.2.3' 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | mavenLocal() 22 | jcenter() 23 | maven { 24 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 25 | url "$rootDir/../node_modules/react-native/android" 26 | } 27 | maven { 28 | url 'https://maven.google.com/' 29 | name 'Google' 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /RNExample/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | !**/*.xcodeproj 3 | !**/*.pbxproj 4 | !**/*.xcworkspacedata 5 | !**/*.xcsettings 6 | !**/*.xcscheme 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | *.xcuserstate 22 | project.xcworkspace 23 | 24 | # Gradle 25 | /build/ 26 | **/lib/android/build/ 27 | **/android/build/ 28 | RNExample/android/app/build/ 29 | RNExample/android/app/gradle/ 30 | RNExample/android/app/gradlew 31 | RNExample/android/app/gradlew.bat 32 | RNExample/build/ 33 | RNExample/android/captures 34 | 35 | # Buck 36 | .buckd 37 | buck-out 38 | RNExample/src/main/jni/prebuilt/lib/armeabi-v7a/ 39 | RNExample/src/main/jni/prebuilt/lib/x86/ 40 | RNExample/src/main/gen 41 | 42 | # Android 43 | .idea 44 | .gradle 45 | local.properties 46 | *.iml 47 | /android/ 48 | 49 | # Node 50 | node_modules 51 | *.log 52 | .nvm 53 | /danger/node_modules/ 54 | 55 | # OS X 56 | .DS_Store 57 | 58 | # Test generated files 59 | *.js.meta 60 | 61 | /coverage 62 | /third-party 63 | 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ioannis Kokkinidis 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 | 23 | -------------------------------------------------------------------------------- /RNExample/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /RNExample/android/app/src/main/java/com/rnexample/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.rnexample; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.sudoplz.rnsynchronouslistmanager.SynchronousListPackage; 7 | import com.facebook.react.ReactNativeHost; 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.shell.MainReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | public class MainApplication extends Application implements ReactApplication { 16 | 17 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 18 | @Override 19 | public boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() { 25 | return Arrays.asList( 26 | new MainReactPackage(), 27 | new SynchronousListPackage(getReactNativeHost()) 28 | ); 29 | } 30 | 31 | @Override 32 | protected String getJSMainModuleName() { 33 | return "index"; 34 | } 35 | }; 36 | 37 | @Override 38 | public ReactNativeHost getReactNativeHost() { 39 | return mReactNativeHost; 40 | } 41 | 42 | @Override 43 | public void onCreate() { 44 | super.onCreate(); 45 | SoLoader.init(this, /* native exopackage */ false); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/js/components/SynchronousList.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import { 4 | requireNativeComponent, 5 | findNodeHandle, 6 | NativeModules, 7 | } from 'react-native'; 8 | 9 | 10 | const SynchronousListNativeComponent = requireNativeComponent('RCTSynchronousList', null); 11 | const { SynchronousListManager } = NativeModules; 12 | 13 | const LOOP_MODES = { 14 | NO_LOOP: 'no-loop', 15 | REPEAT_EMPTY: 'repeat-empty', 16 | REPEAT_EDGE: 'repeat-edge', 17 | } 18 | 19 | class SynchronousList extends React.Component { 20 | 21 | prepareRows() { 22 | SynchronousListManager.prepareRows(); 23 | } 24 | 25 | updateDataAtIndex(index, newData) { 26 | SynchronousListManager.updateDataAtIndex(index, newData); 27 | } 28 | 29 | appendDataToDataSource(newData) { 30 | SynchronousListManager.appendDataToDataSource(newData); 31 | } 32 | 33 | prependDataToDataSource(newData) { 34 | SynchronousListManager.prependDataToDataSource(newData); 35 | } 36 | 37 | scrollToItem(position, animated) { 38 | let useAnimation = animated; 39 | if (useAnimation == null) { 40 | useAnimation = true; 41 | } 42 | SynchronousListManager.scrollToItem(position, useAnimation); 43 | } 44 | 45 | render() { 46 | return ( 47 | 48 | ); 49 | } 50 | } 51 | SynchronousList.LOOP_MODES = LOOP_MODES; 52 | export default SynchronousList; 53 | -------------------------------------------------------------------------------- /RNExample/src/RowTemplate.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | // TextInput, 4 | Text, 5 | View, 6 | } from 'react-native'; 7 | import { SyncRegistry } from 'react-native-synchronous-list'; 8 | 9 | const TemplateName = 'MyTemplate'; 10 | 11 | // const RowTemplate = (props) => ( 12 | // 13 | // 18 | // 19 | // ); 20 | const RowTemplate = (props) => ( 21 | 22 | 26 | {props.name} 27 | 28 | 29 | ); 30 | 31 | /* 32 | CAUTION: 33 | We swizzle react.render so if you try to register a recipe while other components are rendering, 34 | you will get errors (usually manage children errors) 35 | ALWAYS register the recipe when react is NOT rendering other components 36 | (Preferably right when the app launches - and before you render other components!!) 37 | */ 38 | SyncRegistry.registerComponent(TemplateName, () => RowTemplate, ['name','width','height']); 39 | 40 | RowTemplate.TemplateName = TemplateName; 41 | export default RowTemplate; -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/RCTSynchronousList.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNTableViewChildren.h 3 | // example 4 | // 5 | // Created by Tal Kol on 6/8/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RCCSyncRootView.h" 11 | #import "ScrollViewBindFactory.h" 12 | #import "NoLoopBinder.h" 13 | #import "RepeatEdgeBinder.h" 14 | #import "RepeatEmptyBinder.h" 15 | 16 | @class RCTBridge; 17 | 18 | @interface RCTSynchronousList : UIScrollView 19 | 20 | - (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; 21 | - (void) createRows; 22 | - (void) appendDataToDataSource: (NSArray*) newData; 23 | - (void) prependDataToDataSource: (NSArray*) newData; 24 | - (void) updateDataAtIndex: (int) rowIndex withNewData: (id) newData; 25 | - (void) setScrollerZoom: (float) zoomScale animated: (BOOL) animated; 26 | - (int) getCurrentViewIndex; 27 | 28 | @property (nonatomic) float rowHeight; 29 | @property (nonatomic) float rowWidth; 30 | @property (nonatomic) float yeep; 31 | @property (nonatomic) int initialPosition; 32 | @property (nonatomic) NSInteger numRenderRows; 33 | @property (nonatomic) NSString *loopMode; 34 | @property (nonatomic) NSString *templateName; 35 | @property (nonatomic) NSMutableArray *data; 36 | @property (nonatomic) NSDictionary *defaultChildData; 37 | @property (nonatomic) BOOL horizontal; 38 | @property (nonatomic) BOOL paging; 39 | @property (nonatomic) BOOL dynamicViewSizes; 40 | 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /RNExample/ios/RNExample/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 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | NSURL *jsCodeLocation; 20 | 21 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 22 | 23 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 24 | moduleName:@"RNExample" 25 | initialProperties:nil 26 | launchOptions:launchOptions]; 27 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 28 | 29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 30 | UIViewController *rootViewController = [UIViewController new]; 31 | rootViewController.view = rootView; 32 | self.window.rootViewController = rootViewController; 33 | [self.window makeKeyAndVisible]; 34 | return YES; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /lib/android/src/main/java/com/sudoplz/rnsynchronouslistmanager/Sync/Instructions/SetViewInstruction.java: -------------------------------------------------------------------------------- 1 | package com.sudoplz.rnsynchronouslistmanager.Sync.Instructions; 2 | 3 | import com.facebook.react.bridge.ReadableArray; 4 | import com.facebook.react.bridge.WritableNativeArray; 5 | import com.sudoplz.rnsynchronouslistmanager.Utils.WritableAdvancedMap; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * Created by SudoPlz on 19/02/2018. 11 | */ 12 | 13 | public class SetViewInstruction implements Instruction { 14 | private int initialRootTag; 15 | private ReadableArray hierarchyArr; 16 | 17 | public SetViewInstruction(ReadableArray recipeArgs) { 18 | // "args": [3, [2]], 19 | initialRootTag = recipeArgs.getInt(0); 20 | hierarchyArr = recipeArgs.getArray(1); 21 | } 22 | @Override 23 | public int getInitialRootTag() { 24 | return initialRootTag; 25 | } 26 | 27 | @Override 28 | public int getTag() { 29 | return -1; 30 | } 31 | 32 | @Override 33 | public String getModuleName() { 34 | return null; 35 | } 36 | 37 | @Override 38 | public WritableAdvancedMap getProps() { 39 | return null; 40 | } 41 | 42 | @Override 43 | public ReadableArray getHierarchy() { 44 | return hierarchyArr; 45 | } 46 | 47 | @Override 48 | public String getInstructionType() { 49 | return "setChildren"; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "setChildren: "+initialRootTag+", "+hierarchyArr; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-synchronous-list", 3 | "version": "0.2.1", 4 | "description": "A react-native list (for iOS only) that renders it's children synchronously", 5 | "keywords": [ 6 | "synchronous", 7 | "list", 8 | "react-native", 9 | "react-native-synchronous-list" 10 | ], 11 | "author": { 12 | "name": "Ioannis Kokkinidis", 13 | "email": "sudoplz@gmail.com", 14 | "url": "https://www.linkedin.com/in/sudoplz" 15 | }, 16 | "license": "MIT", 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/SudoPlz/react-native-synchronous-list.git" 20 | }, 21 | "main": "index", 22 | "scripts": { 23 | "start": "node node_modules/react-native/local-cli/cli.js start", 24 | "test": "jest" 25 | }, 26 | "rnpm": { 27 | "android": { 28 | "sourceDir": "./lib/android", 29 | "packageInstance": "\t\tnew SynchronousListPackage(getReactNativeHost())" 30 | } 31 | }, 32 | "peerDependencies": { 33 | "react": "*", 34 | "react-native": "*", 35 | "prop-types": "*" 36 | }, 37 | "devDependencies": { 38 | "babel-eslint": "^6.1.2", 39 | "babel-plugin-module-resolver": "^2.3.0", 40 | "babel-preset-airbnb": "^1.1.1", 41 | "babel-preset-react-native": "3.0.2", 42 | "eslint": "^3.3.1", 43 | "eslint-config-airbnb": "^10.0.1", 44 | "eslint-plugin-import": "^1.14.0", 45 | "eslint-plugin-jsx-a11y": "^2.1.0", 46 | "eslint-plugin-prefer-object-spread": "^1.1.0", 47 | "eslint-plugin-react": "^6.1.2" 48 | }, 49 | "jest": { 50 | "preset": "react-native" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/android/src/main/java/com/sudoplz/rnsynchronouslistmanager/Sync/SyncRegistry.java: -------------------------------------------------------------------------------- 1 | package com.sudoplz.rnsynchronouslistmanager.Sync; 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext; 4 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 5 | import com.facebook.react.bridge.ReactMethod; 6 | import com.facebook.react.bridge.ReadableArray; 7 | import com.facebook.react.bridge.ReadableMap; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * Created by SudoPlz on 02/11/2017. 14 | */ 15 | 16 | public class SyncRegistry extends ReactContextBaseJavaModule { 17 | 18 | protected Map recipeRegistry; 19 | protected long lastTag; 20 | 21 | public SyncRegistry(ReactApplicationContext reactContext) { 22 | super(reactContext); 23 | recipeRegistry = new HashMap(); 24 | lastTag = 1000001; 25 | } 26 | 27 | @Override 28 | public String getName() { 29 | return "RCCSyncRegistry"; 30 | } 31 | 32 | public Map getRegistry() { 33 | return recipeRegistry; 34 | } 35 | 36 | @ReactMethod 37 | public void registerRecipe(String registeredName, ReadableMap props, ReadableArray recipe) { 38 | // Toast.makeText(getReactApplicationContext(), message, duration).show(); 39 | // [self.registry setObject:@{@"props": props, @"recipe": recipe} forKey:registeredName]; 40 | recipeRegistry.put(registeredName, new Recipe(props,recipe)); 41 | } 42 | 43 | public long getLastTag() { 44 | return lastTag; 45 | } 46 | 47 | public void setLastTag(long newLastTag) { 48 | lastTag = newLastTag; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /RNExample/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | 16 | ; Ignore polyfills 17 | .*/Libraries/polyfills/.* 18 | 19 | ; Ignore metro 20 | .*/node_modules/metro/.* 21 | 22 | [include] 23 | 24 | [libs] 25 | node_modules/react-native/Libraries/react-native/react-native-interface.js 26 | node_modules/react-native/flow/ 27 | node_modules/react-native/flow-github/ 28 | 29 | [options] 30 | emoji=true 31 | 32 | module.system=haste 33 | 34 | munge_underscores=true 35 | 36 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 37 | 38 | module.file_ext=.js 39 | module.file_ext=.jsx 40 | module.file_ext=.json 41 | module.file_ext=.native.js 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[a-z,_]*\\)?)\\) 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 50 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 51 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 52 | 53 | [version] 54 | ^0.63.0 55 | -------------------------------------------------------------------------------- /lib/android/src/main/java/com/sudoplz/rnsynchronouslistmanager/Sync/Instructions/CreateViewInstruction.java: -------------------------------------------------------------------------------- 1 | package com.sudoplz.rnsynchronouslistmanager.Sync.Instructions; 2 | 3 | import com.facebook.react.bridge.ReadableArray; 4 | import com.sudoplz.rnsynchronouslistmanager.Utils.WritableAdvancedMap; 5 | 6 | /** 7 | * Created by SudoPlz on 19/02/2018. 8 | */ 9 | 10 | public class CreateViewInstruction implements Instruction { 11 | private String moduleName; 12 | private int tag; 13 | private int initialRootTag; 14 | private WritableAdvancedMap props; 15 | 16 | public CreateViewInstruction (ReadableArray recipeArgs) { 17 | // "args":[125,"RCTText",1,{"allowFontScaling":true,"ellipsizeMode":"tail","accessible":true}]," 18 | tag = recipeArgs.getInt(0); 19 | moduleName = recipeArgs.getString(1); 20 | initialRootTag = recipeArgs.getInt(2); 21 | props = new WritableAdvancedMap(recipeArgs.getMap(3)); 22 | } 23 | @Override 24 | public int getInitialRootTag() { 25 | return initialRootTag; 26 | } 27 | 28 | @Override 29 | public int getTag() { 30 | return tag; 31 | } 32 | 33 | @Override 34 | public String getModuleName() { 35 | return moduleName; 36 | } 37 | 38 | @Override 39 | public WritableAdvancedMap getProps() { 40 | return props; 41 | } 42 | 43 | @Override 44 | public ReadableArray getHierarchy() { 45 | return null; 46 | } 47 | 48 | @Override 49 | public String getInstructionType() { 50 | return "createView"; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "createView: ["+tag+", "+moduleName+", "+initialRootTag+", "+props; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /RNExample/ios/RNExample-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSExceptionDomains 45 | 46 | localhost 47 | 48 | NSExceptionAllowsInsecureHTTPLoads 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /RNExample/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 | lib_deps = [] 12 | 13 | for jarfile in glob(['libs/*.jar']): 14 | name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] 15 | lib_deps.append(':' + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | 21 | for aarfile in glob(['libs/*.aar']): 22 | name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] 23 | lib_deps.append(':' + name) 24 | android_prebuilt_aar( 25 | name = name, 26 | aar = aarfile, 27 | ) 28 | 29 | android_library( 30 | name = "all-libs", 31 | exported_deps = lib_deps, 32 | ) 33 | 34 | android_library( 35 | name = "app-code", 36 | srcs = glob([ 37 | "src/main/java/**/*.java", 38 | ]), 39 | deps = [ 40 | ":all-libs", 41 | ":build_config", 42 | ":res", 43 | ], 44 | ) 45 | 46 | android_build_config( 47 | name = "build_config", 48 | package = "com.rnexample", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.rnexample", 54 | res = "src/main/res", 55 | ) 56 | 57 | android_binary( 58 | name = "app", 59 | keystore = "//android/keystores:debug", 60 | manifest = "src/main/AndroidManifest.xml", 61 | package_type = "debug", 62 | deps = [ 63 | ":app-code", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /lib/ios/RCTSynchronousListManager/RCTSynchronousListManager.m: -------------------------------------------------------------------------------- 1 | #import "RCTSynchronousListManager.h" 2 | 3 | @implementation RCTSynchronousListManager 4 | 5 | RCT_EXPORT_MODULE(); 6 | 7 | - (UIView *)view 8 | { 9 | _scrollView = [[RCTSynchronousList alloc] initWithBridge:self.bridge]; 10 | // [_scrollView setFrame:CGRectMake(0, 0, 960, 1132)]; 11 | return _scrollView; 12 | } 13 | 14 | RCT_EXPORT_VIEW_PROPERTY(templateName, NSString) 15 | RCT_EXPORT_VIEW_PROPERTY(rowHeight, float) 16 | RCT_EXPORT_VIEW_PROPERTY(rowWidth, float) 17 | RCT_EXPORT_VIEW_PROPERTY(numRenderRows, NSInteger) 18 | RCT_EXPORT_VIEW_PROPERTY(loopMode, NSString) 19 | RCT_EXPORT_VIEW_PROPERTY(data, NSArray) 20 | RCT_EXPORT_VIEW_PROPERTY(defaultChildData, NSDictionary) 21 | RCT_EXPORT_VIEW_PROPERTY(initialPosition, int) 22 | RCT_EXPORT_VIEW_PROPERTY(horizontal, BOOL) 23 | RCT_EXPORT_VIEW_PROPERTY(paging, BOOL) 24 | RCT_EXPORT_VIEW_PROPERTY(dynamicViewSizes, BOOL) 25 | 26 | RCT_EXPORT_METHOD(prepareRows) 27 | { 28 | [_scrollView createRows]; 29 | } 30 | 31 | RCT_EXPORT_METHOD(appendDataToDataSource: (NSArray *) newData) { 32 | [_scrollView appendDataToDataSource:newData]; 33 | } 34 | 35 | RCT_EXPORT_METHOD(prependDataToDataSource: (NSArray *) newData) { 36 | [_scrollView prependDataToDataSource:newData]; 37 | } 38 | 39 | RCT_EXPORT_METHOD(updateDataAtIndex: (int) rowIndex withNewData: (id) newData) { 40 | [_scrollView updateDataAtIndex:rowIndex withNewData:newData]; 41 | } 42 | 43 | RCT_EXPORT_METHOD(setScrollerZoom: (float) zoomScale animated: (BOOL) animated) { 44 | [_scrollView setScrollerZoom:zoomScale animated:animated]; 45 | } 46 | 47 | RCT_EXPORT_METHOD(getCurrentViewIndex:(RCTPromiseResolveBlock)resolve 48 | rejecter:(RCTPromiseRejectBlock)reject) { 49 | resolve([NSNumber numberWithInt:[_scrollView getCurrentViewIndex]]); 50 | } 51 | 52 | 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /RNExample/ios/RNExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | RNExample 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 | -------------------------------------------------------------------------------- /lib/android/src/main/java/com/sudoplz/rnsynchronouslistmanager/Sync/Recipe.java: -------------------------------------------------------------------------------- 1 | package com.sudoplz.rnsynchronouslistmanager.Sync; 2 | 3 | import com.facebook.react.bridge.ReadableArray; 4 | import com.facebook.react.bridge.ReadableMap; 5 | import com.sudoplz.rnsynchronouslistmanager.Sync.Instructions.CreateViewInstruction; 6 | import com.sudoplz.rnsynchronouslistmanager.Sync.Instructions.Instruction; 7 | import com.sudoplz.rnsynchronouslistmanager.Sync.Instructions.SetViewInstruction; 8 | import com.sudoplz.rnsynchronouslistmanager.Utils.WritableAdvancedMap; 9 | 10 | import java.util.ArrayList; 11 | 12 | /** 13 | * Created by SudoPlz on 19/02/2018. 14 | */ 15 | 16 | public class Recipe { 17 | 18 | private WritableAdvancedMap recipeBindings; 19 | private WritableAdvancedMap recipeInverseBindings; 20 | private ArrayList recipeInstructions; 21 | 22 | public Recipe(ReadableMap bindings, ReadableArray recipe) { 23 | recipeBindings = new WritableAdvancedMap(bindings); 24 | recipeInverseBindings = recipeBindings.getInverted(); 25 | 26 | recipeInstructions = new ArrayList(); 27 | for (int i = 0; i < recipe.size(); i++ ) { 28 | ReadableMap curInstruction = recipe.getMap(i); 29 | if (curInstruction.getString("cmd").equals("createView")) { 30 | recipeInstructions.add(new CreateViewInstruction(curInstruction.getArray("args"))); 31 | } else if (curInstruction.getString("cmd").equals("setChildren")) { 32 | recipeInstructions.add(new SetViewInstruction(curInstruction.getArray("args"))); 33 | } 34 | } 35 | } 36 | 37 | 38 | public ArrayList getRecipeInstructions() { 39 | return recipeInstructions; 40 | } 41 | 42 | public WritableAdvancedMap getRecipeBindings() { 43 | return recipeBindings; 44 | } 45 | 46 | public WritableAdvancedMap getRecipeInverseBindings() { 47 | return recipeInverseBindings; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/android/src/main/java/com/sudoplz/rnsynchronouslistmanager/SynchronousListPackage.java: -------------------------------------------------------------------------------- 1 | package com.sudoplz.rnsynchronouslistmanager; 2 | 3 | 4 | import com.sudoplz.rnsynchronouslistmanager.Sync.SyncRegistry; 5 | import com.sudoplz.rnsynchronouslistmanager.Utils.*; 6 | import com.facebook.react.ReactNativeHost; 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.bridge.NativeModule; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.uimanager.ViewManager; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | public class SynchronousListPackage implements ReactPackage { 16 | private SynchronousListManager listManager; 17 | private ReactNativeHost rcHost; 18 | 19 | public SynchronousListPackage(ReactNativeHost host) { 20 | rcHost = host; 21 | } 22 | /** 23 | * @param reactContext react application context that can be used to create modules 24 | * @return list of native modules to register with the newly created catalyst instance 25 | */ 26 | @Override 27 | public List createNativeModules(ReactApplicationContext reactContext) { 28 | if (listManager == null) { 29 | listManager = new SynchronousListManager(reactContext, rcHost); 30 | // we've passed the host, no need to keep a reference of it any more 31 | rcHost = null; 32 | } 33 | return Arrays.asList( 34 | new SyncRegistry(reactContext), 35 | new SynchronousListModule(reactContext, listManager) 36 | ); 37 | } 38 | 39 | 40 | 41 | /** 42 | * @param reactContext 43 | * @return a list of view managers that should be registered with {@link UIManagerModule} 44 | */ 45 | @Override 46 | public List createViewManagers(ReactApplicationContext reactContext) { 47 | if (listManager == null) { 48 | listManager = new SynchronousListManager(reactContext, rcHost); 49 | } 50 | return Arrays.asList( 51 | listManager 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/js/utils/SyncRegistry.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import React from 'react'; 3 | const ReactNative = require('react-native/Libraries/Renderer/shims/ReactNative'); 4 | const NativeModules = require('react-native').NativeModules; 5 | const UIManager = NativeModules.UIManager; 6 | const RCCSyncRegistry = NativeModules.RCCSyncRegistry; 7 | 8 | const origCreateView = UIManager.createView; 9 | const origSetChildren = UIManager.setChildren; 10 | const origManageChildren = UIManager.manageChildren; 11 | 12 | export default class SyncRegistry { 13 | static registerComponent(registeredName, componentGenerator, propNames) { 14 | const Template = componentGenerator(); 15 | const props = {}; 16 | for (const propName of propNames) { 17 | props[propName] = `__${propName}__`; 18 | } 19 | const recipe = []; 20 | prepareRecipeBySwizzlingUiManager(recipe); 21 | const rendered = ReactNative.render(