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,213 @@
1
+ import DifferenceKit
2
+ import Foundation
3
+ import NitroModules
4
+
5
+ final class DiffableListItem: Differentiable {
6
+ typealias DifferenceIdentifier = String
7
+
8
+ let nativeItem: NativeListItem
9
+ private let contentEqual: (NativeListItem, NativeListItem) -> Bool
10
+
11
+ init(
12
+ nativeItem: NativeListItem,
13
+ contentEqual: @escaping (NativeListItem, NativeListItem) -> Bool
14
+ ) {
15
+ self.nativeItem = nativeItem
16
+ self.contentEqual = contentEqual
17
+ }
18
+
19
+ var differenceIdentifier: String {
20
+ return nativeItem.type + ":" + nativeItem.key
21
+ }
22
+
23
+ func isContentEqual(to source: DiffableListItem) -> Bool {
24
+ if nativeItem.type != source.nativeItem.type {
25
+ return false
26
+ }
27
+ if nativeItem.width != source.nativeItem.width {
28
+ return false
29
+ }
30
+ if nativeItem.height != source.nativeItem.height {
31
+ return false
32
+ }
33
+ return contentEqual(source.nativeItem, nativeItem)
34
+ }
35
+ }
36
+
37
+ protocol NativeListDataSourceObserver: AnyObject {
38
+ func dataSourceDidReload(
39
+ _ dataSource: HybridNativeListDataSource,
40
+ animated: Bool,
41
+ changeset: StagedChangeset<[DiffableListItem]>?
42
+ )
43
+ func dataSourceDidInsert(_ dataSource: HybridNativeListDataSource, index: Int)
44
+ func dataSourceDidUpdate(
45
+ _ dataSource: HybridNativeListDataSource,
46
+ index: Int,
47
+ previousItem: NativeListItem
48
+ )
49
+ func dataSourceDidRemove(
50
+ _ dataSource: HybridNativeListDataSource,
51
+ index: Int,
52
+ removedItem: NativeListItem
53
+ )
54
+ func dataSourceDidMove(_ dataSource: HybridNativeListDataSource, fromIndex: Int, toIndex: Int)
55
+ }
56
+
57
+ class HybridNativeListDataSource: HybridNativeListDataSourceSpec {
58
+ weak var observer: NativeListDataSourceObserver?
59
+ private var items: [DiffableListItem] = []
60
+ private var pendingTargetItems: [DiffableListItem]?
61
+ private var animatedReloadSourceItems: [DiffableListItem]?
62
+ private var contentEqual: (NativeListItem, NativeListItem) -> Bool = { _, _ in false }
63
+
64
+ func dispose() {
65
+ observer = nil
66
+ items.removeAll()
67
+ pendingTargetItems = nil
68
+ animatedReloadSourceItems = nil
69
+ contentEqual = { _, _ in false }
70
+ }
71
+
72
+ func setContentEqualCallback(
73
+ isContentEqual: @escaping (NativeListItem, NativeListItem) -> Bool
74
+ ) throws {
75
+ contentEqual = isContentEqual
76
+ }
77
+
78
+ func replaceData(items newItems: [NativeListItem], animated: Bool) throws {
79
+ let targetItems = wrap(newItems)
80
+ guard animated, observer != nil else {
81
+ pendingTargetItems = nil
82
+ animatedReloadSourceItems = nil
83
+ items = targetItems
84
+ observer?.dataSourceDidReload(self, animated: false, changeset: nil)
85
+ return
86
+ }
87
+
88
+ animatedReloadSourceItems = items
89
+ let changeset = StagedChangeset(source: items, target: targetItems)
90
+ guard !changeset.isEmpty else {
91
+ pendingTargetItems = nil
92
+ animatedReloadSourceItems = nil
93
+ items = targetItems
94
+ observer?.dataSourceDidReload(self, animated: false, changeset: nil)
95
+ return
96
+ }
97
+
98
+ pendingTargetItems = targetItems
99
+ observer?.dataSourceDidReload(self, animated: true, changeset: changeset)
100
+ }
101
+
102
+ func insertItem(index: Double, item: NativeListItem) throws {
103
+ let itemIndex = validInsertionIndex(index)
104
+ let wrappedItem = wrap(item)
105
+ items.insert(wrappedItem, at: itemIndex)
106
+ observer?.dataSourceDidInsert(self, index: itemIndex)
107
+ }
108
+
109
+ func updateItem(index: Double, item: NativeListItem) throws {
110
+ let itemIndex = validExistingIndex(index)
111
+ let previousItem = items[itemIndex].nativeItem
112
+ let wrappedItem = wrap(item)
113
+ items[itemIndex] = wrappedItem
114
+ observer?.dataSourceDidUpdate(self, index: itemIndex, previousItem: previousItem)
115
+ }
116
+
117
+ func removeItem(index: Double) throws {
118
+ let itemIndex = validExistingIndex(index)
119
+ let removedItem = items.remove(at: itemIndex).nativeItem
120
+ observer?.dataSourceDidRemove(self, index: itemIndex, removedItem: removedItem)
121
+ }
122
+
123
+ func moveItem(fromIndex: Double, toIndex: Double) throws {
124
+ let sourceIndex = validExistingIndex(fromIndex)
125
+ let targetIndex = validExistingIndex(toIndex)
126
+ let item = items.remove(at: sourceIndex)
127
+ items.insert(item, at: targetIndex)
128
+ observer?.dataSourceDidMove(self, fromIndex: sourceIndex, toIndex: targetIndex)
129
+ }
130
+
131
+ func getCount() throws -> Double {
132
+ return Double(items.count)
133
+ }
134
+
135
+ func getItem(index: Double) throws -> NativeListItem {
136
+ let itemIndex = validExistingIndex(index)
137
+ return item(at: itemIndex)
138
+ }
139
+
140
+ func item(at index: Int) -> NativeListItem {
141
+ return items[index].nativeItem
142
+ }
143
+
144
+ func itemForCollectionViewQuery(at index: Int) -> NativeListItem {
145
+ if items.indices.contains(index) {
146
+ return items[index].nativeItem
147
+ }
148
+
149
+ // During DifferenceKit staged reloads, UICollectionViewFlowLayout can still ask for
150
+ // sizing information from the pre-animation snapshot after the stage data was applied.
151
+ if let animatedReloadSourceItems, animatedReloadSourceItems.indices.contains(index) {
152
+ return animatedReloadSourceItems[index].nativeItem
153
+ }
154
+
155
+ preconditionFailure("List item index \(index) is out of bounds for collection view query.")
156
+ }
157
+
158
+ func replaceWrappedItemsFromCollectionView(_ nextItems: [DiffableListItem]) {
159
+ items = nextItems
160
+
161
+ guard let pendingTargetItems else {
162
+ animatedReloadSourceItems = nil
163
+ return
164
+ }
165
+
166
+ if hasSameIdentity(nextItems, pendingTargetItems) {
167
+ self.pendingTargetItems = nil
168
+ animatedReloadSourceItems = nil
169
+ }
170
+ }
171
+
172
+ func itemsForPremeasurement() -> [NativeListItem] {
173
+ let sourceItems = pendingTargetItems ?? items
174
+ return sourceItems.map { item in
175
+ item.nativeItem
176
+ }
177
+ }
178
+
179
+ private func wrap(_ item: NativeListItem) -> DiffableListItem {
180
+ return DiffableListItem(nativeItem: item, contentEqual: contentEqual)
181
+ }
182
+
183
+ private func wrap(_ nativeItems: [NativeListItem]) -> [DiffableListItem] {
184
+ return nativeItems.map { item in
185
+ wrap(item)
186
+ }
187
+ }
188
+
189
+ private func hasSameIdentity(
190
+ _ firstItems: [DiffableListItem],
191
+ _ secondItems: [DiffableListItem]
192
+ ) -> Bool {
193
+ if firstItems.count != secondItems.count {
194
+ return false
195
+ }
196
+
197
+ return zip(firstItems, secondItems).allSatisfy { firstItem, secondItem in
198
+ return firstItem.differenceIdentifier == secondItem.differenceIdentifier
199
+ }
200
+ }
201
+
202
+ private func validExistingIndex(_ value: Double) -> Int {
203
+ let index = Int(value)
204
+ precondition(index >= 0 && index < items.count, "List index \(index) is out of bounds.")
205
+ return index
206
+ }
207
+
208
+ private func validInsertionIndex(_ value: Double) -> Int {
209
+ let index = Int(value)
210
+ precondition(index >= 0 && index <= items.count, "List index \(index) is out of bounds.")
211
+ return index
212
+ }
213
+ }
@@ -0,0 +1,49 @@
1
+ //
2
+ // HybridUiListModule.swift
3
+ // ReactNativeList
4
+ //
5
+ // Created by Hanno Gödecke on 14.02.26.
6
+ //
7
+
8
+ import Foundation
9
+ import NitroModules
10
+
11
+ class HybridUiListModule : HybridUiListModuleSpec {
12
+ func iosGetWorkletsModule() throws -> any HybridIOSWorkletsModuleProxyHolderSpec {
13
+ do {
14
+ let holderBox = try TurboModuleInstaller.createWorkletsModuleProxyHolder()
15
+ return HybridIOSWorkletsModuleProxyHolder(holderBox: holderBox)
16
+ } catch {
17
+ throw RuntimeError.error(withMessage: String(describing: error))
18
+ }
19
+ }
20
+
21
+ func setupRuntime(workletsModuleHolder: Variant_NullType__any_HybridIOSWorkletsModuleProxyHolderSpec_?) throws {
22
+ if (!Thread.isMainThread) {
23
+ throw RuntimeError.error(withMessage: "setupRuntime() must be called from UI thread!")
24
+ }
25
+
26
+ guard let workletsModuleHolder else {
27
+ throw RuntimeError.error(withMessage: "setupRuntime() requires IOSWorkletsModuleProxyHolder on iOS.")
28
+ }
29
+
30
+ let holder: HybridIOSWorkletsModuleProxyHolder
31
+ switch workletsModuleHolder {
32
+ case .first:
33
+ throw RuntimeError.error(withMessage: "setupRuntime() received null holder on iOS.")
34
+ case .second(let anyHolder):
35
+ guard let concreteHolder = anyHolder as? HybridIOSWorkletsModuleProxyHolder else {
36
+ throw RuntimeError.error(withMessage: "setupRuntime() received unexpected holder type.")
37
+ }
38
+ holder = concreteHolder
39
+ }
40
+
41
+ do {
42
+ _ = try TurboModuleInstaller.installNativeModuleProxyInUIRuntime(withHolder: holder.holderBox)
43
+ _ = try TurboModuleInstaller.setupEventInterceptor()
44
+ } catch {
45
+ let message = String(describing: error)
46
+ throw RuntimeError.error(withMessage: message)
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,16 @@
1
+ //
2
+ // HybridVIewHolder.swift
3
+ // ReactNativeList
4
+ //
5
+ // Created by Hanno Gödecke on 14.02.26.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ class HybridViewHolder : HybridViewHolderSpec {
11
+ var view: UIView = UIView()
12
+
13
+ typealias ViewType = UIView
14
+
15
+
16
+ }
@@ -0,0 +1,128 @@
1
+ import NitroModules
2
+ import UIKit
3
+
4
+ class HybridNativeListLayout: HybridNativeListLayoutSpec {}
5
+
6
+ protocol NativeListLayoutProviding: AnyObject {
7
+ func makeCollectionViewLayout(owner: HybridUiListView) -> UICollectionViewLayout
8
+ func layoutSize(contentSize: CGSize) -> CGSize
9
+ func itemContentInset() -> UIEdgeInsets
10
+ func estimatedContentWidth(collectionViewWidth: CGFloat, viewWidth: CGFloat) -> CGFloat
11
+ func estimatedContentHeight(collectionViewHeight: CGFloat, viewHeight: CGFloat) -> CGFloat
12
+ }
13
+
14
+ class HybridNativeLinearListLayout: HybridNativeLinearListLayoutSpec, NativeListLayoutProviding {
15
+ private var topInset: CGFloat = 16
16
+ private var bottomInset: CGFloat = 16
17
+ private var itemSpacing: CGFloat = 12
18
+ private var itemHorizontalInset: CGFloat = 0
19
+ private var itemVerticalInset: CGFloat = 0
20
+ private var estimatedItemWidth: CGFloat?
21
+ private var estimatedItemHeight: CGFloat?
22
+
23
+ func dispose() {
24
+ topInset = 16
25
+ bottomInset = 16
26
+ itemSpacing = 12
27
+ itemHorizontalInset = 0
28
+ itemVerticalInset = 0
29
+ estimatedItemWidth = nil
30
+ estimatedItemHeight = nil
31
+ }
32
+
33
+ func setConfig(config: NativeLinearListLayoutConfig) throws {
34
+ topInset = CGFloat(config.topInset)
35
+ bottomInset = CGFloat(config.bottomInset)
36
+ itemSpacing = CGFloat(config.itemSpacing)
37
+ itemHorizontalInset = CGFloat(config.itemHorizontalInset)
38
+ itemVerticalInset = CGFloat(config.itemVerticalInset)
39
+
40
+ let estimatedItemSize = config.iosConfig?.estimatedItemSize
41
+ if let width = estimatedItemSize?.width {
42
+ estimatedItemWidth = CGFloat(width)
43
+ } else {
44
+ estimatedItemWidth = nil
45
+ }
46
+
47
+ if let height = estimatedItemSize?.height {
48
+ estimatedItemHeight = CGFloat(height)
49
+ } else {
50
+ estimatedItemHeight = nil
51
+ }
52
+ }
53
+
54
+ func makeCollectionViewLayout(owner: HybridUiListView) -> UICollectionViewLayout {
55
+ let layout = UICollectionViewFlowLayout()
56
+ layout.scrollDirection = .vertical
57
+ layout.minimumLineSpacing = itemSpacing
58
+ layout.minimumInteritemSpacing = 0
59
+ layout.sectionInset = UIEdgeInsets(
60
+ top: topInset,
61
+ left: 0,
62
+ bottom: bottomInset,
63
+ right: 0
64
+ )
65
+ // FlowLayout owns the vertical offset bookkeeping; we only provide measured sizes.
66
+ layout.estimatedItemSize = .zero
67
+ return layout
68
+ }
69
+
70
+ func layoutSize(contentSize: CGSize) -> CGSize {
71
+ let width = ceil(contentSize.width + itemHorizontalInset * 2)
72
+ let height = ceil(contentSize.height + itemVerticalInset * 2)
73
+ return CGSize(width: width, height: height)
74
+ }
75
+
76
+ func itemContentInset() -> UIEdgeInsets {
77
+ return UIEdgeInsets(
78
+ top: itemVerticalInset,
79
+ left: itemHorizontalInset,
80
+ bottom: itemVerticalInset,
81
+ right: itemHorizontalInset
82
+ )
83
+ }
84
+
85
+ func estimatedContentWidth(collectionViewWidth: CGFloat, viewWidth: CGFloat) -> CGFloat {
86
+ // FlowLayout may ask for sizes before a cell has rendered. A user-provided
87
+ // estimate is only used for that first pass; measured and explicit item sizes
88
+ // are resolved by HybridUiListView before this fallback is reached.
89
+ if let estimatedItemWidth, estimatedItemWidth.isFinite, estimatedItemWidth > 0 {
90
+ return estimatedItemWidth
91
+ }
92
+
93
+ let availableWidth = collectionViewWidth - itemHorizontalInset * 2
94
+ if availableWidth.isFinite && availableWidth > 0 {
95
+ return availableWidth
96
+ }
97
+
98
+ let fallbackWidth = viewWidth - itemHorizontalInset * 2
99
+ if fallbackWidth.isFinite && fallbackWidth > 0 {
100
+ return fallbackWidth
101
+ }
102
+
103
+ return 1
104
+ }
105
+
106
+ func estimatedContentHeight(collectionViewHeight: CGFloat, viewHeight: CGFloat) -> CGFloat {
107
+ // Keep the built-in fallback finite so FlowLayout can place initial cells,
108
+ // then let real measurements replace it after cells bind their content.
109
+ if let estimatedItemHeight, estimatedItemHeight.isFinite, estimatedItemHeight > 0 {
110
+ return estimatedItemHeight
111
+ }
112
+
113
+ if collectionViewHeight.isFinite && collectionViewHeight > 0 {
114
+ return collectionViewHeight / 2
115
+ }
116
+
117
+ if viewHeight.isFinite && viewHeight > 0 {
118
+ return viewHeight / 2
119
+ }
120
+
121
+ let screenHeight = UIScreen.main.bounds.height
122
+ if screenHeight.isFinite && screenHeight > 0 {
123
+ return screenHeight / 2
124
+ }
125
+
126
+ return 120
127
+ }
128
+ }
@@ -0,0 +1,26 @@
1
+ //
2
+ // ErrorUtils.h
3
+ // ReactNativeList
4
+ //
5
+ // Created by Hanno Gödecke on 15.02.26.
6
+ //
7
+
8
+ #import <Foundation/Foundation.h>
9
+
10
+ NS_ASSUME_NONNULL_BEGIN
11
+
12
+ static NSString *const kInstallerErrorDomain = @"ReactNativeList.HybridUiListTurboModuleInstaller";
13
+
14
+ inline void assignError(NSError *__autoreleasing _Nullable *error, NSString *message)
15
+ {
16
+ if (error == nil) {
17
+ return;
18
+ }
19
+
20
+ *error = [NSError errorWithDomain:kInstallerErrorDomain
21
+ code:1
22
+ userInfo:@{
23
+ NSLocalizedDescriptionKey : message,
24
+ }];
25
+ }
26
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,10 @@
1
+ import Foundation
2
+
3
+ class HybridIOSWorkletsModuleProxyHolder: HybridIOSWorkletsModuleProxyHolderSpec {
4
+ let holderBox: HybridWorkletsModuleProxyHolderBox
5
+
6
+ init(holderBox: HybridWorkletsModuleProxyHolderBox) {
7
+ self.holderBox = holderBox
8
+ super.init()
9
+ }
10
+ }
@@ -0,0 +1,20 @@
1
+ //
2
+ // SurfaceHelper.h
3
+ // ReactNativeList
4
+ //
5
+ // Created by Hanno Gödecke on 15.02.26.
6
+ //
7
+
8
+ #import <Foundation/Foundation.h>
9
+ #import <React/RCTPrimitives.h>
10
+ #import <UIKit/UIKit.h>
11
+
12
+ NS_ASSUME_NONNULL_BEGIN
13
+
14
+ @interface SurfaceHelper : NSObject
15
+ + (nullable NSNumber *)createExternalSurface:(NSError * _Nullable * _Nullable)error;
16
+ + (BOOL)releaseExternalSurface:(ReactTag)surfaceId error:(NSError * _Nullable * _Nullable)error;
17
+ + (nullable UIView *)getViewByTag:(ReactTag)tag error:(NSError * _Nullable * _Nullable)error;
18
+ @end
19
+
20
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,144 @@
1
+ //
2
+ // SurfaceHelper.m
3
+ // ReactNativeList
4
+ //
5
+ // Created by Hanno Gödecke on 15.02.26.
6
+ //
7
+
8
+ #import "SurfaceHelper.h"
9
+ #import "SurfacePresenterRegistry.h"
10
+ #import "ErrorUtils.h"
11
+
12
+ #import <React/RCTBridge+Private.h>
13
+ #import <React/RCTBridge.h>
14
+ #import <React/RCTFabricSurface.h>
15
+ #import <React/RCTSurfacePresenterStub.h>
16
+ #import <React/RCTSurfacePresenter.h>
17
+ #import <React/RCTSurfaceProtocol.h>
18
+
19
+ #import <React/RCTComponentViewProtocol.h>
20
+ #import <React/RCTComponentViewRegistry.h>
21
+ #import <React/RCTMountingManager.h>
22
+
23
+ using namespace facebook;
24
+ using namespace facebook::react;
25
+
26
+ namespace {
27
+ static NSMutableDictionary<NSNumber *, id<RCTSurfaceProtocol>> *externalSurfaces()
28
+ {
29
+ static NSMutableDictionary<NSNumber *, id<RCTSurfaceProtocol>> *surfaces = nil;
30
+ static dispatch_once_t onceToken;
31
+ dispatch_once(&onceToken, ^{
32
+ surfaces = [NSMutableDictionary new];
33
+ });
34
+ return surfaces;
35
+ }
36
+
37
+ inline RCTSurfacePresenter* _Nullable resolveSurfacePresenter(RCTBridge *bridge)
38
+ {
39
+ id<RCTSurfacePresenterStub> surfacePresenter =
40
+ (id<RCTSurfacePresenterStub>)[SurfacePresenterRegistry currentSurfacePresenter];
41
+ if (surfacePresenter != nil) {
42
+ return surfacePresenter;
43
+ }
44
+
45
+ return [bridge surfacePresenter];
46
+ }
47
+ } // namespace
48
+
49
+ @implementation SurfaceHelper
50
+
51
+ + (nullable NSNumber *)createExternalSurface:(NSError *__autoreleasing _Nullable * _Nullable)error
52
+ {
53
+ @try {
54
+ if (![NSThread isMainThread]) {
55
+ assignError(error, @"createExternalSurface() must run on the main thread.");
56
+ return nil;
57
+ }
58
+
59
+ RCTBridge *bridge = [RCTBridge currentBridge];
60
+ if (bridge == nil) {
61
+ assignError(error, @"Could not access RCTBridge.currentBridge.");
62
+ return nil;
63
+ }
64
+
65
+ RCTSurfacePresenter *surfacePresenter = resolveSurfacePresenter(bridge);
66
+ if (surfacePresenter == nil) {
67
+ assignError(error, @"Could not access an active RCTSurfacePresenter.");
68
+ return nil;
69
+ }
70
+
71
+ id<RCTSurfaceProtocol> surface = [surfacePresenter createFabricSurfaceForModuleName:@"" initialProperties:@{}];
72
+ if (surface == nil) {
73
+ assignError(error, @"Failed to create Fabric surface.");
74
+ return nil;
75
+ }
76
+
77
+ if (![surface isKindOfClass:[RCTFabricSurface class]]) {
78
+ assignError(error, @"Expected a RCTFabricSurface instance.");
79
+ return nil;
80
+ }
81
+
82
+ [surface start];
83
+ NSNumber *surfaceId = @(surface.rootTag);
84
+ externalSurfaces()[surfaceId] = surface;
85
+ NSLog(@"Created external surface %@", surfaceId);
86
+ return surfaceId;
87
+ } @catch (NSException *exception) {
88
+ assignError(error, [NSString stringWithFormat:@"Surface creation failed with NSException: %@", exception.reason]);
89
+ return nil;
90
+ }
91
+ }
92
+
93
+ + (BOOL)releaseExternalSurface:(ReactTag)surfaceId error:(NSError * _Nullable __autoreleasing * _Nullable)error {
94
+ @try {
95
+ if (![NSThread isMainThread]) {
96
+ assignError(error, @"releaseExternalSurface() must run on the main thread.");
97
+ return NO;
98
+ }
99
+
100
+ NSNumber *surfaceKey = @(surfaceId);
101
+ id<RCTSurfaceProtocol> surface = externalSurfaces()[surfaceKey];
102
+ if (surface == nil) {
103
+ return YES;
104
+ }
105
+
106
+ [surface stop];
107
+ [externalSurfaces() removeObjectForKey:surfaceKey];
108
+ return YES;
109
+ } @catch (NSException *exception) {
110
+ assignError(error, [NSString stringWithFormat:@"Surface release failed with NSException: %@", exception.reason]);
111
+ return NO;
112
+ }
113
+ }
114
+
115
+ + (nullable UIView *)getViewByTag:(ReactTag)tag error:(NSError * _Nullable __autoreleasing * _Nullable)error {
116
+ if (![NSThread isMainThread]) {
117
+ assignError(error, @"getViewByTag() must run on the main thread.");
118
+ return nil;
119
+ }
120
+
121
+ RCTBridge *bridge = [RCTBridge currentBridge];
122
+ if (bridge == nil) {
123
+ assignError(error, @"Could not access RCTBridge.currentBridge.");
124
+ return nil;
125
+ }
126
+
127
+ RCTSurfacePresenter *surfacePresenter = resolveSurfacePresenter(bridge);
128
+ if (surfacePresenter == nil) {
129
+ assignError(error, @"Could not access an active RCTSurfacePresenter.");
130
+ return nil;
131
+ }
132
+
133
+ RCTComponentViewRegistry *componentViewRegistry = surfacePresenter.mountingManager.componentViewRegistry;
134
+ Tag intTag = static_cast<Tag>(tag);
135
+ UIView<RCTComponentViewProtocol> *componentView = [componentViewRegistry findComponentViewWithTag:intTag];
136
+ if (componentView == nil) {
137
+ assignError(error, [NSString stringWithFormat:@"Could not resolve view with tag %d on surface %@", intTag, @-1]);
138
+ return nil;
139
+ }
140
+
141
+ return componentView;
142
+ }
143
+
144
+ @end
@@ -0,0 +1,17 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ #import <React/RCTBridgeModule.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ /**
8
+ * A module whos purpose it is to receive the surface presenter.
9
+ * (RN injects this during module instantiation).
10
+ */
11
+ @interface SurfacePresenterRegistry : NSObject <RCTBridgeModule>
12
+
13
+ + (nullable id)currentSurfacePresenter;
14
+
15
+ @end
16
+
17
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,31 @@
1
+ #import "SurfacePresenterRegistry.h"
2
+
3
+ #import <React/RCTSurfacePresenterStub.h>
4
+
5
+ @protocol RCTTurboModule;
6
+
7
+ static __weak id<RCTSurfacePresenterStub> sCurrentSurfacePresenter = nil;
8
+
9
+ @interface SurfacePresenterRegistry () <RCTTurboModule>
10
+ @end
11
+
12
+ @implementation SurfacePresenterRegistry
13
+
14
+ RCT_EXPORT_MODULE(HybridUiListSurfacePresenterRegistry);
15
+
16
+ + (BOOL)requiresMainQueueSetup
17
+ {
18
+ return NO;
19
+ }
20
+
21
+ + (nullable id)currentSurfacePresenter
22
+ {
23
+ return sCurrentSurfacePresenter;
24
+ }
25
+
26
+ - (void)setSurfacePresenter:(id<RCTSurfacePresenterStub>)surfacePresenter
27
+ {
28
+ sCurrentSurfacePresenter = surfacePresenter;
29
+ }
30
+
31
+ @end
@@ -0,0 +1,18 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+
6
+ @interface HybridWorkletsModuleProxyHolderBox : NSObject
7
+ @end
8
+
9
+ @interface TurboModuleInstaller : NSObject
10
+
11
+ + (nullable HybridWorkletsModuleProxyHolderBox *)createWorkletsModuleProxyHolder:(NSError * _Nullable * _Nullable)error;
12
+ + (BOOL)installNativeModuleProxyInUIRuntimeWithHolder:(HybridWorkletsModuleProxyHolderBox *)holder
13
+ error:(NSError * _Nullable * _Nullable)error;
14
+ + (BOOL)setupEventInterceptor:(NSError * _Nullable * _Nullable)error;
15
+
16
+ @end
17
+
18
+ NS_ASSUME_NONNULL_END