react-native-list 1.0.1 → 2.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/README.md +186 -32
  2. package/ReactNativeList.podspec +39 -0
  3. package/android/CMakeLists.txt +48 -0
  4. package/android/build.gradle +151 -0
  5. package/android/fix-prefab.gradle +51 -0
  6. package/android/gradle.properties +5 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/cpp/JHybridUiListModule.cpp +192 -0
  9. package/android/src/main/cpp/JHybridUiListModule.h +50 -0
  10. package/android/src/main/cpp/cpp-adapter.cpp +12 -0
  11. package/android/src/main/java/com/hannojg/reactnativelist/ReactNativeListPackage.kt +27 -0
  12. package/android/src/main/java/com/margelo/nitro/reactnativelist/HybridNativeListDataSource.kt +146 -0
  13. package/android/src/main/java/com/margelo/nitro/reactnativelist/HybridNativeListLayout.kt +86 -0
  14. package/android/src/main/java/com/margelo/nitro/reactnativelist/HybridUiListModule.kt +116 -0
  15. package/android/src/main/java/com/margelo/nitro/reactnativelist/HybridUiListView.kt +410 -0
  16. package/android/src/main/java/com/margelo/nitro/reactnativelist/HybridViewHolder.kt +9 -0
  17. package/android/src/main/java/com/margelo/nitro/reactnativelist/NativeListAdapter.kt +217 -0
  18. package/ios/DataSource/HybridNativeListDataSource.swift +213 -0
  19. package/ios/HybridObjects/HybridUiListModule.swift +49 -0
  20. package/ios/HybridObjects/HybridViewHolder.swift +16 -0
  21. package/ios/Layout/HybridNativeListLayout.swift +128 -0
  22. package/ios/Utils/ErrorUtils.h +26 -0
  23. package/ios/Utils/HybridIOSWorkletsModuleProxyHolder.swift +10 -0
  24. package/ios/Utils/SurfaceHelper.h +20 -0
  25. package/ios/Utils/SurfaceHelper.mm +144 -0
  26. package/ios/Utils/SurfacePresenterRegistry.h +17 -0
  27. package/ios/Utils/SurfacePresenterRegistry.m +31 -0
  28. package/ios/Utils/TurboModuleInstaller.h +18 -0
  29. package/ios/Utils/TurboModuleInstaller.mm +267 -0
  30. package/ios/Views/HostCell.swift +216 -0
  31. package/ios/Views/HybridUiListView.swift +695 -0
  32. package/lib/ReactFabricMirror.d.ts +4 -0
  33. package/lib/ReactFabricMirror.js +515 -0
  34. package/lib/UiListModule.d.ts +2 -0
  35. package/lib/UiListModule.js +2 -0
  36. package/lib/index.d.ts +8 -0
  37. package/lib/index.js +21 -0
  38. package/lib/privateGlobals.d.ts +14 -0
  39. package/lib/privateGlobals.js +2 -0
  40. package/lib/renderer/RenderHelper.d.ts +2 -0
  41. package/lib/renderer/RenderHelper.js +11 -0
  42. package/lib/renderer/UiManagerHelper.d.ts +2 -0
  43. package/lib/renderer/UiManagerHelper.js +2 -0
  44. package/lib/renderer/fabric/RenderHelper.d.ts +2 -0
  45. package/lib/renderer/fabric/RenderHelper.js +11 -0
  46. package/lib/renderer/fabric/UiManagerHelper.d.ts +2 -0
  47. package/lib/renderer/fabric/UiManagerHelper.js +2 -0
  48. package/lib/renderer/react/ReactFabricMirror.d.ts +4 -0
  49. package/lib/renderer/react/ReactFabricMirror.js +515 -0
  50. package/lib/renderer/react/ReactFabricRenderer.d.ts +3 -0
  51. package/lib/renderer/react/ReactFabricRenderer.js +9 -0
  52. package/lib/specs/IOSWorkletsModuleProxyHolder.nitro.d.ts +6 -0
  53. package/lib/specs/IOSWorkletsModuleProxyHolder.nitro.js +1 -0
  54. package/lib/specs/UIListModule.nitro.d.ts +9 -0
  55. package/lib/specs/UIListModule.nitro.js +1 -0
  56. package/lib/specs/UIManagerHelper.nitro.d.ts +13 -0
  57. package/lib/specs/UIManagerHelper.nitro.js +1 -0
  58. package/lib/specs/UiListView.nitro.d.ts +20 -0
  59. package/lib/specs/UiListView.nitro.js +1 -0
  60. package/lib/specs/ViewHolder.nitro.d.ts +6 -0
  61. package/lib/specs/ViewHolder.nitro.js +1 -0
  62. package/lib/views/List.d.ts +35 -0
  63. package/lib/views/List.js +225 -0
  64. package/lib/views/UiListHostComponent.d.ts +2 -0
  65. package/lib/views/UiListHostComponent.js +3 -0
  66. package/metro/RendererProxyThreadSwitch.js +66 -0
  67. package/metro-config.d.ts +1 -0
  68. package/metro-config.js +124 -0
  69. package/nitro.json +47 -0
  70. package/nitrogen/generated/.gitattributes +1 -0
  71. package/nitrogen/generated/android/ReactNativeList+autolinking.cmake +99 -0
  72. package/nitrogen/generated/android/ReactNativeList+autolinking.gradle +27 -0
  73. package/nitrogen/generated/android/ReactNativeListOnLoad.cpp +156 -0
  74. package/nitrogen/generated/android/ReactNativeListOnLoad.hpp +34 -0
  75. package/nitrogen/generated/android/c++/JFunc_bool_NativeListItem_NativeListItem.hpp +83 -0
  76. package/nitrogen/generated/android/c++/JFunc_bool_double_NativeListItem_double.hpp +83 -0
  77. package/nitrogen/generated/android/c++/JFunc_double_std__string.hpp +78 -0
  78. package/nitrogen/generated/android/c++/JHybridIOSWorkletsModuleProxyHolderSpec.cpp +49 -0
  79. package/nitrogen/generated/android/c++/JHybridIOSWorkletsModuleProxyHolderSpec.hpp +63 -0
  80. package/nitrogen/generated/android/c++/JHybridNativeLinearListLayoutSpec.cpp +63 -0
  81. package/nitrogen/generated/android/c++/JHybridNativeLinearListLayoutSpec.hpp +65 -0
  82. package/nitrogen/generated/android/c++/JHybridNativeListDataSourceSpec.cpp +101 -0
  83. package/nitrogen/generated/android/c++/JHybridNativeListDataSourceSpec.hpp +70 -0
  84. package/nitrogen/generated/android/c++/JHybridNativeListLayoutSpec.cpp +49 -0
  85. package/nitrogen/generated/android/c++/JHybridNativeListLayoutSpec.hpp +63 -0
  86. package/nitrogen/generated/android/c++/JHybridUiListModuleSpec.cpp +65 -0
  87. package/nitrogen/generated/android/c++/JHybridUiListModuleSpec.hpp +64 -0
  88. package/nitrogen/generated/android/c++/JHybridUiListViewSpec.cpp +92 -0
  89. package/nitrogen/generated/android/c++/JHybridUiListViewSpec.hpp +67 -0
  90. package/nitrogen/generated/android/c++/JHybridViewHolderSpec.cpp +49 -0
  91. package/nitrogen/generated/android/c++/JHybridViewHolderSpec.hpp +63 -0
  92. package/nitrogen/generated/android/c++/JNativeItemSizeEstimate.hpp +61 -0
  93. package/nitrogen/generated/android/c++/JNativeLinearListLayoutConfig.hpp +81 -0
  94. package/nitrogen/generated/android/c++/JNativeLinearListLayoutIOSConfig.hpp +59 -0
  95. package/nitrogen/generated/android/c++/JNativeListItem.hpp +76 -0
  96. package/nitrogen/generated/android/c++/JVariant_NullType_HybridIOSWorkletsModuleProxyHolderSpec.cpp +26 -0
  97. package/nitrogen/generated/android/c++/JVariant_NullType_HybridIOSWorkletsModuleProxyHolderSpec.hpp +72 -0
  98. package/nitrogen/generated/android/c++/views/JHybridUiListViewStateUpdater.cpp +53 -0
  99. package/nitrogen/generated/android/c++/views/JHybridUiListViewStateUpdater.hpp +49 -0
  100. package/nitrogen/generated/android/c++/views/JHybridViewHolderStateUpdater.cpp +53 -0
  101. package/nitrogen/generated/android/c++/views/JHybridViewHolderStateUpdater.hpp +49 -0
  102. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/Func_bool_NativeListItem_NativeListItem.kt +80 -0
  103. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/Func_bool_double_NativeListItem_double.kt +80 -0
  104. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/Func_double_std__string.kt +80 -0
  105. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridIOSWorkletsModuleProxyHolderSpec.kt +52 -0
  106. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridNativeLinearListLayoutSpec.kt +54 -0
  107. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridNativeListDataSourceSpec.kt +87 -0
  108. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridNativeListLayoutSpec.kt +52 -0
  109. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridUiListModuleSpec.kt +59 -0
  110. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridUiListViewSpec.kt +76 -0
  111. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridViewHolderSpec.kt +53 -0
  112. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/NativeItemSizeEstimate.kt +56 -0
  113. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/NativeLinearListLayoutConfig.kt +76 -0
  114. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/NativeLinearListLayoutIOSConfig.kt +51 -0
  115. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/NativeListItem.kt +71 -0
  116. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/ReactNativeListOnLoad.kt +35 -0
  117. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/Variant_NullType_HybridIOSWorkletsModuleProxyHolderSpec.kt +62 -0
  118. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/views/HybridUiListViewManager.kt +80 -0
  119. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/views/HybridUiListViewStateUpdater.kt +23 -0
  120. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/views/HybridViewHolderManager.kt +80 -0
  121. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/views/HybridViewHolderStateUpdater.kt +23 -0
  122. package/nitrogen/generated/ios/ReactNativeList+autolinking.rb +62 -0
  123. package/nitrogen/generated/ios/ReactNativeList-Swift-Cxx-Bridge.cpp +162 -0
  124. package/nitrogen/generated/ios/ReactNativeList-Swift-Cxx-Bridge.hpp +368 -0
  125. package/nitrogen/generated/ios/ReactNativeList-Swift-Cxx-Umbrella.hpp +92 -0
  126. package/nitrogen/generated/ios/ReactNativeListAutolinking.mm +83 -0
  127. package/nitrogen/generated/ios/ReactNativeListAutolinking.swift +86 -0
  128. package/nitrogen/generated/ios/c++/HybridIOSWorkletsModuleProxyHolderSpecSwift.cpp +11 -0
  129. package/nitrogen/generated/ios/c++/HybridIOSWorkletsModuleProxyHolderSpecSwift.hpp +75 -0
  130. package/nitrogen/generated/ios/c++/HybridNativeLinearListLayoutSpecSwift.cpp +11 -0
  131. package/nitrogen/generated/ios/c++/HybridNativeLinearListLayoutSpecSwift.hpp +92 -0
  132. package/nitrogen/generated/ios/c++/HybridNativeListDataSourceSpecSwift.cpp +11 -0
  133. package/nitrogen/generated/ios/c++/HybridNativeListDataSourceSpecSwift.hpp +132 -0
  134. package/nitrogen/generated/ios/c++/HybridNativeListLayoutSpecSwift.cpp +11 -0
  135. package/nitrogen/generated/ios/c++/HybridNativeListLayoutSpecSwift.hpp +75 -0
  136. package/nitrogen/generated/ios/c++/HybridUiListModuleSpecSwift.cpp +11 -0
  137. package/nitrogen/generated/ios/c++/HybridUiListModuleSpecSwift.hpp +93 -0
  138. package/nitrogen/generated/ios/c++/HybridUiListViewSpecSwift.cpp +11 -0
  139. package/nitrogen/generated/ios/c++/HybridUiListViewSpecSwift.hpp +121 -0
  140. package/nitrogen/generated/ios/c++/HybridViewHolderSpecSwift.cpp +11 -0
  141. package/nitrogen/generated/ios/c++/HybridViewHolderSpecSwift.hpp +75 -0
  142. package/nitrogen/generated/ios/c++/views/HybridUiListViewComponent.mm +118 -0
  143. package/nitrogen/generated/ios/c++/views/HybridViewHolderComponent.mm +118 -0
  144. package/nitrogen/generated/ios/swift/Func_bool_NativeListItem_NativeListItem.swift +47 -0
  145. package/nitrogen/generated/ios/swift/Func_bool_double_NativeListItem_double.swift +47 -0
  146. package/nitrogen/generated/ios/swift/Func_double_std__string.swift +47 -0
  147. package/nitrogen/generated/ios/swift/HybridIOSWorkletsModuleProxyHolderSpec.swift +55 -0
  148. package/nitrogen/generated/ios/swift/HybridIOSWorkletsModuleProxyHolderSpec_cxx.swift +128 -0
  149. package/nitrogen/generated/ios/swift/HybridNativeLinearListLayoutSpec.swift +55 -0
  150. package/nitrogen/generated/ios/swift/HybridNativeLinearListLayoutSpec_cxx.swift +140 -0
  151. package/nitrogen/generated/ios/swift/HybridNativeListDataSourceSpec.swift +62 -0
  152. package/nitrogen/generated/ios/swift/HybridNativeListDataSourceSpec_cxx.swift +222 -0
  153. package/nitrogen/generated/ios/swift/HybridNativeListLayoutSpec.swift +55 -0
  154. package/nitrogen/generated/ios/swift/HybridNativeListLayoutSpec_cxx.swift +128 -0
  155. package/nitrogen/generated/ios/swift/HybridUiListModuleSpec.swift +56 -0
  156. package/nitrogen/generated/ios/swift/HybridUiListModuleSpec_cxx.swift +175 -0
  157. package/nitrogen/generated/ios/swift/HybridUiListViewSpec.swift +59 -0
  158. package/nitrogen/generated/ios/swift/HybridUiListViewSpec_cxx.swift +227 -0
  159. package/nitrogen/generated/ios/swift/HybridViewHolderSpec.swift +55 -0
  160. package/nitrogen/generated/ios/swift/HybridViewHolderSpec_cxx.swift +147 -0
  161. package/nitrogen/generated/ios/swift/NativeItemSizeEstimate.swift +60 -0
  162. package/nitrogen/generated/ios/swift/NativeLinearListLayoutConfig.swift +60 -0
  163. package/nitrogen/generated/ios/swift/NativeLinearListLayoutIOSConfig.swift +35 -0
  164. package/nitrogen/generated/ios/swift/NativeListItem.swift +75 -0
  165. package/nitrogen/generated/ios/swift/Variant_NullType__any_HybridIOSWorkletsModuleProxyHolderSpec_.swift +30 -0
  166. package/nitrogen/generated/shared/c++/HybridIOSWorkletsModuleProxyHolderSpec.cpp +21 -0
  167. package/nitrogen/generated/shared/c++/HybridIOSWorkletsModuleProxyHolderSpec.hpp +62 -0
  168. package/nitrogen/generated/shared/c++/HybridNativeLinearListLayoutSpec.cpp +22 -0
  169. package/nitrogen/generated/shared/c++/HybridNativeLinearListLayoutSpec.hpp +67 -0
  170. package/nitrogen/generated/shared/c++/HybridNativeListDataSourceSpec.cpp +28 -0
  171. package/nitrogen/generated/shared/c++/HybridNativeListDataSourceSpec.hpp +72 -0
  172. package/nitrogen/generated/shared/c++/HybridNativeListLayoutSpec.cpp +21 -0
  173. package/nitrogen/generated/shared/c++/HybridNativeListLayoutSpec.hpp +62 -0
  174. package/nitrogen/generated/shared/c++/HybridUiListModuleSpec.cpp +22 -0
  175. package/nitrogen/generated/shared/c++/HybridUiListModuleSpec.hpp +68 -0
  176. package/nitrogen/generated/shared/c++/HybridUiListViewSpec.cpp +25 -0
  177. package/nitrogen/generated/shared/c++/HybridUiListViewSpec.hpp +79 -0
  178. package/nitrogen/generated/shared/c++/HybridUiManagerHelperSpec.cpp +23 -0
  179. package/nitrogen/generated/shared/c++/HybridUiManagerHelperSpec.hpp +65 -0
  180. package/nitrogen/generated/shared/c++/HybridViewHolderSpec.cpp +21 -0
  181. package/nitrogen/generated/shared/c++/HybridViewHolderSpec.hpp +62 -0
  182. package/nitrogen/generated/shared/c++/NativeItemSizeEstimate.hpp +87 -0
  183. package/nitrogen/generated/shared/c++/NativeLinearListLayoutConfig.hpp +105 -0
  184. package/nitrogen/generated/shared/c++/NativeLinearListLayoutIOSConfig.hpp +85 -0
  185. package/nitrogen/generated/shared/c++/NativeListItem.hpp +101 -0
  186. package/nitrogen/generated/shared/c++/views/HybridUiListViewComponent.cpp +72 -0
  187. package/nitrogen/generated/shared/c++/views/HybridUiListViewComponent.hpp +109 -0
  188. package/nitrogen/generated/shared/c++/views/HybridViewHolderComponent.cpp +72 -0
  189. package/nitrogen/generated/shared/c++/views/HybridViewHolderComponent.hpp +109 -0
  190. package/nitrogen/generated/shared/json/UiListViewConfig.json +9 -0
  191. package/nitrogen/generated/shared/json/ViewHolderConfig.json +9 -0
  192. package/package.json +152 -5
  193. package/react-native.config.js +16 -0
  194. package/src/ListDataSource.ts +232 -0
  195. package/src/ListLayout.ts +95 -0
  196. package/src/UiListModule.ts +5 -0
  197. package/src/hooks/useChangeEffect.ts +50 -0
  198. package/src/index.tsx +49 -0
  199. package/src/privateGlobals.ts +20 -0
  200. package/src/renderer/fabric/RenderHelper.ts +29 -0
  201. package/src/renderer/fabric/UiManagerHelper.ts +5 -0
  202. package/src/renderer/react/ReactFabricMirror.bundle.js +1984 -0
  203. package/src/renderer/react/ReactFabricMirror.ts +766 -0
  204. package/src/renderer/react/ReactFabricRenderer.ts +11 -0
  205. package/src/specs/IOSWorkletsModuleProxyHolder.nitro.ts +6 -0
  206. package/src/specs/NativeLinearListLayout.nitro.ts +23 -0
  207. package/src/specs/NativeListDataSource.nitro.ts +28 -0
  208. package/src/specs/NativeListLayout.nitro.ts +6 -0
  209. package/src/specs/UIListModule.nitro.ts +13 -0
  210. package/src/specs/UIManagerHelper.nitro.ts +34 -0
  211. package/src/specs/UiListView.nitro.ts +31 -0
  212. package/src/specs/ViewHolder.nitro.ts +11 -0
  213. package/src/views/List.tsx +525 -0
  214. package/src/views/UiListHostComponent.ts +8 -0
  215. package/FillRateHelper.js +0 -179
  216. package/FlatList.js +0 -494
  217. package/LICENSE.md +0 -31
  218. package/MetroListView.js +0 -166
  219. package/SectionList.js +0 -291
  220. package/ViewabilityHelper.js +0 -260
  221. package/VirtualizeUtils.js +0 -163
  222. package/VirtualizedList.js +0 -861
  223. package/VirtualizedSectionList.js +0 -397
  224. package/index.js +0 -5
@@ -0,0 +1,11 @@
1
+ type ReactFabricMirrorModule = typeof import('./ReactFabricMirror')
2
+
3
+ // This has to be a function so that its a worklet which can be called on the UI runtime.
4
+ // Never import the ReactFabricMirrorModule directly from non-worklet code!
5
+ export function getReactFabricRenderer(): ReactFabricMirrorModule {
6
+ 'worklet'
7
+ // This file is generated by `bun bundle:js`. It contains setting up the React instance on the UI runtime.
8
+ // This is what will do the "react render phase" of our react tree. Note that this is seperate from the
9
+ // fabric tree rendering + mounting. This is just react stuff.
10
+ return require('react-native-list/src/renderer/react/ReactFabricMirror.bundle')
11
+ }
@@ -0,0 +1,6 @@
1
+ import { HybridObject } from 'react-native-nitro-modules'
2
+
3
+ export interface IOSWorkletsModuleProxyHolder extends HybridObject<{
4
+ android: 'kotlin'
5
+ ios: 'swift'
6
+ }> {}
@@ -0,0 +1,23 @@
1
+ import { NativeListLayout } from './NativeListLayout.nitro'
2
+
3
+ export interface NativeItemSizeEstimate {
4
+ width?: number
5
+ height?: number
6
+ }
7
+
8
+ export interface NativeLinearListLayoutIOSConfig {
9
+ estimatedItemSize?: NativeItemSizeEstimate
10
+ }
11
+
12
+ export interface NativeLinearListLayoutConfig {
13
+ topInset: number
14
+ bottomInset: number
15
+ itemSpacing: number
16
+ itemHorizontalInset: number
17
+ itemVerticalInset: number
18
+ iosConfig?: NativeLinearListLayoutIOSConfig
19
+ }
20
+
21
+ export interface NativeLinearListLayout extends NativeListLayout {
22
+ setConfig(config: NativeLinearListLayoutConfig): void
23
+ }
@@ -0,0 +1,28 @@
1
+ import { AnyMap, HybridObject, Sync } from 'react-native-nitro-modules'
2
+
3
+ export interface NativeListItem {
4
+ key: string
5
+ type: string
6
+ width?: number
7
+ height?: number
8
+ data: AnyMap
9
+ }
10
+
11
+ export interface NativeListDataSource extends HybridObject<{
12
+ android: 'kotlin'
13
+ ios: 'swift'
14
+ }> {
15
+ setContentEqualCallback(
16
+ isContentEqual: Sync<
17
+ (oldItem: NativeListItem, newItem: NativeListItem) => boolean
18
+ >
19
+ ): void
20
+
21
+ replaceData(items: NativeListItem[], animated: boolean): void
22
+ insertItem(index: number, item: NativeListItem): void
23
+ updateItem(index: number, item: NativeListItem): void
24
+ removeItem(index: number): void
25
+ moveItem(fromIndex: number, toIndex: number): void
26
+ getCount(): number
27
+ getItem(index: number): NativeListItem
28
+ }
@@ -0,0 +1,6 @@
1
+ import { HybridObject } from 'react-native-nitro-modules'
2
+
3
+ export interface NativeListLayout extends HybridObject<{
4
+ android: 'kotlin'
5
+ ios: 'swift'
6
+ }> {}
@@ -0,0 +1,13 @@
1
+ import { HybridObject } from 'react-native-nitro-modules'
2
+ import type { IOSWorkletsModuleProxyHolder } from './IOSWorkletsModuleProxyHolder.nitro'
3
+ // import { ViewHolder } from './ViewHolder.nitro'
4
+
5
+ export interface UiListModule extends HybridObject<{
6
+ android: 'kotlin'
7
+ ios: 'swift'
8
+ }> {
9
+ // TODO: on iOS getting the worklets proxy has JS thread asserts, so we have to get it from JS and pass it to the UI thread. Maybe we can change that in nitro at some point?
10
+ iosGetWorkletsModule(): IOSWorkletsModuleProxyHolder
11
+ setupRuntime(workletsModuleHolder: IOSWorkletsModuleProxyHolder | null): void
12
+ // renderAndGetView(tag: number): ViewHolder
13
+ }
@@ -0,0 +1,34 @@
1
+ import { CustomType, HybridObject } from 'react-native-nitro-modules'
2
+
3
+ type UiManagerBidningType = {
4
+ _stubToMakeNitroHappy_doesNotExist_doNotUse: void
5
+ }
6
+
7
+ export type UiManagerBinding = CustomType<
8
+ UiManagerBidningType,
9
+ 'std::shared_ptr<facebook::react::UIManagerBinding>',
10
+ { include: 'JSIConverter+UIManagerBinding.hpp' }
11
+ >
12
+
13
+ type ShadowNodeListType = {
14
+ _stubToMakeNitroHappy_doesNotExist_doNotUse: void
15
+ }
16
+
17
+ export type ShadowNodeList = CustomType<
18
+ ShadowNodeListType,
19
+ 'facebook::react::ShadowNode::UnsharedListOfShared',
20
+ { include: 'JSIConverter+ShadowNodeList.hpp' }
21
+ >
22
+
23
+ export interface UiManagerHelper extends HybridObject<{
24
+ android: 'c++'
25
+ ios: 'c++'
26
+ }> {
27
+ completeRootSync(
28
+ nativeFabricUIManager: UiManagerBinding,
29
+ surfaceId: number,
30
+ childSet: ShadowNodeList
31
+ ): void
32
+ registerManagedSurface(surfaceId: number): void
33
+ unregisterManagedSurface(surfaceId: number): void
34
+ }
@@ -0,0 +1,31 @@
1
+ import {
2
+ HybridView,
3
+ HybridViewMethods,
4
+ HybridViewProps,
5
+ Sync,
6
+ } from 'react-native-nitro-modules'
7
+ import {
8
+ NativeListDataSource,
9
+ NativeListItem,
10
+ } from './NativeListDataSource.nitro'
11
+ import { NativeListLayout } from './NativeListLayout.nitro'
12
+ import { UiListModule } from './UIListModule.nitro'
13
+
14
+ export interface UiListViewProps extends HybridViewProps {}
15
+
16
+ export interface UiListViewMethods extends HybridViewMethods {
17
+ setListCallbacks(
18
+ uiListModule: UiListModule,
19
+ createView: Sync<(type: string) => number>,
20
+ updateView: Sync<
21
+ (reactTag: number, item: NativeListItem, index: number) => boolean
22
+ >
23
+ ): void
24
+
25
+ setDataSource(dataSource: NativeListDataSource): void
26
+ setLayout(layout: NativeListLayout): void
27
+ getSurfaceId(): number
28
+ disposeRendererSurface(): void
29
+ }
30
+
31
+ export type UiListView = HybridView<UiListViewProps, UiListViewMethods>
@@ -0,0 +1,11 @@
1
+ // import type { ColorValue } from "react-native";
2
+ import type {
3
+ HybridView,
4
+ HybridViewMethods,
5
+ HybridViewProps,
6
+ } from 'react-native-nitro-modules'
7
+
8
+ export interface ViewHolderProps extends HybridViewProps {}
9
+
10
+ export interface ViewHolderMethods extends HybridViewMethods {}
11
+ export type ViewHolder = HybridView<ViewHolderProps, ViewHolderMethods>
@@ -0,0 +1,525 @@
1
+ import React, { useCallback, useEffect, useMemo, useRef } from 'react'
2
+ import { View, ViewStyle } from 'react-native'
3
+ import { callback, NitroModules } from 'react-native-nitro-modules'
4
+ import {
5
+ createShareable,
6
+ scheduleOnUI,
7
+ UIRuntimeId,
8
+ } from 'react-native-worklets'
9
+ import type {
10
+ ListDataSource,
11
+ ListDataSourceMutation,
12
+ ListItem,
13
+ ListItemForType,
14
+ ListItemType,
15
+ } from '../ListDataSource'
16
+ import {
17
+ addListDataSourceMutationListener,
18
+ getNativeListDataSource,
19
+ } from '../ListDataSource'
20
+ import { createLinearListLayout, ListLayout } from '../ListLayout'
21
+ import { useChangeEffect } from '../hooks/useChangeEffect'
22
+ import {
23
+ completeRootSyncWorklet,
24
+ registerManagedSurfaceWorklet,
25
+ unregisterManagedSurfaceWorklet,
26
+ uiListModuleBoxed,
27
+ } from '../renderer/fabric/RenderHelper'
28
+ import { getReactFabricRenderer } from '../renderer/react/ReactFabricRenderer'
29
+ import type { NativeListItem } from '../specs/NativeListDataSource.nitro'
30
+ import type { UiListViewMethods } from '../specs/UiListView.nitro'
31
+ import { UiListHostComponent } from './UiListHostComponent'
32
+
33
+ type NativeTaggedRef = {
34
+ __nativeTag: number
35
+ }
36
+
37
+ type RenderedElementRecord = {
38
+ element: React.ReactElement
39
+ /**
40
+ * Stable React element key for a rendered slot in our tree.
41
+ */
42
+ reactKey: number
43
+ /**
44
+ * Datasource item identity.
45
+ * The user provided key for an item with data.
46
+ */
47
+ dataKey: string | null
48
+ /**
49
+ * Native React tag binding this React instance to a React Native view.
50
+ * A React tag can exist without a dataKey while the view is not currently
51
+ * bound to a datasource item.
52
+ */
53
+ reactTag: number
54
+ }
55
+
56
+ type ListState = {
57
+ elementRecords: RenderedElementRecord[]
58
+ reactTagToRecordIndex: Record<number, number>
59
+ reactTagToReactKey: Record<number, number>
60
+ reactTagToDataKey: Record<number, string>
61
+ dataKeyToReactTag: Record<string, number>
62
+ nextReactKey: number
63
+ isDisposed: boolean
64
+ surfaceId: number | null
65
+ }
66
+
67
+ export type ListRenderer<TItem extends ListItem> = {
68
+ renderItemWorklet: (info: {
69
+ item?: TItem
70
+ index?: number
71
+ key?: string
72
+ type: ListItemType<TItem>
73
+ }) => React.ReactElement<any>
74
+ }
75
+
76
+ export type ListRenderers<TItem extends ListItem> = {
77
+ [TType in ListItemType<TItem>]: ListRenderer<ListItemForType<TItem, TType>>
78
+ }
79
+
80
+ export type ListProps<TItem extends ListItem> = {
81
+ dataSource: ListDataSource<TItem>
82
+ layout?: ListLayout
83
+ renderers: ListRenderers<TItem>
84
+ style?: ViewStyle
85
+ }
86
+
87
+ function ListInner<TItem extends ListItem>(props: ListProps<TItem>) {
88
+ const { dataSource, layout, renderers, style } = props
89
+ const isSetup = useRef(false)
90
+ const nativeListRef = useRef<UiListViewMethods | null>(null)
91
+
92
+ const listState = useMemo(() => {
93
+ return createShareable<ListState>(UIRuntimeId, {
94
+ elementRecords: [],
95
+ nextReactKey: 0,
96
+ reactTagToRecordIndex: {},
97
+ reactTagToReactKey: {},
98
+ // Maintain two reverse lookups that are one-to-one
99
+ // Used for O(1) operations in element updates and removals,
100
+ // where we only get the reactTag from native, and need to find the record,
101
+ // or need to find the reactTag for a dataKey
102
+ reactTagToDataKey: {},
103
+ dataKeyToReactTag: {},
104
+ isDisposed: false,
105
+ surfaceId: null,
106
+ })
107
+ }, [])
108
+
109
+ const getListState = useCallback(() => {
110
+ 'worklet'
111
+ if (listState.isHost === false) {
112
+ throw new Error(
113
+ 'Expected listState to be only accessed on the UI Runtime!'
114
+ )
115
+ }
116
+ return listState.value
117
+ }, [listState])
118
+
119
+ const resolvedLayout = useMemo(() => {
120
+ if (layout != null) {
121
+ return layout
122
+ }
123
+
124
+ return createLinearListLayout()
125
+ }, [layout])
126
+
127
+ const ownsResolvedLayout = layout == null
128
+
129
+ useEffect(() => {
130
+ if (!ownsResolvedLayout) {
131
+ return
132
+ }
133
+
134
+ return () => {
135
+ resolvedLayout.release()
136
+ }
137
+ }, [ownsResolvedLayout, resolvedLayout])
138
+
139
+ const boxedDataSource = useMemo(() => {
140
+ const nativeDataSource = getNativeListDataSource(dataSource)
141
+ return NitroModules.box(nativeDataSource)
142
+ }, [dataSource])
143
+
144
+ const boxedLayout = useMemo(() => {
145
+ return NitroModules.box(resolvedLayout.__nativeLayout)
146
+ }, [resolvedLayout])
147
+
148
+ const clearListItemKeys = useMemo(() => {
149
+ return () => {
150
+ 'worklet'
151
+
152
+ const state = getListState()
153
+ state.elementRecords.forEach((record) => {
154
+ record.dataKey = null
155
+ })
156
+
157
+ for (const tagKey of Object.keys(state.reactTagToDataKey)) {
158
+ delete state.reactTagToDataKey[Number(tagKey)]
159
+ }
160
+
161
+ for (const dataKey of Object.keys(state.dataKeyToReactTag)) {
162
+ delete state.dataKeyToReactTag[dataKey]
163
+ }
164
+ }
165
+ }, [getListState])
166
+
167
+ const handleDataSourceMutation = useMemo(() => {
168
+ return (mutation: ListDataSourceMutation) => {
169
+ 'worklet'
170
+
171
+ if (mutation.type === 'replaceData') {
172
+ clearListItemKeys()
173
+ return
174
+ }
175
+
176
+ let dataKey: string
177
+ if (mutation.type === 'removeItem') {
178
+ dataKey = mutation.itemKey
179
+ } else {
180
+ dataKey = mutation.previousItemKey
181
+ }
182
+
183
+ const state = getListState()
184
+ const reactTag = state.dataKeyToReactTag[dataKey]
185
+ if (reactTag == null) {
186
+ return
187
+ }
188
+
189
+ const position = state.reactTagToRecordIndex[reactTag]
190
+ if (position == null) {
191
+ delete state.dataKeyToReactTag[dataKey]
192
+ delete state.reactTagToDataKey[reactTag]
193
+ return
194
+ }
195
+
196
+ const record = state.elementRecords[position]
197
+ if (record == null) {
198
+ delete state.dataKeyToReactTag[dataKey]
199
+ delete state.reactTagToDataKey[reactTag]
200
+ return
201
+ }
202
+
203
+ if (record.dataKey !== dataKey) {
204
+ delete state.dataKeyToReactTag[dataKey]
205
+ return
206
+ }
207
+
208
+ record.dataKey = null
209
+ delete state.dataKeyToReactTag[dataKey]
210
+ delete state.reactTagToDataKey[reactTag]
211
+ }
212
+ }, [clearListItemKeys, getListState])
213
+
214
+ useEffect(() => {
215
+ return addListDataSourceMutationListener(
216
+ dataSource,
217
+ handleDataSourceMutation
218
+ )
219
+ }, [dataSource, handleDataSourceMutation])
220
+
221
+ useEffect(() => {
222
+ return () => {
223
+ const ref = nativeListRef.current
224
+ const didSetup = isSetup.current
225
+
226
+ nativeListRef.current = null
227
+ isSetup.current = false
228
+
229
+ if (ref == null) {
230
+ return
231
+ }
232
+
233
+ scheduleOnUI(() => {
234
+ 'worklet'
235
+
236
+ const state = getListState()
237
+ state.isDisposed = true
238
+
239
+ if (!didSetup) {
240
+ ref.disposeRendererSurface()
241
+ return
242
+ }
243
+
244
+ const surfaceId = state.surfaceId
245
+ ref.disposeRendererSurface()
246
+ state.surfaceId = null
247
+
248
+ if (surfaceId == null) {
249
+ return
250
+ }
251
+
252
+ const { disposeReactRoot } = getReactFabricRenderer()
253
+ unregisterManagedSurfaceWorklet(surfaceId)
254
+ disposeReactRoot(surfaceId, completeRootSyncWorklet)
255
+ state.elementRecords = []
256
+
257
+ for (const key of Object.keys(state.reactTagToRecordIndex)) {
258
+ delete state.reactTagToRecordIndex[Number(key)]
259
+ }
260
+
261
+ for (const key of Object.keys(state.reactTagToReactKey)) {
262
+ delete state.reactTagToReactKey[Number(key)]
263
+ }
264
+
265
+ for (const key of Object.keys(state.reactTagToDataKey)) {
266
+ delete state.reactTagToDataKey[Number(key)]
267
+ }
268
+
269
+ for (const key of Object.keys(state.dataKeyToReactTag)) {
270
+ delete state.dataKeyToReactTag[key]
271
+ }
272
+ })
273
+ }
274
+ }, [getListState])
275
+
276
+ useChangeEffect(() => {
277
+ const ref = nativeListRef.current
278
+ if (ref == null) return
279
+
280
+ scheduleOnUI(clearListItemKeys)
281
+
282
+ const nativeDataSource = getNativeListDataSource(dataSource)
283
+ ref.setDataSource(nativeDataSource)
284
+ ref.setLayout(resolvedLayout.__nativeLayout)
285
+ }, [clearListItemKeys, dataSource, resolvedLayout])
286
+
287
+ return (
288
+ <UiListHostComponent
289
+ style={style}
290
+ hybridRef={callback((ref) => {
291
+ nativeListRef.current = ref
292
+
293
+ if (isSetup.current) return
294
+ isSetup.current = true
295
+
296
+ scheduleOnUI(() => {
297
+ 'worklet'
298
+
299
+ const { reactRender } = getReactFabricRenderer()
300
+ const state = getListState()
301
+ const surfaceId = ref.getSurfaceId()
302
+ state.isDisposed = false
303
+ state.surfaceId = surfaceId
304
+ registerManagedSurfaceWorklet(surfaceId)
305
+
306
+ function renderListElements() {
307
+ 'worklet'
308
+
309
+ return state.elementRecords.map((record) => {
310
+ const wrapperStyle = {
311
+ // Why are we rendering position absolute?
312
+ // This will layout all items at (0x0).This is important because the native lists will relayout the views.
313
+ // In one iteration I was rendering all items just regularly. When then the items position in the elements changed or items were added before,
314
+ // fabric was thinking it had to update the layout position of those items, breaking the layout in the list.
315
+ // If fabric thinks all items are always at (0x0) it won't get the idea to relocate them!
316
+ position: 'absolute' as const,
317
+ left: 0,
318
+ top: 0,
319
+ }
320
+ const wrapperKey = 'reactkey-wrapper-' + record.reactKey
321
+ return (
322
+ <View key={wrapperKey} style={wrapperStyle} collapsable={false}>
323
+ {record.element}
324
+ </View>
325
+ )
326
+ })
327
+ }
328
+
329
+ function rebuildTagPositions() {
330
+ 'worklet'
331
+
332
+ for (const key of Object.keys(state.reactTagToRecordIndex)) {
333
+ delete state.reactTagToRecordIndex[Number(key)]
334
+ }
335
+
336
+ state.elementRecords.forEach((record, index) => {
337
+ if (record.reactTag < 0) {
338
+ return
339
+ }
340
+
341
+ state.reactTagToRecordIndex[record.reactTag] = index
342
+ })
343
+ }
344
+
345
+ function bindDataKeyToReactTag(dataKey: string, reactTag: number) {
346
+ 'worklet'
347
+
348
+ const previousDataKey = state.reactTagToDataKey[reactTag]
349
+ if (previousDataKey != null && previousDataKey !== dataKey) {
350
+ // This same reactTag used to represent another dataKey.
351
+ // If it is now being assigned to dataKey, remove the old reverse lookup
352
+ delete state.dataKeyToReactTag[previousDataKey]
353
+ }
354
+
355
+ const previousReactTag = state.dataKeyToReactTag[dataKey]
356
+ if (previousReactTag != null && previousReactTag !== reactTag) {
357
+ // This dataKey used to point at another reactTag.
358
+ // If it is now being assigned to this reactTag, clear the old tag's binding
359
+ const previousPosition =
360
+ state.reactTagToRecordIndex[previousReactTag]
361
+ if (previousPosition != null) {
362
+ const previousRecord = state.elementRecords[previousPosition]
363
+ if (previousRecord != null) {
364
+ previousRecord.dataKey = null
365
+ }
366
+ }
367
+ delete state.reactTagToDataKey[previousReactTag]
368
+ }
369
+
370
+ state.reactTagToDataKey[reactTag] = dataKey
371
+ state.dataKeyToReactTag[dataKey] = reactTag
372
+ }
373
+
374
+ function renderContentInReact() {
375
+ 'worklet'
376
+
377
+ if (state.isDisposed) {
378
+ return
379
+ }
380
+
381
+ const elements = renderListElements()
382
+ const parentContainer = <View>{elements}</View>
383
+ reactRender(
384
+ surfaceId,
385
+ parentContainer,
386
+ () => {},
387
+ completeRootSyncWorklet
388
+ )
389
+ rebuildTagPositions()
390
+ }
391
+
392
+ function setNativeListDataSource() {
393
+ 'worklet'
394
+
395
+ const nativeDataSource = boxedDataSource.unbox()
396
+ const nativeLayout = boxedLayout.unbox()
397
+ ref.setDataSource(nativeDataSource)
398
+ ref.setLayout(nativeLayout)
399
+ }
400
+
401
+ function createViewCallback(type: string) {
402
+ if (state.isDisposed) {
403
+ throw new Error('Cannot create view after list was disposed')
404
+ }
405
+
406
+ const nativeRef = globalThis.React.createRef<NativeTaggedRef>()
407
+ const reactKey = state.nextReactKey++
408
+ const typedType = type as ListItemType<TItem>
409
+ const renderer = renderers[typedType] as ListRenderer<TItem>
410
+
411
+ if (renderer == null) {
412
+ throw new Error('No renderer for list item type ' + type)
413
+ }
414
+
415
+ const newElement = renderer.renderItemWorklet({
416
+ type: typedType,
417
+ })
418
+
419
+ const newProps = {
420
+ key: 'reactkey-' + reactKey,
421
+ ref: nativeRef,
422
+ collapsable: false,
423
+ }
424
+ const newElementWithKey = globalThis.React.cloneElement(
425
+ newElement,
426
+ newProps
427
+ )
428
+
429
+ const newRecord: RenderedElementRecord = {
430
+ element: newElementWithKey,
431
+ reactKey,
432
+ dataKey: null,
433
+ reactTag: -1,
434
+ }
435
+ const newLength = state.elementRecords.push(newRecord)
436
+ const currentIndex = newLength - 1
437
+
438
+ // Why for rendering one item we have to render the whole content?!
439
+ // Thats because react/react-native would issue remove transitions if we'd only render the item we need, and then swap it for another item.
440
+ // When rendering all content react-reconciler will only update the diff on the native side, which is just this one item, so performance wise this seems to be okay.
441
+ renderContentInReact()
442
+
443
+ if (nativeRef.current == null) {
444
+ throw new Error('Ref is null after render')
445
+ }
446
+
447
+ const reactTag = nativeRef.current.__nativeTag
448
+ newRecord.reactTag = reactTag
449
+ state.reactTagToRecordIndex[reactTag] = currentIndex
450
+ state.reactTagToReactKey[reactTag] = reactKey
451
+
452
+ return reactTag
453
+ }
454
+
455
+ function updateViewCallback(
456
+ reactTag: number,
457
+ item: NativeListItem,
458
+ index: number
459
+ ) {
460
+ if (state.isDisposed) {
461
+ return false
462
+ }
463
+
464
+ const typedType: ListItemType<TItem> = item.type
465
+ const renderer = renderers[typedType] as ListRenderer<TItem>
466
+
467
+ if (renderer == null) {
468
+ throw new Error('No renderer for list item type ' + item.type)
469
+ }
470
+
471
+ const reactKey = state.reactTagToReactKey[reactTag]
472
+ if (reactKey == null) {
473
+ throw new Error('No reactKey for reactTag ' + reactTag)
474
+ }
475
+
476
+ const newElement = renderer.renderItemWorklet({
477
+ item: item as unknown as TItem,
478
+ index,
479
+ key: item.key,
480
+ type: typedType,
481
+ })
482
+ const newProps = {
483
+ key: 'reactkey-' + reactKey,
484
+ collapsable: false,
485
+ }
486
+ const newElementWithKey = globalThis.React.cloneElement(
487
+ newElement,
488
+ newProps
489
+ )
490
+
491
+ const position = state.reactTagToRecordIndex[reactTag]
492
+ if (position == null) {
493
+ throw new Error('No position for reactTag ' + reactTag)
494
+ }
495
+
496
+ const record = state.elementRecords[position]
497
+ if (record == null) {
498
+ throw new Error('No record for reactTag ' + reactTag)
499
+ }
500
+
501
+ bindDataKeyToReactTag(item.key, reactTag)
502
+ record.element = newElementWithKey
503
+ record.dataKey = item.key
504
+
505
+ renderContentInReact()
506
+
507
+ return true
508
+ }
509
+
510
+ const uiListModuleUnboxed = uiListModuleBoxed.unbox()
511
+ ref.setListCallbacks(
512
+ uiListModuleUnboxed,
513
+ createViewCallback,
514
+ updateViewCallback
515
+ )
516
+ setNativeListDataSource()
517
+ })
518
+ })}
519
+ />
520
+ )
521
+ }
522
+
523
+ export const List = ListInner as <TItem extends ListItem>(
524
+ props: ListProps<TItem>
525
+ ) => React.ReactElement | null