├── .gitattributes ├── example ├── .watchmanconfig ├── .gitattributes ├── app.json ├── android │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── app │ │ ├── .settings │ │ │ └── org.eclipse.buildship.core.prefs │ │ ├── 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 │ │ │ │ │ └── example │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ │ └── AndroidManifest.xml │ │ ├── .classpath │ │ ├── .project │ │ ├── BUCK │ │ ├── proguard-rules.pro │ │ └── build.gradle │ ├── keystores │ │ ├── debug.keystore.properties │ │ └── BUCK │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── .project │ ├── build.gradle │ ├── gradle.properties │ ├── gradlew.bat │ └── gradlew ├── ios │ ├── example │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── empty.swift │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── AppDelegate.m │ │ ├── Info.plist │ │ └── Base.lproj │ │ │ └── LaunchScreen.xib │ ├── example-Bridging-Header.h │ ├── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m │ ├── example-tvOSTests │ │ └── Info.plist │ ├── example-tvOS │ │ └── Info.plist │ └── example.xcodeproj │ │ └── xcshareddata │ │ └── xcschemes │ │ ├── example.xcscheme │ │ └── example-tvOS.xcscheme ├── .buckconfig ├── index.js ├── .babelrc ├── rn-cli.config.js ├── __tests__ │ ├── index.ios.js │ └── index.android.js ├── package.json ├── .gitignore ├── .flowconfig └── App.js ├── android ├── .settings │ └── org.eclipse.buildship.core.prefs ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── res │ │ └── values │ │ │ └── recyclerview-styles.xml │ │ └── java │ │ └── com │ │ └── github │ │ └── godness84 │ │ └── RNRecyclerViewList │ │ ├── RNRecyclerviewListModule.java │ │ ├── RecyclerViewItemViewManager.java │ │ ├── RNRecyclerviewListPackage.java │ │ ├── VisibleItemsChangeEvent.java │ │ ├── ContentSizeChangeEvent.java │ │ ├── RecyclerViewItemView.java │ │ ├── NotAnimatedItemAnimator.java │ │ ├── RecyclerViewBackedScrollViewManager.java │ │ └── RecyclerViewBackedScrollView.java ├── .classpath ├── .project ├── build.gradle ├── gradlew.bat └── gradlew ├── index.js ├── ios ├── RNReactNativeRecyclerviewList-Bridging-Header.h ├── RecyclerListItemView.swift ├── RNReactNativeRecyclerviewList.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── project.pbxproj ├── RNReactNativeRecyclerviewList.xcworkspace │ └── contents.xcworkspacedata ├── RNRecyclerListViewManager.h ├── RNRecyclerListItemViewManager.h ├── RNReactNativeRecyclerviewList.podspec ├── RNRecyclerListItemViewManager.m ├── RNRecyclerListViewManager.m └── RNRecyclerListView.swift ├── .gitignore ├── package.json ├── LICENSE.md ├── yarn.lock ├── src ├── DataSource.js ├── RecyclerViewList.ios.js └── RecyclerViewList.js └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "displayName": "example" 4 | } -------------------------------------------------------------------------------- /android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir= 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /example/android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | #Thu Apr 19 14:25:25 BST 2018 2 | connection.project.dir= 3 | -------------------------------------------------------------------------------- /example/android/app/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | #Thu Apr 19 14:25:25 BST 2018 2 | connection.project.dir=.. 3 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | example 3 | 4 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /example/ios/example-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import {AppRegistry} from 'react-native'; 2 | import example from './App'; 3 | 4 | AppRegistry.registerComponent('example', () => example); 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wordpress-mobile/react-native-recyclerview-list/trunk/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wordpress-mobile/react-native-recyclerview-list/trunk/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import RecyclerViewList from './src/RecyclerViewList'; 2 | import DataSource from './src/DataSource'; 3 | 4 | export default RecyclerViewList; 5 | export { DataSource }; 6 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wordpress-mobile/react-native-recyclerview-list/trunk/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wordpress-mobile/react-native-recyclerview-list/trunk/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wordpress-mobile/react-native-recyclerview-list/trunk/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wordpress-mobile/react-native-recyclerview-list/trunk/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"], 3 | "plugins": [ 4 | ["module-resolver", { 5 | "alias": { 6 | "react-native-recyclerview-list": "../" 7 | } 8 | }] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /example/ios/example/empty.swift: -------------------------------------------------------------------------------- 1 | // 2 | // empty.swift 3 | // example 4 | // 5 | // Created by Sergio Estevao on 24/04/2018. 6 | // Copyright © 2018 Facebook. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /android/src/main/res/values/recyclerview-styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/RNReactNativeRecyclerviewList-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import 6 | #import 7 | -------------------------------------------------------------------------------- /ios/RecyclerListItemView.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | @objc class RecyclerListItemView: RCTView { 5 | 6 | var itemIndex: NSInteger = NSNotFound 7 | 8 | var itemKey: NSInteger = NSNotFound 9 | 10 | } 11 | -------------------------------------------------------------------------------- /ios/RNReactNativeRecyclerviewList.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | 3 | include ':react-native-recyclerview-list' 4 | project(':react-native-recyclerview-list').projectDir = new File(rootProject.projectDir, '../../android') 5 | 6 | include ':app' 7 | 8 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 6 | -------------------------------------------------------------------------------- /ios/RNReactNativeRecyclerviewList.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | 3 | 5 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/rn-cli.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | getProjectRoots() { 5 | return [__dirname, path.resolve(__dirname, '..')]; 6 | }, 7 | getProvidesModuleNodeModules() { 8 | return [ 9 | 'react-native', 10 | 'react', 11 | ]; 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /ios/RNRecyclerListViewManager.h: -------------------------------------------------------------------------------- 1 | 2 | #if __has_include("RCTViewManager.h") 3 | #import "RCTViewManager.h" 4 | #else 5 | #import 6 | #endif 7 | 8 | #import "RNReactNativeRecyclerviewList-Swift.h" 9 | 10 | @interface RNRecyclerListViewManager : RCTViewManager 11 | 12 | @end 13 | 14 | -------------------------------------------------------------------------------- /ios/RNRecyclerListItemViewManager.h: -------------------------------------------------------------------------------- 1 | 2 | #if __has_include("RCTViewManager.h") 3 | #import "RCTViewManager.h" 4 | #else 5 | #import 6 | #endif 7 | 8 | #import "RNReactNativeRecyclerviewList-Swift.h" 9 | 10 | @interface RNRecyclerListItemViewManager : RCTViewManager 11 | 12 | @end 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # OSX 3 | # 4 | .DS_Store 5 | 6 | # node.js 7 | # 8 | node_modules/ 9 | npm-debug.log 10 | yarn-error.log 11 | 12 | 13 | # Android/IntelliJ 14 | # 15 | build/ 16 | .idea 17 | .gradle 18 | local.properties 19 | *.iml 20 | 21 | # BUCK 22 | buck-out/ 23 | \.buckd/ 24 | *.keystore 25 | 26 | # User Data 27 | xcuserdata/ 28 | -------------------------------------------------------------------------------- /example/__tests__/index.ios.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.ios.js'; 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 | -------------------------------------------------------------------------------- /ios/RNReactNativeRecyclerviewList.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/__tests__/index.android.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.android.js'; 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 | -------------------------------------------------------------------------------- /android/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/android/app/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "example"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | example 4 | Project example created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/ios/example/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 | -------------------------------------------------------------------------------- /example/ios/example/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 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "android": "react-native run-android", 8 | "ios": "react-native run-ios", 9 | "test": "jest" 10 | }, 11 | "dependencies": { 12 | "react": "^16.3.0-alpha.1", 13 | "react-native": "0.54.2" 14 | }, 15 | "devDependencies": { 16 | "babel-jest": "20.0.3", 17 | "babel-plugin-module-resolver": "^3.1.1", 18 | "babel-preset-react-native": "^2.1.0", 19 | "jest": "20.0.4", 20 | "react-test-renderer": "16.0.0-alpha.12" 21 | }, 22 | "jest": { 23 | "preset": "react-native" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /example/android/app/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app 4 | Project app created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project react-native-recyclerview-list created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/godness84/RNRecyclerViewList/RNRecyclerviewListModule.java: -------------------------------------------------------------------------------- 1 | 2 | package com.github.godness84.RNRecyclerViewList; 3 | 4 | import com.facebook.react.bridge.ReactApplicationContext; 5 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 6 | import com.facebook.react.bridge.ReactMethod; 7 | import com.facebook.react.bridge.Callback; 8 | 9 | public class RNRecyclerviewListModule extends ReactContextBaseJavaModule { 10 | 11 | private final ReactApplicationContext reactContext; 12 | 13 | public RNRecyclerviewListModule(ReactApplicationContext reactContext) { 14 | super(reactContext); 15 | this.reactContext = reactContext; 16 | } 17 | 18 | @Override 19 | public String getName() { 20 | return "RNRecyclerviewList"; 21 | } 22 | } -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.1.4' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | google() 19 | jcenter() 20 | maven { 21 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 22 | url "$rootDir/../node_modules/react-native/android" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /example/ios/example/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 | } -------------------------------------------------------------------------------- /ios/RNReactNativeRecyclerviewList.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | s.name = "RNReactNativeRecyclerviewList" 4 | s.version = "1.0.0" 5 | s.summary = "RNReactNativeRecyclerviewList" 6 | s.description = <<-DESC 7 | RNReactNativeRecyclerviewList 8 | DESC 9 | s.homepage = "" 10 | s.license = "MIT" 11 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 12 | s.author = { "author" => "author@domain.cn" } 13 | s.platform = :ios, "7.0" 14 | s.source = { :git => "https://github.com/author/RNReactNativeRecyclerviewList.git", :tag => "master" } 15 | s.source_files = "RNReactNativeRecyclerviewList/**/*.{h,m}" 16 | s.requires_arc = true 17 | 18 | 19 | s.dependency "React" 20 | #s.dependency "others" 21 | 22 | end 23 | 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-recyclerview-list", 3 | "version": "1.0.0", 4 | "description": "A RecyclerView implementation for React Native", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/godness84/react-native-recyclerview-list.git" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1", 12 | "clean": "rm -rf node_modules/ example/node_modules/ yarn.lock example/yarn.lock package-lock.json example/package-lock.json" 13 | }, 14 | "keywords": [ 15 | "recyclerview", 16 | "list", 17 | "react-native" 18 | ], 19 | "author": "", 20 | "license": "MIT", 21 | "peerDependencies": { 22 | "react": "16.6.1", 23 | "react-native": "0.57.5" 24 | }, 25 | "dependencies": { 26 | "prop-types": "^15.6.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example/ios/exampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/example-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 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/godness84/RNRecyclerViewList/RecyclerViewItemViewManager.java: -------------------------------------------------------------------------------- 1 | package com.github.godness84.RNRecyclerViewList; 2 | 3 | import com.facebook.react.uimanager.ThemedReactContext; 4 | import com.facebook.react.uimanager.ViewGroupManager; 5 | import com.facebook.react.uimanager.annotations.ReactProp; 6 | 7 | public class RecyclerViewItemViewManager extends ViewGroupManager { 8 | private static String REACT_CLASS = "RecyclerViewItemView"; 9 | 10 | @Override 11 | public String getName() { 12 | return REACT_CLASS; 13 | } 14 | 15 | @Override 16 | protected RecyclerViewItemView createViewInstance(ThemedReactContext reactContext) { 17 | return new RecyclerViewItemView(reactContext); 18 | } 19 | 20 | @ReactProp(name = "itemIndex") 21 | public void setItemIndex(RecyclerViewItemView view, int itemIndex) { 22 | view.setItemIndex(itemIndex); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/godness84/RNRecyclerViewList/RNRecyclerviewListPackage.java: -------------------------------------------------------------------------------- 1 | 2 | package com.github.godness84.RNRecyclerViewList; 3 | 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.bridge.NativeModule; 10 | import com.facebook.react.bridge.ReactApplicationContext; 11 | import com.facebook.react.uimanager.ViewManager; 12 | import com.facebook.react.bridge.JavaScriptModule; 13 | 14 | public class RNRecyclerviewListPackage implements ReactPackage { 15 | @Override 16 | public List createNativeModules(ReactApplicationContext reactContext) { 17 | return Collections.emptyList(); 18 | } 19 | 20 | @Override 21 | public List createViewManagers(ReactApplicationContext reactContext) { 22 | return Arrays.asList( 23 | new RecyclerViewBackedScrollViewManager(), 24 | new RecyclerViewItemViewManager() 25 | ); 26 | } 27 | } -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Marc Shilling 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 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/godness84/RNRecyclerViewList/VisibleItemsChangeEvent.java: -------------------------------------------------------------------------------- 1 | package com.github.godness84.RNRecyclerViewList; 2 | 3 | import com.facebook.react.bridge.Arguments; 4 | import com.facebook.react.bridge.WritableMap; 5 | import com.facebook.react.uimanager.events.Event; 6 | import com.facebook.react.uimanager.events.RCTEventEmitter; 7 | 8 | public class VisibleItemsChangeEvent extends Event { 9 | 10 | public static final String EVENT_NAME = "visibleItemsChange"; 11 | 12 | private final int mFirstIndex; 13 | private final int mLastIndex; 14 | 15 | public VisibleItemsChangeEvent(int viewTag, long timestampMs, int firstIndex, int lastIndex) { 16 | super(viewTag); 17 | mFirstIndex = firstIndex; 18 | mLastIndex = lastIndex; 19 | } 20 | 21 | @Override 22 | public String getEventName() { 23 | return EVENT_NAME; 24 | } 25 | 26 | @Override 27 | public void dispatch(RCTEventEmitter rctEventEmitter) { 28 | WritableMap data = Arguments.createMap(); 29 | data.putInt("firstIndex", mFirstIndex); 30 | data.putInt("lastIndex", mLastIndex); 31 | rctEventEmitter.receiveEvent(getViewTag(), EVENT_NAME, data); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "js-tokens@^3.0.0 || ^4.0.0": 6 | version "4.0.0" 7 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 8 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 9 | 10 | loose-envify@^1.3.1: 11 | version "1.4.0" 12 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" 13 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== 14 | dependencies: 15 | js-tokens "^3.0.0 || ^4.0.0" 16 | 17 | object-assign@^4.1.1: 18 | version "4.1.1" 19 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 20 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 21 | 22 | prop-types@^15.6.0: 23 | version "15.6.2" 24 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" 25 | integrity sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ== 26 | dependencies: 27 | loose-envify "^1.3.1" 28 | object-assign "^4.1.1" 29 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/godness84/RNRecyclerViewList/ContentSizeChangeEvent.java: -------------------------------------------------------------------------------- 1 | package com.github.godness84.RNRecyclerViewList; 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 | * Event dispatched by {@link RecyclerViewBackedScrollView} when total height of it's children 11 | * changes 12 | */ 13 | public class ContentSizeChangeEvent extends Event { 14 | 15 | public static final String EVENT_NAME = "topContentSizeChange"; 16 | 17 | private final int mWidth; 18 | private final int mHeight; 19 | 20 | public ContentSizeChangeEvent(int viewTag, long timestampMs, int width, int height) { 21 | super(viewTag); 22 | mWidth = width; 23 | mHeight = height; 24 | } 25 | 26 | @Override 27 | public String getEventName() { 28 | return EVENT_NAME; 29 | } 30 | 31 | @Override 32 | public void dispatch(RCTEventEmitter rctEventEmitter) { 33 | WritableMap data = Arguments.createMap(); 34 | data.putDouble("width", PixelUtil.toDIPFromPixel(mWidth)); 35 | data.putDouble("height", PixelUtil.toDIPFromPixel(mHeight)); 36 | rctEventEmitter.receiveEvent(getViewTag(), EVENT_NAME, data); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/godness84/RNRecyclerViewList/RecyclerViewItemView.java: -------------------------------------------------------------------------------- 1 | package com.github.godness84.RNRecyclerViewList; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.ViewGroup; 6 | 7 | public class RecyclerViewItemView extends ViewGroup { 8 | private static final String TAG = "RecyclerViewItem"; 9 | 10 | private int mItemIndex; 11 | private boolean mItemIndexInitialized; 12 | 13 | public RecyclerViewItemView(Context context) { 14 | super(context); 15 | } 16 | 17 | @Override 18 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 19 | // noop 20 | } 21 | 22 | public void setItemIndex(int itemIndex) { 23 | if (mItemIndexInitialized && this.mItemIndex != itemIndex){ 24 | this.mItemIndex = itemIndex; 25 | if (getParent() != null) { 26 | ((RecyclerViewBackedScrollView.RecyclableWrapperViewGroup) getParent()).getAdapter().notifyItemChanged(mItemIndex); 27 | ((RecyclerViewBackedScrollView.RecyclableWrapperViewGroup) getParent()).getAdapter().notifyItemChanged(itemIndex); 28 | } 29 | } else { 30 | this.mItemIndex = itemIndex; 31 | } 32 | 33 | mItemIndexInitialized = true; 34 | } 35 | 36 | public int getItemIndex() { 37 | return mItemIndex; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.facebook.react.ReactNativeHost; 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.shell.MainReactPackage; 9 | import com.facebook.soloader.SoLoader; 10 | import com.github.godness84.RNRecyclerViewList.RNRecyclerviewListPackage; 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 RNRecyclerviewListPackage() 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 | -------------------------------------------------------------------------------- /ios/RNRecyclerListItemViewManager.m: -------------------------------------------------------------------------------- 1 | 2 | #import "RNRecyclerListItemViewManager.h" 3 | #import 4 | #import 5 | #import 6 | 7 | typedef void (^ListItemViewBlock)(RecyclerListItemView *listItemView); 8 | 9 | @implementation RNRecyclerListItemViewManager 10 | 11 | RCT_EXPORT_MODULE() 12 | 13 | - (dispatch_queue_t)methodQueue 14 | { 15 | return dispatch_get_main_queue(); 16 | } 17 | 18 | - (UIView *)view 19 | { 20 | return [[RecyclerListItemView alloc] init]; 21 | } 22 | 23 | - (void)executeBlock:(ListItemViewBlock)block onNode:(NSNumber *)node { 24 | 25 | [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { 26 | id view = viewRegistry[node]; 27 | if (![view isKindOfClass:[RecyclerListItemView class]]) { 28 | RCTLogError(@"Invalid view returned from registry, expecting RNReactNativeTableView, got: %@", view); 29 | return; 30 | } 31 | RecyclerListItemView *item = view; 32 | if (block) { 33 | block(item); 34 | } 35 | }]; 36 | } 37 | 38 | RCT_CUSTOM_VIEW_PROPERTY(itemIndex, NSInteger, RecyclerListItemView) 39 | { 40 | view.itemIndex = [RCTConvert NSInteger:json]; 41 | } 42 | 43 | RCT_CUSTOM_VIEW_PROPERTY(itemKey, NSInteger, RecyclerListItemView) 44 | { 45 | view.itemKey = [RCTConvert NSInteger:json]; 46 | } 47 | 48 | 49 | @end 50 | 51 | -------------------------------------------------------------------------------- /example/ios/example/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:@"example" 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 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | buildscript { 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.1.4' 10 | } 11 | } 12 | 13 | apply plugin: 'com.android.library' 14 | 15 | // import the `readReactNativeVersion()` function 16 | apply from: 'https://gist.githubusercontent.com/hypest/742448b9588b3a0aa580a5e80ae95bdf/raw/8eb62d40ee7a5104d2fcaeff21ce6f29bd93b054/readReactNativeVersion.gradle' 17 | 18 | android { 19 | compileSdkVersion 28 20 | 21 | defaultConfig { 22 | minSdkVersion 16 23 | targetSdkVersion 26 24 | versionCode 1 25 | versionName "1.0" 26 | } 27 | lintOptions { 28 | abortOnError false 29 | } 30 | } 31 | 32 | repositories { 33 | google() 34 | jcenter() 35 | 36 | if (project == rootProject) { 37 | // if we are the root project, use a remote RN maven repo so jitpack can build this lib without local RN setup 38 | def reactNativeRepo = 'https://dl.bintray.com/wordpress-mobile/react-native-mirror/' 39 | println "Will use the RN maven repo at ${reactNativeRepo}" 40 | maven { url reactNativeRepo } 41 | } 42 | } 43 | 44 | dependencies { 45 | if (project == rootProject) { 46 | def rnVersion = readReactNativeVersion('../package.json', 'peerDependencies') 47 | implementation "com.facebook.react:react-native:${rnVersion}" 48 | } else { 49 | implementation 'com.facebook.react:react-native:+' 50 | } 51 | implementation 'com.android.support:recyclerview-v7:27.1.1' 52 | } 53 | -------------------------------------------------------------------------------- /example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | 16 | ; Ignore polyfills 17 | .*/Libraries/polyfills/.* 18 | 19 | ; Ignore metro 20 | .*/node_modules/metro/.* 21 | 22 | [include] 23 | 24 | [libs] 25 | node_modules/react-native/Libraries/react-native/react-native-interface.js 26 | node_modules/react-native/flow/ 27 | node_modules/react-native/flow-github/ 28 | 29 | [options] 30 | emoji=true 31 | 32 | 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.65.0 55 | -------------------------------------------------------------------------------- /example/ios/example-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSExceptionDomains 45 | 46 | localhost 47 | 48 | NSExceptionAllowsInsecureHTTPLoads 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /example/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.example", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.example", 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 | -------------------------------------------------------------------------------- /example/ios/example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | 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 | NSAllowsArbitraryLoads 46 | 47 | NSExceptionDomains 48 | 49 | localhost 50 | 51 | NSExceptionAllowsInsecureHTTPLoads 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/godness84/RNRecyclerViewList/NotAnimatedItemAnimator.java: -------------------------------------------------------------------------------- 1 | package com.github.godness84.RNRecyclerViewList; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.support.v7.widget.SimpleItemAnimator; 5 | 6 | /** 7 | * Implementation of {@link SimpleItemAnimator} that disables all default animations. 8 | */ 9 | /*package*/ class NotAnimatedItemAnimator extends SimpleItemAnimator { 10 | 11 | @Override 12 | public void runPendingAnimations() { 13 | // nothing 14 | } 15 | 16 | @Override 17 | public boolean animateRemove(RecyclerView.ViewHolder holder) { 18 | dispatchRemoveStarting(holder); 19 | dispatchRemoveFinished(holder); 20 | return true; 21 | } 22 | 23 | @Override 24 | public boolean animateAdd(RecyclerView.ViewHolder holder) { 25 | dispatchAddStarting(holder); 26 | dispatchAddFinished(holder); 27 | return true; 28 | } 29 | 30 | @Override 31 | public boolean animateMove( 32 | RecyclerView.ViewHolder holder, 33 | int fromX, 34 | int fromY, 35 | int toX, 36 | int toY) { 37 | dispatchMoveStarting(holder); 38 | dispatchMoveFinished(holder); 39 | return true; 40 | } 41 | 42 | @Override 43 | public boolean animateChange( 44 | RecyclerView.ViewHolder oldHolder, 45 | RecyclerView.ViewHolder newHolder, 46 | int fromLeft, 47 | int fromTop, 48 | int toLeft, 49 | int toTop) { 50 | dispatchChangeStarting(oldHolder, true); 51 | dispatchChangeFinished(oldHolder, true); 52 | 53 | // TODO: capire perchè a volte diventa NULL 54 | if (newHolder != null) { 55 | dispatchChangeStarting(newHolder, false); 56 | dispatchChangeFinished(newHolder, false); 57 | } 58 | return true; 59 | } 60 | 61 | @Override 62 | public void endAnimation(RecyclerView.ViewHolder item) { 63 | } 64 | 65 | @Override 66 | public void endAnimations() { 67 | } 68 | 69 | @Override 70 | public boolean isRunning() { 71 | return false; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /example/ios/exampleTests/exampleTests.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 | #import 12 | 13 | #import 14 | #import 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface exampleTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation exampleTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 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 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/DataSource.js: -------------------------------------------------------------------------------- 1 | export default class DataSource { 2 | constructor(data, keyExtractor) { 3 | this._data = data || []; 4 | this._keyExtractor = keyExtractor; 5 | this._listeners = []; 6 | 7 | if (!keyExtractor) { 8 | console.warn( 9 | 'RecyclerViewList/DataSource: missing keyExtractor, it\'s strongly recommended to specify a keyExtractor function ' + 10 | 'in order to use all the features correctly.' 11 | ); 12 | 13 | this._keyExtractor = (item, index) => { 14 | return JSON.stringify(item) + '_' + index; 15 | } 16 | } 17 | } 18 | 19 | push(item) { 20 | this._data.push(item); 21 | this._listeners.forEach((listener) => { 22 | listener && listener.onPush && listener.onPush(item); 23 | }); 24 | } 25 | 26 | unshift(item) { 27 | this._data.unshift(item); 28 | this._listeners.forEach((listener) => { 29 | listener && listener.onUnshift && listener.onUnshift(item); 30 | }); 31 | } 32 | 33 | splice(start, deleteCount, ...items) { 34 | this._data.splice(start, deleteCount, ...items); 35 | this._listeners.forEach((listener) => { 36 | listener && listener.onSplice && listener.onSplice(start, deleteCount, ...items); 37 | }); 38 | } 39 | 40 | size() { 41 | return this._data.length; 42 | } 43 | 44 | moveUp(index) { 45 | if (index <= 0) { 46 | return; 47 | } 48 | const item = this._data[index]; 49 | this._data[index] = this._data[index - 1]; 50 | this._data[index - 1] = item; 51 | this._listeners.forEach((listener) => { 52 | listener && listener.onMoveUp && listener.onMoveUp(index); 53 | }); 54 | } 55 | 56 | moveDown(index) { 57 | if (index >= this._data.length - 1) { 58 | return; 59 | } 60 | const item = this._data[index]; 61 | this._data[index] = this._data[index + 1]; 62 | this._data[index + 1] = item; 63 | this._listeners.forEach((listener) => { 64 | listener && listener.onMoveDown && listener.onMoveDown(index); 65 | }); 66 | } 67 | 68 | set(index, item) { 69 | this._data[index] = item; 70 | this._listeners.forEach((listener) => { 71 | listener && listener.onSplice && listener.onSet(index, item); 72 | }); 73 | } 74 | 75 | setDirty() { 76 | this._listeners.forEach((listener) => { 77 | listener && listener.onSetDirty && listener.onSetDirty(); 78 | }); 79 | } 80 | 81 | get(index) { 82 | return this._data[index]; 83 | } 84 | 85 | getKey(item, index) { 86 | return this._keyExtractor(item, index); 87 | } 88 | 89 | _addListener(listener) { 90 | this._listeners.push(listener); 91 | } 92 | 93 | _removeListener(listener) { 94 | var index = this._listeners.indexOf(listener); 95 | if (index > -1) { 96 | this._listeners.splice(index, 1); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # TextLayoutBuilder uses a non-public Android constructor within StaticLayout. 54 | # See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details. 55 | -dontwarn android.text.StaticLayout 56 | 57 | # okhttp 58 | 59 | -keepattributes Signature 60 | -keepattributes *Annotation* 61 | -keep class okhttp3.** { *; } 62 | -keep interface okhttp3.** { *; } 63 | -dontwarn okhttp3.** 64 | 65 | # okio 66 | 67 | -keep class sun.misc.Unsafe { *; } 68 | -dontwarn java.nio.file.* 69 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 70 | -dontwarn okio.** 71 | -------------------------------------------------------------------------------- /ios/RNRecyclerListViewManager.m: -------------------------------------------------------------------------------- 1 | 2 | #import "RNRecyclerListViewManager.h" 3 | #import 4 | #import 5 | #import 6 | 7 | typedef void (^ListViewBlock)(RecyclerListView *listView); 8 | 9 | @implementation RNRecyclerListViewManager 10 | 11 | - (dispatch_queue_t)methodQueue 12 | { 13 | return dispatch_get_main_queue(); 14 | } 15 | 16 | - (UIView *)view 17 | { 18 | return [[RecyclerListView alloc] init]; 19 | } 20 | 21 | - (void)executeBlock:(ListViewBlock)block onNode:(NSNumber *)node { 22 | 23 | [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { 24 | id view = viewRegistry[node]; 25 | if (![view isKindOfClass:[RecyclerListView class]]) { 26 | RCTLogError(@"Invalid view returned from registry, expecting RNReactNativeTableView, got: %@", view); 27 | return; 28 | } 29 | RecyclerListView *listView = view; 30 | if (block) { 31 | block(listView); 32 | } 33 | }]; 34 | } 35 | 36 | RCT_EXPORT_METHOD(scrollToIndex:(nonnull NSNumber *)node index:(NSInteger)index animated:(BOOL)animated) 37 | { 38 | RCTLogInfo(@"Scroll to index %i at %i", (int)index, animated); 39 | [self executeBlock:^(RecyclerListView *listView) { 40 | [listView scrollTo:index animated:animated]; 41 | } onNode:node]; 42 | } 43 | 44 | RCT_EXPORT_METHOD(notifyDataSetChanged:(nonnull NSNumber *)node size:(NSInteger)size ) 45 | { 46 | RCTLogInfo(@"Dataset changed size to %i", (int)size); 47 | [self executeBlock:^(RecyclerListView *listView) { 48 | listView.dataSize = size; 49 | } onNode:node]; 50 | } 51 | 52 | RCT_EXPORT_METHOD(notifyItemMoved:(nonnull NSNumber *)node indexFromPosition:(NSInteger)from toPosition:(NSInteger)to) 53 | { 54 | RCTLogInfo(@"Dataset moved item from %i to %i", (int)from, (int)to); 55 | [self executeBlock:^(RecyclerListView *listView) { 56 | [listView moveCellFrom:from to:to]; 57 | } onNode:node]; 58 | } 59 | 60 | RCT_EXPORT_METHOD(notifyItemRangeInserted:(nonnull NSNumber *)node position:(NSInteger)position count:(NSInteger)count ) 61 | { 62 | RCTLogInfo(@"Dataset inserted at position %i, %i items", (int)position, (int)count); 63 | [self executeBlock:^(RecyclerListView *listView) { 64 | [listView insertItemsAt:position amount:count]; 65 | } onNode:node]; 66 | } 67 | 68 | RCT_EXPORT_METHOD(notifyItemRangeRemoved:(nonnull NSNumber *)node position:(NSInteger)position count:(NSInteger)count ) 69 | { 70 | RCTLogInfo(@"Dataset removed at position %i, %i items", (int)position, (int)count); 71 | [self executeBlock:^(RecyclerListView *listView) { 72 | [listView removeItemsAt:position amount:count]; 73 | } onNode:node]; 74 | } 75 | 76 | 77 | RCT_CUSTOM_VIEW_PROPERTY(itemCount, NSInteger, RecyclerListView) 78 | { 79 | view.dataSize = [RCTConvert NSInteger:json]; 80 | } 81 | 82 | RCT_EXPORT_VIEW_PROPERTY(onVisibleItemsChange, RCTBubblingEventBlock) 83 | 84 | RCT_EXPORT_MODULE() 85 | 86 | @end 87 | 88 | -------------------------------------------------------------------------------- /example/ios/example/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // whether to bundle JS and assets in debug mode 22 | * bundleInDebug: false, 23 | * 24 | * // whether to bundle JS and assets in release mode 25 | * bundleInRelease: true, 26 | * 27 | * // whether to bundle JS and assets in another build variant (if configured). 28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 29 | * // The configuration property can be in the following formats 30 | * // 'bundleIn${productFlavor}${buildType}' 31 | * // 'bundleIn${buildType}' 32 | * // bundleInFreeDebug: true, 33 | * // bundleInPaidRelease: true, 34 | * // bundleInBeta: true, 35 | * 36 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 37 | * // for example: to disable dev mode in the staging build type (if configured) 38 | * devDisabledInStaging: true, 39 | * // The configuration property can be in the following formats 40 | * // 'devDisabledIn${productFlavor}${buildType}' 41 | * // 'devDisabledIn${buildType}' 42 | * 43 | * // the root of your project, i.e. where "package.json" lives 44 | * root: "../../", 45 | * 46 | * // where to put the JS bundle asset in debug mode 47 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 48 | * 49 | * // where to put the JS bundle asset in release mode 50 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 51 | * 52 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 53 | * // require('./image.png')), in debug mode 54 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 55 | * 56 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 57 | * // require('./image.png')), in release mode 58 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 59 | * 60 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 61 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 62 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 63 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 64 | * // for example, you might want to remove it from here. 65 | * inputExcludes: ["android/**", "ios/**"], 66 | * 67 | * // override which node gets called and with what additional arguments 68 | * nodeExecutableAndArgs: ["node"], 69 | * 70 | * // supply additional arguments to the packager 71 | * extraPackagerArgs: [] 72 | * ] 73 | */ 74 | 75 | project.ext.react = [ 76 | entryFile: "index.js" 77 | ] 78 | 79 | apply from: "../../node_modules/react-native/react.gradle" 80 | 81 | /** 82 | * Set this to true to create two separate APKs instead of one: 83 | * - An APK that only works on ARM devices 84 | * - An APK that only works on x86 devices 85 | * The advantage is the size of the APK is reduced by about 4MB. 86 | * Upload all the APKs to the Play Store and people will download 87 | * the correct one based on the CPU architecture of their device. 88 | */ 89 | def enableSeparateBuildPerCPUArchitecture = false 90 | 91 | /** 92 | * Run Proguard to shrink the Java bytecode in release builds. 93 | */ 94 | def enableProguardInReleaseBuilds = false 95 | 96 | android { 97 | compileSdkVersion 28 98 | 99 | defaultConfig { 100 | applicationId "com.example" 101 | minSdkVersion 16 102 | targetSdkVersion 26 103 | versionCode 1 104 | versionName "1.0" 105 | ndk { 106 | abiFilters "armeabi-v7a", "x86" 107 | } 108 | } 109 | splits { 110 | abi { 111 | reset() 112 | enable enableSeparateBuildPerCPUArchitecture 113 | universalApk false // If true, also generate a universal APK 114 | include "armeabi-v7a", "x86" 115 | } 116 | } 117 | buildTypes { 118 | release { 119 | minifyEnabled enableProguardInReleaseBuilds 120 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 121 | } 122 | } 123 | // applicationVariants are e.g. debug, release 124 | applicationVariants.all { variant -> 125 | variant.outputs.each { output -> 126 | // For each separate APK per architecture, set a unique version code as described here: 127 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 128 | def versionCodes = ["armeabi-v7a":1, "x86":2] 129 | def abi = output.getFilter(OutputFile.ABI) 130 | if (abi != null) { // null for the universal-debug, universal-release variants 131 | output.versionCodeOverride = 132 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 133 | } 134 | } 135 | } 136 | } 137 | 138 | dependencies { 139 | implementation project(':react-native-recyclerview-list') 140 | implementation fileTree(dir: "libs", include: ["*.jar"]) 141 | implementation "com.android.support:appcompat-v7:27.1.1" 142 | implementation "com.facebook.react:react-native:+" // From node_modules 143 | } 144 | 145 | // Run this once to be able to run the application with BUCK 146 | // puts all compile dependencies into folder libs for BUCK to use 147 | task copyDownloadableDepsToLibs(type: Copy) { 148 | from configurations.compile 149 | into 'libs' 150 | } 151 | -------------------------------------------------------------------------------- /ios/RNRecyclerListView.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | @objc class RecyclerListView: UIView { 5 | 6 | public var onVisibleItemsChange: RCTBubblingEventBlock? 7 | public var firstVisibleIndex = -1 8 | public var lastVisibleIndex = -1 9 | 10 | fileprivate var data = [RecyclerListItemView]() 11 | 12 | let cellIdentifier = "RecycleCell" 13 | 14 | lazy var tableView: UITableView = { 15 | return UITableView(frame: self.frame, style: .plain) 16 | }() 17 | 18 | @objc override init(frame: CGRect) { 19 | super.init(frame: frame) 20 | commonInit() 21 | } 22 | 23 | @objc required init?(coder aDecoder: NSCoder) { 24 | super.init(coder: aDecoder) 25 | commonInit() 26 | } 27 | 28 | func commonInit() { 29 | tableView.dataSource = self 30 | tableView.delegate = self 31 | tableView.register(WrapperCell.self, forCellReuseIdentifier: cellIdentifier) 32 | tableView.rowHeight = UITableViewAutomaticDimension 33 | tableView.estimatedRowHeight = 80 34 | addSubview(tableView) 35 | } 36 | 37 | override func layoutSubviews() { 38 | super.layoutSubviews() 39 | tableView.frame = bounds 40 | } 41 | 42 | override func addSubview(_ view: UIView) { 43 | // if we are adding another thing let's ignore it 44 | guard let listItem = view as? RecyclerListItemView else { 45 | super.addSubview(view) 46 | return 47 | } 48 | // only add it if we don't have an item already 49 | if let item = data.first(where: { $0.itemKey == listItem.itemKey}) { 50 | print("Found cell with index: \(listItem.itemIndex) and key: \(listItem.itemKey)") 51 | if let cell = tableView.cellForRow(at: IndexPath(row: item.itemIndex, section: 0)) as? WrapperCell { 52 | cell.listItemView = listItem 53 | data[item.itemIndex] = listItem 54 | print("Update cell with index: \(listItem.itemIndex)") 55 | } 56 | } else { 57 | print("Add cell with index: \(listItem.itemIndex) and key: \(listItem.itemKey)") 58 | 59 | data.insert(listItem, at: listItem.itemIndex) 60 | let newIndexPaths = [IndexPath(row:listItem.itemIndex, section: 0)] 61 | tableView.insertRows(at: newIndexPaths , with: .automatic) 62 | let deadlineTime: DispatchTime = DispatchTime.now() + .milliseconds(250) 63 | DispatchQueue.main.asyncAfter(deadline: deadlineTime) { 64 | self.tableView.beginUpdates() 65 | self.tableView.endUpdates() 66 | } 67 | } 68 | } 69 | } 70 | 71 | extension RecyclerListView: UITableViewDataSource { 72 | 73 | func numberOfSections(in tableView: UITableView) -> Int { 74 | return 1 75 | } 76 | 77 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 78 | return data.count 79 | } 80 | 81 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 82 | let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! WrapperCell 83 | let view = data[indexPath.row] 84 | cell.listItemView = view 85 | return cell 86 | } 87 | } 88 | 89 | extension RecyclerListView: UITableViewDelegate { 90 | 91 | func scrollViewDidScroll(_ scrollView: UIScrollView) { 92 | 93 | guard let visibleRows = tableView.indexPathsForVisibleRows, 94 | let firstIndex = visibleRows.first?.row, 95 | let lastIndex = visibleRows.last?.row else { 96 | return 97 | } 98 | if firstVisibleIndex != firstIndex || lastVisibleIndex != lastIndex { 99 | firstVisibleIndex = firstIndex 100 | lastVisibleIndex = lastIndex 101 | onVisibleItemsChange?(["firstIndex": firstIndex, "lastIndex": lastIndex]) 102 | } 103 | } 104 | } 105 | 106 | extension RecyclerListView { 107 | 108 | @objc var dataSize: Int { 109 | set { 110 | data.removeAll() 111 | tableView.reloadData() 112 | } 113 | 114 | get { 115 | return data.count 116 | } 117 | } 118 | 119 | @objc func scroll(to: Int, animated: Bool) { 120 | let position = max(min(to, data.count-1),0) 121 | tableView.scrollToRow(at: IndexPath(row: position, section: 0), at: .top, animated: animated) 122 | } 123 | 124 | @objc func moveCell(from: Int, to: Int) { 125 | data.swapAt(from, to) 126 | tableView.moveRow(at: IndexPath(row: from, section: 0), to: IndexPath(row: to, section: 0)) 127 | } 128 | 129 | func makeIndexPaths(from: Int, count: Int) -> [IndexPath] { 130 | var indices = [IndexPath]() 131 | for i in 0.. { 30 | 31 | public static final String REACT_CLASS = "AndroidRecyclerViewBackedScrollView"; 32 | public static final int COMMAND_NOTIFY_ITEM_RANGE_INSERTED = 1; 33 | public static final int COMMAND_NOTIFY_ITEM_RANGE_REMOVED = 2; 34 | public static final int COMMAND_NOTIFY_DATASET_CHANGED = 3; 35 | public static final int COMMAND_SCROLL_TO_INDEX = 4; 36 | public static final int COMMAND_NOTIFY_ITEM_MOVED = 5; 37 | private static final String TAG = "RecyclerViewManager"; 38 | 39 | @Override 40 | public String getName() { 41 | return REACT_CLASS; 42 | } 43 | 44 | @Override 45 | protected RecyclerViewBackedScrollView createViewInstance(ThemedReactContext reactContext) { 46 | return new RecyclerViewBackedScrollView(reactContext); 47 | } 48 | 49 | @Override 50 | public void addView(RecyclerViewBackedScrollView parent, View child, int index) { 51 | Assertions.assertCondition(child instanceof RecyclerViewItemView, 52 | "Views attached to RecyclerViewBackedScrollView must be RecyclerViewItemView views."); 53 | RecyclerViewItemView item = (RecyclerViewItemView) child; 54 | parent.addViewToAdapter(item, index); 55 | } 56 | 57 | @Override 58 | public int getChildCount(RecyclerViewBackedScrollView parent) { 59 | return parent.getChildCountFromAdapter(); 60 | } 61 | 62 | @Override 63 | public View getChildAt(RecyclerViewBackedScrollView parent, int index) { 64 | return parent.getChildAtFromAdapter(index); 65 | } 66 | 67 | @Override 68 | public void removeViewAt(RecyclerViewBackedScrollView parent, int index) { 69 | parent.removeViewFromAdapter(index); 70 | } 71 | 72 | @ReactProp(name = "itemCount") 73 | public void setItemCount(RecyclerViewBackedScrollView parent, int itemCount) { 74 | parent.setItemCount(itemCount); 75 | parent.getAdapter().notifyDataSetChanged(); 76 | } 77 | 78 | @ReactProp(name = "itemAnimatorEnabled", defaultBoolean = true) 79 | public void setItemAnimatorEnabled(RecyclerViewBackedScrollView parent, boolean enabled) { 80 | parent.setItemAnimatorEnabled(enabled); 81 | } 82 | 83 | @Override 84 | public Map getCommandsMap() { 85 | return MapBuilder.of("notifyItemRangeInserted", COMMAND_NOTIFY_ITEM_RANGE_INSERTED, "notifyItemRangeRemoved", 86 | COMMAND_NOTIFY_ITEM_RANGE_REMOVED, "notifyItemMoved", COMMAND_NOTIFY_ITEM_MOVED, "notifyDataSetChanged", 87 | COMMAND_NOTIFY_DATASET_CHANGED, "scrollToIndex", COMMAND_SCROLL_TO_INDEX); 88 | } 89 | 90 | @Override 91 | public void receiveCommand(final RecyclerViewBackedScrollView parent, int commandType, 92 | @Nullable ReadableArray args) { 93 | Assertions.assertNotNull(parent); 94 | Assertions.assertNotNull(args); 95 | switch (commandType) { 96 | case COMMAND_NOTIFY_ITEM_RANGE_INSERTED: { 97 | final int position = args.getInt(0); 98 | final int count = args.getInt(1); 99 | //Log.d(TAG, String.format("notify item range inserted: position %d, count %d", position, count)); 100 | 101 | RecyclerViewBackedScrollView.ReactListAdapter adapter = (RecyclerViewBackedScrollView.ReactListAdapter) parent 102 | .getAdapter(); 103 | adapter.setItemCount(adapter.getItemCount() + count); 104 | adapter.notifyItemRangeInserted(position, count); 105 | return; 106 | } 107 | 108 | case COMMAND_NOTIFY_ITEM_RANGE_REMOVED: { 109 | final int position = args.getInt(0); 110 | final int count = args.getInt(1); 111 | //Log.d(TAG, String.format("notify item range removed: position %d, count %d", position, count)); 112 | 113 | RecyclerViewBackedScrollView.ReactListAdapter adapter = (RecyclerViewBackedScrollView.ReactListAdapter) parent 114 | .getAdapter(); 115 | adapter.setItemCount(adapter.getItemCount() - count); 116 | adapter.notifyItemRangeRemoved(position, count); 117 | return; 118 | } 119 | 120 | case COMMAND_NOTIFY_ITEM_MOVED: { 121 | final int currentPosition = args.getInt(0); 122 | final int nextPosition = args.getInt(1); 123 | RecyclerViewBackedScrollView.ReactListAdapter adapter = (RecyclerViewBackedScrollView.ReactListAdapter) parent 124 | .getAdapter(); 125 | adapter.notifyItemMoved(currentPosition, nextPosition); 126 | return; 127 | } 128 | 129 | case COMMAND_NOTIFY_DATASET_CHANGED: { 130 | final int itemCount = args.getInt(0); 131 | RecyclerViewBackedScrollView.ReactListAdapter adapter = (RecyclerViewBackedScrollView.ReactListAdapter) parent 132 | .getAdapter(); 133 | adapter.setItemCount(itemCount); 134 | parent.getAdapter().notifyDataSetChanged(); 135 | return; 136 | } 137 | 138 | case COMMAND_SCROLL_TO_INDEX: { 139 | boolean animated = args.getBoolean(0); 140 | int index = args.getInt(1); 141 | RecyclerViewBackedScrollView.ScrollOptions options = new RecyclerViewBackedScrollView.ScrollOptions(); 142 | options.millisecondsPerInch = args.isNull(2) ? null : (float) args.getDouble(2); 143 | options.viewPosition = args.isNull(3) ? null : (float) args.getDouble(3); 144 | options.viewOffset = args.isNull(4) ? null : (float) args.getDouble(4); 145 | 146 | if (animated) { 147 | parent.smoothScrollToPosition(index, options); 148 | } else { 149 | parent.scrollToPosition(index, options); 150 | } 151 | return; 152 | } 153 | 154 | default: 155 | throw new IllegalArgumentException( 156 | String.format("Unsupported command %d received by %s.", commandType, getClass().getSimpleName())); 157 | } 158 | } 159 | 160 | @Override 161 | public @Nullable Map getExportedCustomDirectEventTypeConstants() { 162 | return MapBuilder.builder() 163 | .put(ScrollEventType.getJSEventName(ScrollEventType.SCROLL), MapBuilder.of("registrationName", "onScroll")) 164 | .put(ContentSizeChangeEvent.EVENT_NAME, MapBuilder.of("registrationName", "onContentSizeChange")) 165 | .put(VisibleItemsChangeEvent.EVENT_NAME, MapBuilder.of("registrationName", "onVisibleItemsChange")) 166 | .build(); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | Platform, 10 | AppRegistry, 11 | StyleSheet, 12 | Text, 13 | View, 14 | Image, 15 | Button, 16 | TouchableHighlight, 17 | ToastAndroid, 18 | SafeAreaView 19 | } from 'react-native'; 20 | 21 | import RecyclerViewList, { DataSource } from 'react-native-recyclerview-list'; 22 | 23 | var _gCounter = 1; 24 | function newItem() { 25 | return { 26 | id: _gCounter++, 27 | counter: 0 28 | }; 29 | } 30 | 31 | export default class example extends Component { 32 | constructor(props) { 33 | super(props); 34 | var data = Array(10).fill().map((e, i) => newItem()); 35 | 36 | this.state = { 37 | dataSource: new DataSource(data, (item, index) => item.id) 38 | }; 39 | } 40 | 41 | renderMainContent() { 42 | const { dataSource } = this.state; 43 | 44 | return ( 45 | 46 | {this.renderTopControlPanel()} 47 | this._recycler = component} 49 | style={{ flex: 1 }} 50 | dataSource={dataSource} 51 | renderItem={this.renderItem} 52 | windowSize={20} 53 | initialScrollIndex={0} 54 | ListHeaderComponent={( 55 | 56 | )} 57 | ListFooterComponent={( 58 | 59 | )} 60 | ListEmptyComponent={( 61 | 62 | No results. 63 | 64 | )} 65 | ItemSeparatorComponent={( 66 | 67 | )} /> 68 | {this.renderBottomControlPanel()} 69 | 70 | ); 71 | } 72 | 73 | render() { 74 | if (Platform.OS === 'ios') { 75 | return ( 76 | 77 | {this.renderMainContent()} 78 | 79 | ); 80 | } else { 81 | return ( 82 | 83 | {this.renderMainContent()} 84 | 85 | ); 86 | } 87 | } 88 | 89 | renderItem = ({ item, index }) => { 90 | return ( 91 | this.remove(index)} 95 | onAddAbove={() => this.addAbove(index)} 96 | onMoveUp={() => this.moveUp(index)} 97 | onMoveDown={() => this.moveDown(index)} 98 | onAddBelow={() => this.addBelow(index)} 99 | onIncrementCounter={() => this.incrementCounter(index)} /> 100 | ); 101 | } 102 | 103 | renderTopControlPanel() { 104 | return ( 105 | 106 |