react-native-nitro-list 0.1.2 → 0.1.3

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 (160) hide show
  1. package/README.md +16 -44
  2. package/ios/HybridNitroList.swift +7 -5
  3. package/lib/commonjs/index.js +31 -3
  4. package/lib/commonjs/index.js.map +1 -1
  5. package/lib/commonjs/layout/MutableLinearLayout.js.map +1 -1
  6. package/lib/commonjs/native/NitroListView.js +20 -0
  7. package/lib/commonjs/native/NitroListView.js.map +1 -0
  8. package/lib/commonjs/recycler/CellPool.js +5 -18
  9. package/lib/commonjs/recycler/CellPool.js.map +1 -1
  10. package/lib/commonjs/recycler/{RecyclerList.js → NitroList.js} +91 -92
  11. package/lib/commonjs/recycler/NitroList.js.map +1 -0
  12. package/lib/commonjs/scroll/useScrollVelocity.js +20 -0
  13. package/lib/commonjs/scroll/useScrollVelocity.js.map +1 -0
  14. package/lib/commonjs/{native/NitroList.types.js → specs/NitroList.nitro.js} +1 -1
  15. package/lib/commonjs/{native/NitroList.types.js.map → specs/NitroList.nitro.js.map} +1 -1
  16. package/lib/commonjs/types/CellType.js +25 -0
  17. package/lib/commonjs/types/CellType.js.map +1 -1
  18. package/lib/commonjs/types/recycler/CellPool.js +42 -0
  19. package/lib/commonjs/types/recycler/CellPool.js.map +1 -0
  20. package/lib/commonjs/types/recycler/{RecyclerListProps.js → NitroListProps.js} +1 -1
  21. package/lib/commonjs/types/recycler/NitroListProps.js.map +1 -0
  22. package/lib/commonjs/types/recycler/{RecyclerCellInstance.js → RecyclerCell.js} +1 -1
  23. package/lib/commonjs/types/recycler/RecyclerCell.js.map +1 -0
  24. package/lib/commonjs/windowing/computeVisibleItemRange.js +22 -32
  25. package/lib/commonjs/windowing/computeVisibleItemRange.js.map +1 -1
  26. package/lib/module/index.js +5 -1
  27. package/lib/module/index.js.map +1 -1
  28. package/lib/module/layout/MutableLinearLayout.js.map +1 -1
  29. package/lib/module/native/NitroListView.js +17 -0
  30. package/lib/module/native/NitroListView.js.map +1 -0
  31. package/lib/module/recycler/CellPool.js +5 -18
  32. package/lib/module/recycler/CellPool.js.map +1 -1
  33. package/lib/module/recycler/NitroList.js +220 -0
  34. package/lib/module/recycler/NitroList.js.map +1 -0
  35. package/lib/module/scroll/useScrollVelocity.js +16 -0
  36. package/lib/module/scroll/useScrollVelocity.js.map +1 -0
  37. package/lib/module/specs/NitroList.nitro.js +4 -0
  38. package/lib/module/{native/NitroList.types.js.map → specs/NitroList.nitro.js.map} +1 -1
  39. package/lib/module/types/CellType.js +20 -0
  40. package/lib/module/types/CellType.js.map +1 -1
  41. package/lib/module/types/recycler/CellPool.js +37 -0
  42. package/lib/module/types/recycler/CellPool.js.map +1 -0
  43. package/lib/module/types/recycler/NitroListProps.js +4 -0
  44. package/lib/module/types/recycler/NitroListProps.js.map +1 -0
  45. package/lib/module/types/recycler/RecyclerCell.js +4 -0
  46. package/lib/module/types/recycler/RecyclerCell.js.map +1 -0
  47. package/lib/module/windowing/computeVisibleItemRange.js +22 -32
  48. package/lib/module/windowing/computeVisibleItemRange.js.map +1 -1
  49. package/lib/typescript/src/index.d.ts +3 -1
  50. package/lib/typescript/src/index.d.ts.map +1 -1
  51. package/lib/typescript/src/native/NitroListView.d.ts +20 -0
  52. package/lib/typescript/src/native/NitroListView.d.ts.map +1 -0
  53. package/lib/typescript/src/recycler/CellPool.d.ts +4 -7
  54. package/lib/typescript/src/recycler/CellPool.d.ts.map +1 -1
  55. package/lib/typescript/src/recycler/NitroList.d.ts +4 -0
  56. package/lib/typescript/src/recycler/NitroList.d.ts.map +1 -0
  57. package/lib/typescript/src/scroll/useScrollVelocity.d.ts +2 -0
  58. package/lib/typescript/src/scroll/useScrollVelocity.d.ts.map +1 -0
  59. package/lib/typescript/src/specs/NitroList.nitro.d.ts +26 -0
  60. package/lib/typescript/src/specs/NitroList.nitro.d.ts.map +1 -0
  61. package/lib/typescript/src/types/CellKey.d.ts +1 -9
  62. package/lib/typescript/src/types/CellKey.d.ts.map +1 -1
  63. package/lib/typescript/src/types/CellType.d.ts +12 -1
  64. package/lib/typescript/src/types/CellType.d.ts.map +1 -1
  65. package/lib/typescript/src/types/recycler/CellPool.d.ts +16 -0
  66. package/lib/typescript/src/types/recycler/CellPool.d.ts.map +1 -0
  67. package/lib/typescript/src/types/recycler/{RecyclerListProps.d.ts → NitroListProps.d.ts} +3 -3
  68. package/lib/typescript/src/types/recycler/NitroListProps.d.ts.map +1 -0
  69. package/lib/typescript/src/types/recycler/RecyclerCell.d.ts +12 -0
  70. package/lib/typescript/src/types/recycler/RecyclerCell.d.ts.map +1 -0
  71. package/lib/typescript/src/types/recycler/RecyclerItemRenderer.d.ts +6 -2
  72. package/lib/typescript/src/types/recycler/RecyclerItemRenderer.d.ts.map +1 -1
  73. package/lib/typescript/src/types/recycler/index.d.ts +2 -2
  74. package/lib/typescript/src/types/recycler/index.d.ts.map +1 -1
  75. package/lib/typescript/src/windowing/computeVisibleItemRange.d.ts +16 -12
  76. package/lib/typescript/src/windowing/computeVisibleItemRange.d.ts.map +1 -1
  77. package/nitro.json +5 -5
  78. package/nitrogen/generated/android/NitroList+autolinking.cmake +2 -2
  79. package/nitrogen/generated/android/NitroListOnLoad.cpp +2 -2
  80. package/nitrogen/generated/android/c++/{JHybridNitroLayoutEngineSpec.cpp → JHybridNitroListSpec.cpp} +15 -15
  81. package/nitrogen/generated/android/c++/{JHybridNitroLayoutEngineSpec.hpp → JHybridNitroListSpec.hpp} +11 -11
  82. package/nitrogen/generated/android/c++/{JLayoutRect.hpp → JItemLayout.hpp} +10 -10
  83. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolist/{HybridNitroLayoutEngineSpec.kt → HybridNitroListSpec.kt} +7 -7
  84. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolist/{LayoutRect.kt → ItemLayout.kt} +5 -5
  85. package/nitrogen/generated/ios/NitroList-Swift-Cxx-Bridge.cpp +9 -9
  86. package/nitrogen/generated/ios/NitroList-Swift-Cxx-Bridge.hpp +27 -27
  87. package/nitrogen/generated/ios/NitroList-Swift-Cxx-Umbrella.hpp +8 -8
  88. package/nitrogen/generated/ios/c++/{HybridNitroLayoutEngineSpecSwift.cpp → HybridNitroListSpecSwift.cpp} +2 -2
  89. package/nitrogen/generated/ios/c++/{HybridNitroLayoutEngineSpecSwift.hpp → HybridNitroListSpecSwift.hpp} +16 -16
  90. package/nitrogen/generated/ios/swift/HybridNitroListSpec.swift +56 -0
  91. package/nitrogen/generated/ios/swift/{HybridNitroLayoutEngineSpec_cxx.swift → HybridNitroListSpec_cxx.swift} +23 -23
  92. package/nitrogen/generated/ios/swift/{LayoutRect.swift → ItemLayout.swift} +5 -5
  93. package/nitrogen/generated/shared/c++/{HybridNitroLayoutEngineSpec.cpp → HybridNitroListSpec.cpp} +4 -4
  94. package/nitrogen/generated/shared/c++/{HybridNitroLayoutEngineSpec.hpp → HybridNitroListSpec.hpp} +13 -13
  95. package/nitrogen/generated/shared/c++/{LayoutRect.hpp → ItemLayout.hpp} +11 -11
  96. package/package.json +5 -5
  97. package/src/index.ts +7 -1
  98. package/src/layout/MutableLinearLayout.ts +1 -1
  99. package/src/native/NitroListView.ts +22 -0
  100. package/src/recycler/CellPool.ts +10 -24
  101. package/src/recycler/NitroList.tsx +317 -0
  102. package/src/scroll/useScrollVelocity.ts +16 -0
  103. package/src/specs/NitroList.nitro.ts +29 -0
  104. package/src/types/CellKey.ts +2 -9
  105. package/src/types/CellType.ts +8 -9
  106. package/src/types/recycler/CellPool.ts +45 -0
  107. package/src/types/recycler/{RecyclerListProps.ts → NitroListProps.ts} +2 -2
  108. package/src/types/recycler/RecyclerCell.ts +12 -0
  109. package/src/types/recycler/RecyclerItemRenderer.ts +6 -2
  110. package/src/types/recycler/index.ts +2 -2
  111. package/src/windowing/computeVisibleItemRange.ts +42 -38
  112. package/lib/commonjs/NitroList.js +0 -9
  113. package/lib/commonjs/NitroList.js.map +0 -1
  114. package/lib/commonjs/native/NitroLayoutEngine.js +0 -9
  115. package/lib/commonjs/native/NitroLayoutEngine.js.map +0 -1
  116. package/lib/commonjs/native/NitroRecyclerView.js +0 -9
  117. package/lib/commonjs/native/NitroRecyclerView.js.map +0 -1
  118. package/lib/commonjs/recycler/RecyclerList.js.map +0 -1
  119. package/lib/commonjs/specs/nitro-layout-engine.nitro.js +0 -6
  120. package/lib/commonjs/specs/nitro-layout-engine.nitro.js.map +0 -1
  121. package/lib/commonjs/types/recycler/RecyclerCellInstance.js.map +0 -1
  122. package/lib/commonjs/types/recycler/RecyclerListProps.js.map +0 -1
  123. package/lib/module/NitroList.js +0 -5
  124. package/lib/module/NitroList.js.map +0 -1
  125. package/lib/module/native/NitroLayoutEngine.js +0 -5
  126. package/lib/module/native/NitroLayoutEngine.js.map +0 -1
  127. package/lib/module/native/NitroList.types.js +0 -4
  128. package/lib/module/native/NitroRecyclerView.js +0 -5
  129. package/lib/module/native/NitroRecyclerView.js.map +0 -1
  130. package/lib/module/recycler/RecyclerList.js +0 -221
  131. package/lib/module/recycler/RecyclerList.js.map +0 -1
  132. package/lib/module/specs/nitro-layout-engine.nitro.js +0 -4
  133. package/lib/module/specs/nitro-layout-engine.nitro.js.map +0 -1
  134. package/lib/module/types/recycler/RecyclerCellInstance.js +0 -4
  135. package/lib/module/types/recycler/RecyclerCellInstance.js.map +0 -1
  136. package/lib/module/types/recycler/RecyclerListProps.js +0 -4
  137. package/lib/module/types/recycler/RecyclerListProps.js.map +0 -1
  138. package/lib/typescript/src/NitroList.d.ts +0 -5
  139. package/lib/typescript/src/NitroList.d.ts.map +0 -1
  140. package/lib/typescript/src/native/NitroLayoutEngine.d.ts +0 -3
  141. package/lib/typescript/src/native/NitroLayoutEngine.d.ts.map +0 -1
  142. package/lib/typescript/src/native/NitroList.types.d.ts +0 -9
  143. package/lib/typescript/src/native/NitroList.types.d.ts.map +0 -1
  144. package/lib/typescript/src/native/NitroRecyclerView.d.ts +0 -5
  145. package/lib/typescript/src/native/NitroRecyclerView.d.ts.map +0 -1
  146. package/lib/typescript/src/recycler/RecyclerList.d.ts +0 -4
  147. package/lib/typescript/src/recycler/RecyclerList.d.ts.map +0 -1
  148. package/lib/typescript/src/specs/nitro-layout-engine.nitro.d.ts +0 -14
  149. package/lib/typescript/src/specs/nitro-layout-engine.nitro.d.ts.map +0 -1
  150. package/lib/typescript/src/types/recycler/RecyclerCellInstance.d.ts +0 -37
  151. package/lib/typescript/src/types/recycler/RecyclerCellInstance.d.ts.map +0 -1
  152. package/lib/typescript/src/types/recycler/RecyclerListProps.d.ts.map +0 -1
  153. package/nitrogen/generated/ios/swift/HybridNitroLayoutEngineSpec.swift +0 -56
  154. package/src/NitroList.ts +0 -8
  155. package/src/native/NitroLayoutEngine.ts +0 -7
  156. package/src/native/NitroList.types.ts +0 -13
  157. package/src/native/NitroRecyclerView.ts +0 -8
  158. package/src/recycler/RecyclerList.tsx +0 -304
  159. package/src/specs/nitro-layout-engine.nitro.ts +0 -17
  160. package/src/types/recycler/RecyclerCellInstance.ts +0 -40
@@ -1,5 +1,5 @@
1
1
  ///
2
- /// HybridNitroLayoutEngineSpec.hpp
2
+ /// HybridNitroListSpec.hpp
3
3
  /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
4
  /// https://github.com/mrousavy/nitro
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
@@ -13,10 +13,10 @@
13
13
  #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
14
  #endif
15
15
 
16
- // Forward declaration of `LayoutRect` to properly resolve imports.
17
- namespace margelo::nitro::nitrolist { struct LayoutRect; }
16
+ // Forward declaration of `ItemLayout` to properly resolve imports.
17
+ namespace margelo::nitro::nitrolist { struct ItemLayout; }
18
18
 
19
- #include "LayoutRect.hpp"
19
+ #include "ItemLayout.hpp"
20
20
  #include <vector>
21
21
 
22
22
  namespace margelo::nitro::nitrolist {
@@ -24,25 +24,25 @@ namespace margelo::nitro::nitrolist {
24
24
  using namespace margelo::nitro;
25
25
 
26
26
  /**
27
- * An abstract base class for `NitroLayoutEngine`
28
- * Inherit this class to create instances of `HybridNitroLayoutEngineSpec` in C++.
27
+ * An abstract base class for `NitroList`
28
+ * Inherit this class to create instances of `HybridNitroListSpec` in C++.
29
29
  * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual.
30
30
  * @example
31
31
  * ```cpp
32
- * class HybridNitroLayoutEngine: public HybridNitroLayoutEngineSpec {
32
+ * class HybridNitroList: public HybridNitroListSpec {
33
33
  * public:
34
- * HybridNitroLayoutEngine(...): HybridObject(TAG) { ... }
34
+ * HybridNitroList(...): HybridObject(TAG) { ... }
35
35
  * // ...
36
36
  * };
37
37
  * ```
38
38
  */
39
- class HybridNitroLayoutEngineSpec: public virtual HybridObject {
39
+ class HybridNitroListSpec: public virtual HybridObject {
40
40
  public:
41
41
  // Constructor
42
- explicit HybridNitroLayoutEngineSpec(): HybridObject(TAG) { }
42
+ explicit HybridNitroListSpec(): HybridObject(TAG) { }
43
43
 
44
44
  // Destructor
45
- ~HybridNitroLayoutEngineSpec() override = default;
45
+ ~HybridNitroListSpec() override = default;
46
46
 
47
47
  public:
48
48
  // Properties
@@ -50,7 +50,7 @@ namespace margelo::nitro::nitrolist {
50
50
 
51
51
  public:
52
52
  // Methods
53
- virtual std::vector<LayoutRect> computeLayout(double containerWidth, const std::vector<double>& itemHeights) = 0;
53
+ virtual std::vector<ItemLayout> computeLayout(double containerWidth, const std::vector<double>& itemHeights) = 0;
54
54
 
55
55
  protected:
56
56
  // Hybrid Setup
@@ -58,7 +58,7 @@ namespace margelo::nitro::nitrolist {
58
58
 
59
59
  protected:
60
60
  // Tag for logging
61
- static constexpr auto TAG = "NitroLayoutEngine";
61
+ static constexpr auto TAG = "NitroList";
62
62
  };
63
63
 
64
64
  } // namespace margelo::nitro::nitrolist
@@ -1,5 +1,5 @@
1
1
  ///
2
- /// LayoutRect.hpp
2
+ /// ItemLayout.hpp
3
3
  /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
4
  /// https://github.com/mrousavy/nitro
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
@@ -35,9 +35,9 @@
35
35
  namespace margelo::nitro::nitrolist {
36
36
 
37
37
  /**
38
- * A struct which can be represented as a JavaScript object (LayoutRect).
38
+ * A struct which can be represented as a JavaScript object (ItemLayout).
39
39
  */
40
- struct LayoutRect final {
40
+ struct ItemLayout final {
41
41
  public:
42
42
  double x SWIFT_PRIVATE;
43
43
  double y SWIFT_PRIVATE;
@@ -45,30 +45,30 @@ namespace margelo::nitro::nitrolist {
45
45
  double height SWIFT_PRIVATE;
46
46
 
47
47
  public:
48
- LayoutRect() = default;
49
- explicit LayoutRect(double x, double y, double width, double height): x(x), y(y), width(width), height(height) {}
48
+ ItemLayout() = default;
49
+ explicit ItemLayout(double x, double y, double width, double height): x(x), y(y), width(width), height(height) {}
50
50
 
51
51
  public:
52
- friend bool operator==(const LayoutRect& lhs, const LayoutRect& rhs) = default;
52
+ friend bool operator==(const ItemLayout& lhs, const ItemLayout& rhs) = default;
53
53
  };
54
54
 
55
55
  } // namespace margelo::nitro::nitrolist
56
56
 
57
57
  namespace margelo::nitro {
58
58
 
59
- // C++ LayoutRect <> JS LayoutRect (object)
59
+ // C++ ItemLayout <> JS ItemLayout (object)
60
60
  template <>
61
- struct JSIConverter<margelo::nitro::nitrolist::LayoutRect> final {
62
- static inline margelo::nitro::nitrolist::LayoutRect fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
61
+ struct JSIConverter<margelo::nitro::nitrolist::ItemLayout> final {
62
+ static inline margelo::nitro::nitrolist::ItemLayout fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
63
63
  jsi::Object obj = arg.asObject(runtime);
64
- return margelo::nitro::nitrolist::LayoutRect(
64
+ return margelo::nitro::nitrolist::ItemLayout(
65
65
  JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "x"))),
66
66
  JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "y"))),
67
67
  JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width"))),
68
68
  JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height")))
69
69
  );
70
70
  }
71
- static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitrolist::LayoutRect& arg) {
71
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitrolist::ItemLayout& arg) {
72
72
  jsi::Object obj(runtime);
73
73
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "x"), JSIConverter<double>::toJSI(runtime, arg.x));
74
74
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "y"), JSIConverter<double>::toJSI(runtime, arg.y));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-nitro-list",
3
- "version": "0.1.2",
4
- "description": "react-native-nitro-list is a react native package built with Nitro",
3
+ "version": "0.1.3",
4
+ "description": "High-performance list for React Native powered by Nitro",
5
5
  "main": "./lib/commonjs/index.js",
6
6
  "module": "./lib/module/index.js",
7
7
  "types": "./lib/typescript/src/index.d.ts",
@@ -45,11 +45,11 @@
45
45
  "workspaces": [
46
46
  "example"
47
47
  ],
48
- "repository": "https://github.com/shivshankartiwari/react-native-nitro-list.git",
48
+ "repository": "https://github.com/pythonsst/nitro-list",
49
49
  "author": "Shiv Shankar Tiwari",
50
50
  "license": "MIT",
51
- "bugs": "https://github.com/shivshankartiwari/react-native-nitro-list/issues",
52
- "homepage": "https://github.com/shivshankartiwari/react-native-nitro-list#readme",
51
+ "bugs": "https://github.com/pythonsst/nitro-list/issues",
52
+ "homepage": "https://github.com/pythonsst/nitro-list#readme",
53
53
  "publishConfig": {
54
54
  "access": "public",
55
55
  "registry": "https://registry.npmjs.org/"
package/src/index.ts CHANGED
@@ -1 +1,7 @@
1
- export { RecyclerList } from './recycler/RecyclerList'
1
+
2
+ // Export the Native View
3
+ export * from './native/NitroListView';
4
+ // Export the JSI Specs
5
+ export * from './specs/NitroList.nitro';
6
+
7
+ export { default as NitroList } from './recycler/NitroList';
@@ -1,9 +1,9 @@
1
1
  import type { Axis } from '../types/Axis'
2
2
  import type { LinearLayoutInput } from '../types/layout'
3
3
  import type { LayoutRect } from '../types/layout/LayoutRect'
4
-
5
4
  import { DEFAULT_ITEM_SPACING } from './constants/layoutDefaults'
6
5
 
6
+
7
7
  /**
8
8
  * Mutable, deterministic linear layout engine.
9
9
  *
@@ -0,0 +1,22 @@
1
+ import { requireNativeComponent, type ViewProps } from 'react-native'
2
+
3
+ /**
4
+ * Props for the NitroListView.
5
+ * * We extend ViewProps so that developers can use standard styles
6
+ * like 'flex: 1' or 'backgroundColor' on the list.
7
+ */
8
+ export type NitroListViewProps = ViewProps & {
9
+ /**
10
+ * The total number of items to be displayed in the list.
11
+ * This is passed to the native side (Swift/Kotlin) for layout math.
12
+ */
13
+ itemCount: number
14
+ }
15
+
16
+ /**
17
+ * The Native Component that maps to the physical view in Swift/Kotlin.
18
+ * * 'NitroListView' is the "Secret Handshake" name. It must match
19
+ * the name exported by your Native ViewManagers.
20
+ */
21
+ export const NitroListView =
22
+ requireNativeComponent<NitroListViewProps>('NitroListView')
@@ -1,47 +1,33 @@
1
+ import type { RecyclerCell } from '../types/recycler/RecyclerCell'
1
2
  import type { CellType } from '../types/CellType'
2
- import type { RecyclerCellInstance } from '../types/recycler'
3
3
 
4
- export class CellPool {
5
- private readonly pools = new Map<CellType, RecyclerCellInstance[]>()
4
+ export class CellPool<T extends RecyclerCell<any>> {
5
+ private readonly pools = new Map<CellType, T[]>()
6
6
  private readonly maxPerType = new Map<CellType, number>()
7
7
 
8
- /** ✅ NEW: check if a type is registered */
9
8
  hasType(type: CellType): boolean {
10
9
  return this.pools.has(type)
11
10
  }
12
11
 
13
12
  registerType(type: CellType, maxCount: number): void {
14
- if (this.maxPerType.has(type)) return
15
- this.maxPerType.set(type, maxCount)
13
+ if (this.pools.has(type)) return
16
14
  this.pools.set(type, [])
15
+ this.maxPerType.set(type, maxCount)
17
16
  }
18
17
 
19
- acquire(type: CellType): RecyclerCellInstance | null {
18
+ acquire(type: CellType): T | null {
20
19
  const bucket = this.pools.get(type)
21
20
  if (!bucket || bucket.length === 0) return null
22
- return bucket.pop()!
21
+ return bucket.pop() ?? null
23
22
  }
24
23
 
25
- release(cell: RecyclerCellInstance): void {
26
- const { type } = cell
27
- const bucket = this.pools.get(type)
28
- const max = this.maxPerType.get(type)
29
-
24
+ release(cell: T): void {
25
+ const bucket = this.pools.get(cell.type)
26
+ const max = this.maxPerType.get(cell.type)
30
27
  if (!bucket || max === undefined) return
31
28
 
32
29
  cell.index = -1
33
-
34
30
  if (bucket.length >= max) return
35
31
  bucket.push(cell)
36
32
  }
37
-
38
- clear(): void {
39
- for (const bucket of this.pools.values()) {
40
- bucket.length = 0
41
- }
42
- }
43
-
44
- getPoolSize(type: CellType): number {
45
- return this.pools.get(type)?.length ?? 0
46
- }
47
33
  }
@@ -0,0 +1,317 @@
1
+ import React, { useRef, useEffect, useState } from 'react'
2
+ import {
3
+ ScrollView,
4
+ View,
5
+ StyleSheet,
6
+ type NativeSyntheticEvent,
7
+ type NativeScrollEvent,
8
+ type LayoutChangeEvent,
9
+ } from 'react-native'
10
+
11
+ import type { LayoutRect, VisibleRange } from '../types'
12
+ import type { RecyclerCell, NitroListProps } from '../types/recycler'
13
+ import type { CellType } from '../types/CellType'
14
+ import type { CellKey } from '../types/CellKey'
15
+
16
+ import { MutableLinearLayout } from '../layout/MutableLinearLayout'
17
+ import { computeVisibleItemRange } from '../windowing'
18
+ import { CellPool } from './CellPool'
19
+
20
+ /* ========================================================= */
21
+
22
+ const DEFAULT_VIEWPORT_SIZE = 800
23
+ const DEFAULT_ITEM_SIZE = 60
24
+ const SCROLL_EVENT_THROTTLE = 16
25
+
26
+ /* ========================================================= */
27
+ /* Internal (mutable) cell */
28
+ /* ========================================================= */
29
+
30
+ interface InternalRecyclerCell extends RecyclerCell<CellKey> {
31
+ active: boolean
32
+ }
33
+
34
+ /* ========================================================= */
35
+ /* NitroList */
36
+ /* ========================================================= */
37
+
38
+ export default function NitroList<T>(
39
+ props: NitroListProps<T>
40
+ ): React.ReactElement {
41
+ const {
42
+ data,
43
+ renderItem,
44
+ getCellType,
45
+ scrollDirection = 'vertical',
46
+ bufferRatio = 1.3,
47
+ containerCrossAxisSize,
48
+ itemMainAxisSizes,
49
+ padding = { start: 0, end: 0 },
50
+ itemSpacing,
51
+ } = props
52
+
53
+ const isVertical = scrollDirection === 'vertical'
54
+ const [, forceRender] = useState(0)
55
+
56
+ const DEBUG = true
57
+
58
+
59
+ /* ======================================================= */
60
+ /* Layout engine (recomputed when signature changes) */
61
+ /* ======================================================= */
62
+
63
+ const layoutEngineRef = useRef<MutableLinearLayout | null>(null)
64
+ const layoutSignatureRef = useRef<string>('')
65
+
66
+ const layoutSignature = [
67
+ scrollDirection,
68
+ containerCrossAxisSize,
69
+ data.length,
70
+ itemMainAxisSizes,
71
+ padding.start,
72
+ padding.end,
73
+ itemSpacing ?? 'none',
74
+ ].join('|')
75
+
76
+ if (
77
+ layoutEngineRef.current === null ||
78
+ layoutSignatureRef.current !== layoutSignature
79
+ ) {
80
+ const engine = new MutableLinearLayout(scrollDirection)
81
+ engine.compute({
82
+ crossAxisSize: containerCrossAxisSize,
83
+ itemMainAxisSizes,
84
+ padding,
85
+ itemSpacing,
86
+ })
87
+
88
+ layoutEngineRef.current = engine
89
+ layoutSignatureRef.current = layoutSignature
90
+ }
91
+
92
+ const layouts: readonly LayoutRect[] =
93
+ layoutEngineRef.current.getLayouts()
94
+
95
+ const contentSize: number =
96
+ layoutEngineRef.current.getContentSize()
97
+
98
+ /* ======================================================= */
99
+ /* Scroll metrics */
100
+ /* ======================================================= */
101
+
102
+ const scrollOffsetRef = useRef<number>(0)
103
+ const viewportSizeRef = useRef<number>(DEFAULT_VIEWPORT_SIZE)
104
+
105
+ /* ======================================================= */
106
+ /* Cell pool */
107
+ /* ======================================================= */
108
+
109
+ const cellPoolRef = useRef<CellPool<InternalRecyclerCell>>(
110
+ new CellPool<InternalRecyclerCell>()
111
+ )
112
+
113
+ const activeCellsRef = useRef<InternalRecyclerCell[]>([])
114
+ const nextKeyRef = useRef<number>(0)
115
+
116
+ const createCell = (type: CellType): InternalRecyclerCell => ({
117
+ key: nextKeyRef.current++,
118
+ type,
119
+ index: -1,
120
+ active: false,
121
+ })
122
+
123
+ /* ======================================================= */
124
+ /* Visible window coordination */
125
+ /* ======================================================= */
126
+
127
+ const updateVisibleCells = (): void => {
128
+ const range: VisibleRange | null =
129
+ computeVisibleItemRange({
130
+ layouts,
131
+ offset: scrollOffsetRef.current,
132
+ viewportSize: viewportSizeRef.current,
133
+ buffer: viewportSizeRef.current * bufferRatio,
134
+ isVertical,
135
+ })
136
+
137
+
138
+
139
+ if (range === null) return
140
+
141
+ if (DEBUG) {
142
+ console.log(
143
+ '[NitroList] range:',
144
+ range.startIndex,
145
+ range.endIndex
146
+ )
147
+ }
148
+
149
+
150
+ const active = activeCellsRef.current
151
+
152
+ // 1. Mark all inactive
153
+ for (const cell of active) {
154
+ cell.active = false
155
+ }
156
+
157
+ let writeIndex = 0
158
+
159
+ // 2. Activate visible range
160
+ for (
161
+ let index = range.startIndex;
162
+ index <= range.endIndex;
163
+ index++
164
+ ) {
165
+ const item = data[index]
166
+ if (item === undefined) continue
167
+
168
+ const type = getCellType(item, index)
169
+
170
+ // Seed pool once per type
171
+ if (!cellPoolRef.current.hasType(type)) {
172
+ const maxCells =
173
+ Math.ceil(
174
+ (viewportSizeRef.current / DEFAULT_ITEM_SIZE) *
175
+ bufferRatio
176
+ ) + 2
177
+
178
+ cellPoolRef.current.registerType(type, maxCells)
179
+ for (let i = 0; i < maxCells; i++) {
180
+ cellPoolRef.current.release(createCell(type))
181
+ }
182
+ }
183
+
184
+ let cell: InternalRecyclerCell | null =
185
+ cellPoolRef.current.acquire(type)
186
+
187
+ if (cell === null) {
188
+ const reusable = active.find(
189
+ c => !c.active && c.type === type
190
+ )
191
+
192
+ cell = reusable ?? createCell(type)
193
+ }
194
+
195
+
196
+ cell.index = index
197
+ cell.active = true
198
+ active[writeIndex++] = cell
199
+ }
200
+
201
+ // 3. Recycle unused cells
202
+ for (let i = writeIndex; i < active.length; i++) {
203
+ const cell = active[i]
204
+ if (cell === undefined) continue
205
+
206
+ cell.index = -1
207
+ cell.active = false
208
+ cellPoolRef.current.release(cell)
209
+ }
210
+
211
+ // 4. Compact once
212
+ if (active.length !== writeIndex) {
213
+ active.length = writeIndex
214
+ forceRender(v => v + 1)
215
+ }
216
+ }
217
+
218
+ /* ======================================================= */
219
+ /* Handlers */
220
+ /* ======================================================= */
221
+
222
+ const onScroll = (
223
+ e: NativeSyntheticEvent<NativeScrollEvent>
224
+ ): void => {
225
+ scrollOffsetRef.current = isVertical
226
+ ? e.nativeEvent.contentOffset.y
227
+ : e.nativeEvent.contentOffset.x
228
+
229
+ updateVisibleCells()
230
+ }
231
+
232
+ const onLayout = (e: LayoutChangeEvent): void => {
233
+ viewportSizeRef.current = isVertical
234
+ ? e.nativeEvent.layout.height
235
+ : e.nativeEvent.layout.width
236
+
237
+ updateVisibleCells()
238
+ }
239
+
240
+ useEffect(() => {
241
+ updateVisibleCells()
242
+ // eslint-disable-next-line react-hooks/exhaustive-deps
243
+ }, [])
244
+
245
+
246
+ if (DEBUG) {
247
+ console.log('[NitroList] layouts:', layouts.length)
248
+ console.log('[NitroList] contentSize:', contentSize)
249
+ console.log('[NitroList] data.length:', data.length)
250
+ }
251
+
252
+ /* ======================================================= */
253
+ /* Render */
254
+ /* ======================================================= */
255
+
256
+ return (
257
+ <ScrollView
258
+ onScroll={onScroll}
259
+ onLayout={onLayout}
260
+ horizontal={!isVertical}
261
+ scrollEventThrottle={SCROLL_EVENT_THROTTLE}
262
+ removeClippedSubviews
263
+ >
264
+ {/* REQUIRED positioning container */}
265
+ <View
266
+ style={
267
+ isVertical
268
+ ? { height: contentSize }
269
+ : { width: contentSize }
270
+ }
271
+ >
272
+ {activeCellsRef.current.map(cell => {
273
+ const layout = layouts[cell.index]
274
+ const item = data[cell.index]
275
+
276
+ if (layout === undefined || item === undefined) {
277
+ return null
278
+ }
279
+
280
+ return (
281
+ <View
282
+ key={cell.key}
283
+ style={[
284
+ styles.cell,
285
+ isVertical
286
+ ? {
287
+ top: layout.y,
288
+ height: layout.height,
289
+ width: layout.width,
290
+ }
291
+ : {
292
+ left: layout.x,
293
+ width: layout.width,
294
+ height: layout.height,
295
+ },
296
+ ]}
297
+ >
298
+ {renderItem({
299
+ item,
300
+ index: cell.index,
301
+ cell,
302
+ })}
303
+ </View>
304
+ )
305
+ })}
306
+ </View>
307
+ </ScrollView>
308
+ )
309
+ }
310
+
311
+ /* ========================================================= */
312
+
313
+ const styles = StyleSheet.create({
314
+ cell: {
315
+ position: 'absolute',
316
+ },
317
+ })
@@ -0,0 +1,16 @@
1
+ export function createScrollVelocityTracker() {
2
+ let lastOffset = 0
3
+ let lastTime = Date.now()
4
+
5
+ return function getVelocity(offset: number): number {
6
+ const now = Date.now()
7
+ const dt = now - lastTime
8
+ const dy = Math.abs(offset - lastOffset)
9
+
10
+ lastOffset = offset
11
+ lastTime = now
12
+
13
+ if (dt === 0) return 0
14
+ return dy / dt // px per ms
15
+ }
16
+ }
@@ -0,0 +1,29 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules'
2
+
3
+ /**
4
+ * Represents the physical area an item occupies on the screen.
5
+ * Standard: 'Layout' suffix tells the user this is a data object, not a logic object.
6
+ */
7
+ export interface ItemLayout {
8
+ readonly x: number
9
+ readonly y: number
10
+ readonly width: number
11
+ readonly height: number
12
+ }
13
+
14
+ /**
15
+ * The high-performance layout engine for NitroList.
16
+ * This interface will be used by Nitro to generate your Swift and Kotlin code.
17
+ */
18
+ export interface NitroList
19
+ extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
20
+
21
+ /**
22
+ * Calculates exactly where every item should sit on the screen.
23
+ * Standard: Use descriptive parameter names so developers know what to provide.
24
+ */
25
+ computeLayout(
26
+ containerWidth: number,
27
+ itemHeights: readonly number[]
28
+ ): readonly ItemLayout[]
29
+ }
@@ -1,9 +1,2 @@
1
- /**
2
- * Stable identifier for a cell.
3
- *
4
- * Cross-platform equivalent:
5
- * - Flutter: Key
6
- * - Android: stableId
7
- * - iOS: reuseIdentifier (logical)
8
- */
9
- export type CellKey = string | number
1
+ // CellKey.ts
2
+ export type CellKey = number
@@ -1,10 +1,9 @@
1
- /**
2
- * Logical cell type.
3
- *
4
- * Used to group compatible cells for recycling.
5
- *
6
- * Cross-platform equivalent:
7
- * - Android: viewType
8
- * - iOS: reuseIdentifier
1
+ /** * CellType is just a string, but we give it a name
2
+ * so the code is easier to read.
9
3
  */
10
- export type CellType = string | number
4
+ export type CellType = string;
5
+
6
+ /** * This is an "Identity" function. It doesn't do anything
7
+ * to the computer, but it helps the human reading the code.
8
+ */
9
+ export const cellType = (name: string): CellType => name;