react-native-nitro-list 0.0.1 → 0.1.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.
- package/README.md +250 -16
- package/android/build.gradle +5 -4
- package/android/src/main/java/com/nitrolist/HybridNitroRecyclerView.kt +26 -0
- package/android/src/main/java/com/nitrolist/HybridNitroRecyclerViewManager.kt +29 -0
- package/android/src/main/java/com/nitrolist/NitroListPackage.kt +13 -24
- package/android/src/main/java/com/nitrolist/NitroRecyclerAdapter.kt +32 -0
- package/android/src/main/java/com/nitrolist/NitroRecyclerViewHolder.kt +8 -0
- package/ios/HybridNitroList.swift +28 -15
- package/lib/commonjs/NitroList.js +9 -0
- package/lib/commonjs/NitroList.js.map +1 -0
- package/lib/commonjs/ReusableView.js +2 -0
- package/lib/commonjs/ReusableView.js.map +1 -0
- package/lib/commonjs/cell/Cell.js +2 -0
- package/lib/{module/specs/nitro-list.nitro.js.map → commonjs/cell/Cell.js.map} +1 -1
- package/lib/commonjs/cell/CellKeyGenerator.js +21 -0
- package/lib/commonjs/cell/CellKeyGenerator.js.map +1 -0
- package/lib/commonjs/cell/CellRecycler.js +70 -0
- package/lib/commonjs/cell/CellRecycler.js.map +1 -0
- package/lib/commonjs/cell/createCell.js +21 -0
- package/lib/commonjs/cell/createCell.js.map +1 -0
- package/lib/commonjs/cell/index.js +20 -0
- package/lib/commonjs/cell/index.js.map +1 -0
- package/lib/commonjs/debug/useDebugOverlay.js +15 -0
- package/lib/commonjs/debug/useDebugOverlay.js.map +1 -0
- package/lib/commonjs/getVisibleIndices.js +38 -0
- package/lib/commonjs/getVisibleIndices.js.map +1 -0
- package/lib/commonjs/hooks/usePersistentCallback.js +36 -0
- package/lib/commonjs/hooks/usePersistentCallback.js.map +1 -0
- package/lib/commonjs/index.js +7 -5
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/layout/MutableLinearLayout.js +104 -0
- package/lib/commonjs/layout/MutableLinearLayout.js.map +1 -0
- package/lib/commonjs/layout/constants/layoutDefaults.js +19 -0
- package/lib/commonjs/layout/constants/layoutDefaults.js.map +1 -0
- package/lib/commonjs/layout/index.js +2 -0
- package/lib/commonjs/{specs/nitro-list.nitro.js.map → layout/index.js.map} +1 -1
- package/lib/commonjs/measurement/MeasureLayout.js +20 -0
- package/lib/commonjs/measurement/MeasureLayout.js.map +1 -0
- package/lib/commonjs/measurement/useItemMeasurement.js +28 -0
- package/lib/commonjs/measurement/useItemMeasurement.js.map +1 -0
- package/lib/commonjs/native/NitroLayoutEngine.js +9 -0
- package/lib/commonjs/native/NitroLayoutEngine.js.map +1 -0
- package/lib/commonjs/{specs/nitro-list.nitro.js → native/NitroList.types.js} +1 -1
- package/lib/commonjs/native/NitroList.types.js.map +1 -0
- package/lib/commonjs/native/NitroRecyclerView.js +9 -0
- package/lib/commonjs/native/NitroRecyclerView.js.map +1 -0
- package/lib/commonjs/prefetch/PrefetchHelper.js +39 -0
- package/lib/commonjs/prefetch/PrefetchHelper.js.map +1 -0
- package/lib/commonjs/recycler/CellPool.js +46 -0
- package/lib/commonjs/recycler/CellPool.js.map +1 -0
- package/lib/commonjs/recycler/RecyclerList.js +224 -0
- package/lib/commonjs/recycler/RecyclerList.js.map +1 -0
- package/lib/commonjs/specs/nitro-layout-engine.nitro.js +6 -0
- package/lib/commonjs/specs/nitro-layout-engine.nitro.js.map +1 -0
- package/lib/commonjs/types/Axis.js +2 -0
- package/lib/commonjs/types/Axis.js.map +1 -0
- package/lib/commonjs/types/CellKey.js +2 -0
- package/lib/commonjs/types/CellKey.js.map +1 -0
- package/lib/commonjs/types/CellType.js +2 -0
- package/lib/commonjs/types/CellType.js.map +1 -0
- package/lib/commonjs/types/VisibleRange.js +2 -0
- package/lib/commonjs/types/VisibleRange.js.map +1 -0
- package/lib/commonjs/types/VisibleRangeInput.js +6 -0
- package/lib/commonjs/types/VisibleRangeInput.js.map +1 -0
- package/lib/commonjs/types/index.js +13 -0
- package/lib/commonjs/types/index.js.map +1 -0
- package/lib/commonjs/types/layout/LayoutRect.js +2 -0
- package/lib/commonjs/types/layout/LayoutRect.js.map +1 -0
- package/lib/commonjs/types/layout/LinearLayoutInput.js +6 -0
- package/lib/commonjs/types/layout/LinearLayoutInput.js.map +1 -0
- package/lib/commonjs/types/layout/MainAxisPadding.js +26 -0
- package/lib/commonjs/types/layout/MainAxisPadding.js.map +1 -0
- package/lib/commonjs/types/layout/index.js +2 -0
- package/lib/commonjs/types/layout/index.js.map +1 -0
- package/lib/commonjs/types/recycler/RecyclerCellInstance.js +6 -0
- package/lib/commonjs/types/recycler/RecyclerCellInstance.js.map +1 -0
- package/lib/commonjs/types/recycler/RecyclerItemRenderer.js +6 -0
- package/lib/commonjs/types/recycler/RecyclerItemRenderer.js.map +1 -0
- package/lib/commonjs/types/recycler/RecyclerListProps.js +6 -0
- package/lib/commonjs/types/recycler/RecyclerListProps.js.map +1 -0
- package/lib/commonjs/types/recycler/index.js +2 -0
- package/lib/commonjs/types/recycler/index.js.map +1 -0
- package/lib/commonjs/types/scroll/ScrollMetrics.js +6 -0
- package/lib/commonjs/types/scroll/ScrollMetrics.js.map +1 -0
- package/lib/commonjs/types/scroll/index.js +2 -0
- package/lib/commonjs/types/scroll/index.js.map +1 -0
- package/lib/commonjs/utils/arrayEqual.js +15 -0
- package/lib/commonjs/utils/arrayEqual.js.map +1 -0
- package/lib/commonjs/utils/assertNever.js +10 -0
- package/lib/commonjs/utils/assertNever.js.map +1 -0
- package/lib/commonjs/utils/clamp.js +10 -0
- package/lib/commonjs/utils/clamp.js.map +1 -0
- package/lib/commonjs/utils/devAssert.js +12 -0
- package/lib/commonjs/utils/devAssert.js.map +1 -0
- package/lib/commonjs/utils/invariant.js +12 -0
- package/lib/commonjs/utils/invariant.js.map +1 -0
- package/lib/commonjs/utils/isDefined.js +10 -0
- package/lib/commonjs/utils/isDefined.js.map +1 -0
- package/lib/commonjs/utils/isNumber.js +10 -0
- package/lib/commonjs/utils/isNumber.js.map +1 -0
- package/lib/commonjs/utils/noop.js +10 -0
- package/lib/commonjs/utils/noop.js.map +1 -0
- package/lib/commonjs/utils/shallowEqual.js +22 -0
- package/lib/commonjs/utils/shallowEqual.js.map +1 -0
- package/lib/commonjs/utils/throttle.js +17 -0
- package/lib/commonjs/utils/throttle.js.map +1 -0
- package/lib/commonjs/windowing/ScrollMetrics.js +2 -0
- package/lib/commonjs/windowing/ScrollMetrics.js.map +1 -0
- package/lib/commonjs/windowing/computeVisibleItemRange.js +71 -0
- package/lib/commonjs/windowing/computeVisibleItemRange.js.map +1 -0
- package/lib/commonjs/windowing/index.js +20 -0
- package/lib/commonjs/windowing/index.js.map +1 -0
- package/lib/commonjs/windowing/useScrollMetrics.js +39 -0
- package/lib/commonjs/windowing/useScrollMetrics.js.map +1 -0
- package/lib/module/NitroList.js +5 -0
- package/lib/module/NitroList.js.map +1 -0
- package/lib/module/ReusableView.js +2 -0
- package/lib/module/ReusableView.js.map +1 -0
- package/lib/module/cell/Cell.js +2 -0
- package/lib/module/cell/Cell.js.map +1 -0
- package/lib/module/cell/CellKeyGenerator.js +16 -0
- package/lib/module/cell/CellKeyGenerator.js.map +1 -0
- package/lib/module/cell/CellRecycler.js +66 -0
- package/lib/module/cell/CellRecycler.js.map +1 -0
- package/lib/module/cell/createCell.js +17 -0
- package/lib/module/cell/createCell.js.map +1 -0
- package/lib/module/cell/index.js +7 -0
- package/lib/module/cell/index.js.map +1 -0
- package/lib/module/debug/useDebugOverlay.js +11 -0
- package/lib/module/debug/useDebugOverlay.js.map +1 -0
- package/lib/module/getVisibleIndices.js +34 -0
- package/lib/module/getVisibleIndices.js.map +1 -0
- package/lib/module/hooks/usePersistentCallback.js +33 -0
- package/lib/module/hooks/usePersistentCallback.js.map +1 -0
- package/lib/module/index.js +1 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/layout/MutableLinearLayout.js +100 -0
- package/lib/module/layout/MutableLinearLayout.js.map +1 -0
- package/lib/module/layout/constants/layoutDefaults.js +15 -0
- package/lib/module/layout/constants/layoutDefaults.js.map +1 -0
- package/lib/module/layout/index.js +2 -0
- package/lib/module/layout/index.js.map +1 -0
- package/lib/module/measurement/MeasureLayout.js +16 -0
- package/lib/module/measurement/MeasureLayout.js.map +1 -0
- package/lib/module/measurement/useItemMeasurement.js +24 -0
- package/lib/module/measurement/useItemMeasurement.js.map +1 -0
- package/lib/module/native/NitroLayoutEngine.js +5 -0
- package/lib/module/native/NitroLayoutEngine.js.map +1 -0
- package/lib/module/native/NitroList.types.js +4 -0
- package/lib/module/native/NitroList.types.js.map +1 -0
- package/lib/module/native/NitroRecyclerView.js +5 -0
- package/lib/module/native/NitroRecyclerView.js.map +1 -0
- package/lib/module/prefetch/PrefetchHelper.js +34 -0
- package/lib/module/prefetch/PrefetchHelper.js.map +1 -0
- package/lib/module/recycler/CellPool.js +41 -0
- package/lib/module/recycler/CellPool.js.map +1 -0
- package/lib/module/recycler/RecyclerList.js +221 -0
- package/lib/module/recycler/RecyclerList.js.map +1 -0
- package/lib/module/specs/nitro-layout-engine.nitro.js +4 -0
- package/lib/module/specs/nitro-layout-engine.nitro.js.map +1 -0
- package/lib/module/types/Axis.js +2 -0
- package/lib/module/types/Axis.js.map +1 -0
- package/lib/module/types/CellKey.js +2 -0
- package/lib/module/types/CellKey.js.map +1 -0
- package/lib/module/types/CellType.js +2 -0
- package/lib/module/types/CellType.js.map +1 -0
- package/lib/module/types/VisibleRange.js +2 -0
- package/lib/module/types/VisibleRange.js.map +1 -0
- package/lib/module/types/VisibleRangeInput.js +4 -0
- package/lib/module/types/VisibleRangeInput.js.map +1 -0
- package/lib/module/types/index.js +4 -0
- package/lib/module/types/index.js.map +1 -0
- package/lib/module/types/layout/LayoutRect.js +2 -0
- package/lib/module/types/layout/LayoutRect.js.map +1 -0
- package/lib/module/types/layout/LinearLayoutInput.js +4 -0
- package/lib/module/types/layout/LinearLayoutInput.js.map +1 -0
- package/lib/module/types/layout/MainAxisPadding.js +22 -0
- package/lib/module/types/layout/MainAxisPadding.js.map +1 -0
- package/lib/module/types/layout/index.js +2 -0
- package/lib/module/types/layout/index.js.map +1 -0
- package/lib/module/types/recycler/RecyclerCellInstance.js +4 -0
- package/lib/module/types/recycler/RecyclerCellInstance.js.map +1 -0
- package/lib/module/types/recycler/RecyclerItemRenderer.js +4 -0
- package/lib/module/types/recycler/RecyclerItemRenderer.js.map +1 -0
- package/lib/module/types/recycler/RecyclerListProps.js +4 -0
- package/lib/module/types/recycler/RecyclerListProps.js.map +1 -0
- package/lib/module/types/recycler/index.js +2 -0
- package/lib/module/types/recycler/index.js.map +1 -0
- package/lib/module/types/scroll/ScrollMetrics.js +4 -0
- package/lib/module/types/scroll/ScrollMetrics.js.map +1 -0
- package/lib/module/types/scroll/index.js +2 -0
- package/lib/module/types/scroll/index.js.map +1 -0
- package/lib/module/utils/arrayEqual.js +11 -0
- package/lib/module/utils/arrayEqual.js.map +1 -0
- package/lib/module/utils/assertNever.js +6 -0
- package/lib/module/utils/assertNever.js.map +1 -0
- package/lib/module/utils/clamp.js +6 -0
- package/lib/module/utils/clamp.js.map +1 -0
- package/lib/module/utils/devAssert.js +8 -0
- package/lib/module/utils/devAssert.js.map +1 -0
- package/lib/module/utils/invariant.js +8 -0
- package/lib/module/utils/invariant.js.map +1 -0
- package/lib/module/utils/isDefined.js +6 -0
- package/lib/module/utils/isDefined.js.map +1 -0
- package/lib/module/utils/isNumber.js +6 -0
- package/lib/module/utils/isNumber.js.map +1 -0
- package/lib/module/utils/noop.js +6 -0
- package/lib/module/utils/noop.js.map +1 -0
- package/lib/module/utils/shallowEqual.js +18 -0
- package/lib/module/utils/shallowEqual.js.map +1 -0
- package/lib/module/utils/throttle.js +13 -0
- package/lib/module/utils/throttle.js.map +1 -0
- package/lib/module/windowing/ScrollMetrics.js +2 -0
- package/lib/module/windowing/ScrollMetrics.js.map +1 -0
- package/lib/module/windowing/computeVisibleItemRange.js +67 -0
- package/lib/module/windowing/computeVisibleItemRange.js.map +1 -0
- package/lib/module/windowing/index.js +5 -0
- package/lib/module/windowing/index.js.map +1 -0
- package/lib/module/windowing/useScrollMetrics.js +35 -0
- package/lib/module/windowing/useScrollMetrics.js.map +1 -0
- package/lib/typescript/src/NitroList.d.ts +5 -0
- package/lib/typescript/src/NitroList.d.ts.map +1 -0
- package/lib/typescript/src/ReusableView.d.ts +9 -0
- package/lib/typescript/src/ReusableView.d.ts.map +1 -0
- package/lib/typescript/src/cell/Cell.d.ts +22 -0
- package/lib/typescript/src/cell/Cell.d.ts.map +1 -0
- package/lib/typescript/src/cell/CellKeyGenerator.d.ts +11 -0
- package/lib/typescript/src/cell/CellKeyGenerator.d.ts.map +1 -0
- package/lib/typescript/src/cell/CellRecycler.d.ts +27 -0
- package/lib/typescript/src/cell/CellRecycler.d.ts.map +1 -0
- package/lib/typescript/src/cell/createCell.d.ts +7 -0
- package/lib/typescript/src/cell/createCell.d.ts.map +1 -0
- package/lib/typescript/src/cell/index.d.ts +4 -0
- package/lib/typescript/src/cell/index.d.ts.map +1 -0
- package/lib/typescript/src/debug/useDebugOverlay.d.ts +2 -0
- package/lib/typescript/src/debug/useDebugOverlay.d.ts.map +1 -0
- package/lib/typescript/src/getVisibleIndices.d.ts +8 -0
- package/lib/typescript/src/getVisibleIndices.d.ts.map +1 -0
- package/lib/typescript/src/hooks/usePersistentCallback.d.ts +16 -0
- package/lib/typescript/src/hooks/usePersistentCallback.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/layout/MutableLinearLayout.d.ts +54 -0
- package/lib/typescript/src/layout/MutableLinearLayout.d.ts.map +1 -0
- package/lib/typescript/src/layout/constants/layoutDefaults.d.ts +13 -0
- package/lib/typescript/src/layout/constants/layoutDefaults.d.ts.map +1 -0
- package/lib/typescript/src/layout/index.d.ts +2 -0
- package/lib/typescript/src/layout/index.d.ts.map +1 -0
- package/lib/typescript/src/measurement/MeasureLayout.d.ts +10 -0
- package/lib/typescript/src/measurement/MeasureLayout.d.ts.map +1 -0
- package/lib/typescript/src/measurement/useItemMeasurement.d.ts +11 -0
- package/lib/typescript/src/measurement/useItemMeasurement.d.ts.map +1 -0
- package/lib/typescript/src/native/NitroLayoutEngine.d.ts +3 -0
- package/lib/typescript/src/native/NitroLayoutEngine.d.ts.map +1 -0
- package/lib/typescript/src/native/NitroList.types.d.ts +9 -0
- package/lib/typescript/src/native/NitroList.types.d.ts.map +1 -0
- package/lib/typescript/src/native/NitroRecyclerView.d.ts +5 -0
- package/lib/typescript/src/native/NitroRecyclerView.d.ts.map +1 -0
- package/lib/typescript/src/prefetch/PrefetchHelper.d.ts +17 -0
- package/lib/typescript/src/prefetch/PrefetchHelper.d.ts.map +1 -0
- package/lib/typescript/src/recycler/CellPool.d.ts +14 -0
- package/lib/typescript/src/recycler/CellPool.d.ts.map +1 -0
- package/lib/typescript/src/recycler/RecyclerList.d.ts +4 -0
- package/lib/typescript/src/recycler/RecyclerList.d.ts.map +1 -0
- package/lib/typescript/src/specs/nitro-layout-engine.nitro.d.ts +14 -0
- package/lib/typescript/src/specs/nitro-layout-engine.nitro.d.ts.map +1 -0
- package/lib/typescript/src/types/Axis.d.ts +11 -0
- package/lib/typescript/src/types/Axis.d.ts.map +1 -0
- package/lib/typescript/src/types/CellKey.d.ts +10 -0
- package/lib/typescript/src/types/CellKey.d.ts.map +1 -0
- package/lib/typescript/src/types/CellType.d.ts +11 -0
- package/lib/typescript/src/types/CellType.d.ts.map +1 -0
- package/lib/typescript/src/types/VisibleRange.d.ts +10 -0
- package/lib/typescript/src/types/VisibleRange.d.ts.map +1 -0
- package/lib/typescript/src/types/VisibleRangeInput.d.ts +35 -0
- package/lib/typescript/src/types/VisibleRangeInput.d.ts.map +1 -0
- package/lib/typescript/src/types/index.d.ts +8 -0
- package/lib/typescript/src/types/index.d.ts.map +1 -0
- package/lib/typescript/src/types/layout/LayoutRect.d.ts +15 -0
- package/lib/typescript/src/types/layout/LayoutRect.d.ts.map +1 -0
- package/lib/typescript/src/types/layout/LinearLayoutInput.d.ts +10 -0
- package/lib/typescript/src/types/layout/LinearLayoutInput.d.ts.map +1 -0
- package/lib/typescript/src/types/layout/MainAxisPadding.d.ts +20 -0
- package/lib/typescript/src/types/layout/MainAxisPadding.d.ts.map +1 -0
- package/lib/typescript/src/types/layout/index.d.ts +4 -0
- package/lib/typescript/src/types/layout/index.d.ts.map +1 -0
- package/lib/typescript/src/types/recycler/RecyclerCellInstance.d.ts +37 -0
- package/lib/typescript/src/types/recycler/RecyclerCellInstance.d.ts.map +1 -0
- package/lib/typescript/src/types/recycler/RecyclerItemRenderer.d.ts +8 -0
- package/lib/typescript/src/types/recycler/RecyclerItemRenderer.d.ts.map +1 -0
- package/lib/typescript/src/types/recycler/RecyclerListProps.d.ts +66 -0
- package/lib/typescript/src/types/recycler/RecyclerListProps.d.ts.map +1 -0
- package/lib/typescript/src/types/recycler/index.d.ts +4 -0
- package/lib/typescript/src/types/recycler/index.d.ts.map +1 -0
- package/lib/typescript/src/types/scroll/ScrollMetrics.d.ts +15 -0
- package/lib/typescript/src/types/scroll/ScrollMetrics.d.ts.map +1 -0
- package/lib/typescript/src/types/scroll/index.d.ts +2 -0
- package/lib/typescript/src/types/scroll/index.d.ts.map +1 -0
- package/lib/typescript/src/utils/arrayEqual.d.ts +2 -0
- package/lib/typescript/src/utils/arrayEqual.d.ts.map +1 -0
- package/lib/typescript/src/utils/assertNever.d.ts +2 -0
- package/lib/typescript/src/utils/assertNever.d.ts.map +1 -0
- package/lib/typescript/src/utils/clamp.d.ts +2 -0
- package/lib/typescript/src/utils/clamp.d.ts.map +1 -0
- package/lib/typescript/src/utils/devAssert.d.ts +2 -0
- package/lib/typescript/src/utils/devAssert.d.ts.map +1 -0
- package/lib/typescript/src/utils/invariant.d.ts +2 -0
- package/lib/typescript/src/utils/invariant.d.ts.map +1 -0
- package/lib/typescript/src/utils/isDefined.d.ts +2 -0
- package/lib/typescript/src/utils/isDefined.d.ts.map +1 -0
- package/lib/typescript/src/utils/isNumber.d.ts +2 -0
- package/lib/typescript/src/utils/isNumber.d.ts.map +1 -0
- package/lib/typescript/src/utils/noop.d.ts +2 -0
- package/lib/typescript/src/utils/noop.d.ts.map +1 -0
- package/lib/typescript/src/utils/shallowEqual.d.ts +2 -0
- package/lib/typescript/src/utils/shallowEqual.d.ts.map +1 -0
- package/lib/typescript/src/utils/throttle.d.ts +2 -0
- package/lib/typescript/src/utils/throttle.d.ts.map +1 -0
- package/lib/typescript/src/windowing/ScrollMetrics.d.ts +11 -0
- package/lib/typescript/src/windowing/ScrollMetrics.d.ts.map +1 -0
- package/lib/typescript/src/windowing/computeVisibleItemRange.d.ts +15 -0
- package/lib/typescript/src/windowing/computeVisibleItemRange.d.ts.map +1 -0
- package/lib/typescript/src/windowing/index.d.ts +4 -0
- package/lib/typescript/src/windowing/index.d.ts.map +1 -0
- package/lib/typescript/src/windowing/useScrollMetrics.d.ts +14 -0
- package/lib/typescript/src/windowing/useScrollMetrics.d.ts.map +1 -0
- package/nitro.json +20 -13
- package/nitrogen/generated/android/NitroList+autolinking.cmake +2 -4
- package/nitrogen/generated/android/NitroListOnLoad.cpp +3 -13
- package/nitrogen/generated/android/c++/JHybridNitroLayoutEngineSpec.cpp +69 -0
- package/nitrogen/generated/android/c++/{JHybridNitroListSpec.hpp → JHybridNitroLayoutEngineSpec.hpp} +12 -13
- package/nitrogen/generated/android/c++/JLayoutRect.hpp +69 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolist/{HybridNitroListSpec.kt → HybridNitroLayoutEngineSpec.kt} +11 -13
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolist/LayoutRect.kt +47 -0
- package/nitrogen/generated/ios/NitroList-Swift-Cxx-Bridge.cpp +9 -9
- package/nitrogen/generated/ios/NitroList-Swift-Cxx-Bridge.hpp +50 -13
- package/nitrogen/generated/ios/NitroList-Swift-Cxx-Umbrella.hpp +11 -5
- package/nitrogen/generated/ios/c++/{HybridNitroListSpecSwift.cpp → HybridNitroLayoutEngineSpecSwift.cpp} +2 -2
- package/nitrogen/generated/ios/c++/HybridNitroLayoutEngineSpecSwift.hpp +78 -0
- package/nitrogen/generated/ios/swift/HybridNitroLayoutEngineSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/{HybridNitroListSpec_cxx.swift → HybridNitroLayoutEngineSpec_cxx.swift} +35 -38
- package/nitrogen/generated/ios/swift/LayoutRect.swift +45 -0
- package/nitrogen/generated/shared/c++/{HybridNitroListSpec.cpp → HybridNitroLayoutEngineSpec.cpp} +4 -5
- package/nitrogen/generated/shared/c++/{HybridNitroListSpec.hpp → HybridNitroLayoutEngineSpec.hpp} +15 -14
- package/nitrogen/generated/shared/c++/LayoutRect.hpp +95 -0
- package/package.json +12 -4
- package/src/NitroList.ts +8 -0
- package/src/ReusableView.ts +12 -0
- package/src/cell/Cell.ts +23 -0
- package/src/cell/CellKeyGenerator.ts +17 -0
- package/src/cell/CellRecycler.ts +76 -0
- package/src/cell/createCell.ts +15 -0
- package/src/cell/index.ts +5 -0
- package/src/debug/useDebugOverlay.ts +14 -0
- package/src/getVisibleIndices.ts +55 -0
- package/src/hooks/usePersistentCallback.ts +37 -0
- package/src/index.ts +1 -14
- package/src/layout/MutableLinearLayout.ts +112 -0
- package/src/layout/constants/layoutDefaults.ts +12 -0
- package/src/layout/index.ts +1 -0
- package/src/measurement/MeasureLayout.ts +20 -0
- package/src/measurement/useItemMeasurement.ts +32 -0
- package/src/native/NitroLayoutEngine.ts +7 -0
- package/src/native/NitroList.types.ts +13 -0
- package/src/native/NitroRecyclerView.ts +8 -0
- package/src/prefetch/PrefetchHelper.ts +47 -0
- package/src/recycler/CellPool.ts +47 -0
- package/src/recycler/RecyclerList.tsx +304 -0
- package/src/specs/nitro-layout-engine.nitro.ts +17 -0
- package/src/types/Axis.ts +10 -0
- package/src/types/CellKey.ts +9 -0
- package/src/types/CellType.ts +10 -0
- package/src/types/VisibleRange.ts +9 -0
- package/src/types/VisibleRangeInput.ts +39 -0
- package/src/types/index.ts +15 -0
- package/src/types/layout/LayoutRect.ts +14 -0
- package/src/types/layout/LinearLayoutInput.ts +12 -0
- package/src/types/layout/MainAxisPadding.ts +23 -0
- package/src/types/layout/index.ts +3 -0
- package/src/types/recycler/RecyclerCellInstance.ts +40 -0
- package/src/types/recycler/RecyclerItemRenderer.ts +8 -0
- package/src/types/recycler/RecyclerListProps.ts +74 -0
- package/src/types/recycler/index.ts +3 -0
- package/src/types/scroll/ScrollMetrics.ts +18 -0
- package/src/types/scroll/index.ts +1 -0
- package/src/utils/arrayEqual.ts +13 -0
- package/src/utils/assertNever.ts +3 -0
- package/src/utils/clamp.ts +7 -0
- package/src/utils/devAssert.ts +8 -0
- package/src/utils/invariant.ts +8 -0
- package/src/utils/isDefined.ts +5 -0
- package/src/utils/isNumber.ts +3 -0
- package/src/utils/noop.ts +3 -0
- package/src/utils/shallowEqual.ts +34 -0
- package/src/utils/throttle.ts +13 -0
- package/src/windowing/ScrollMetrics.ts +11 -0
- package/src/windowing/computeVisibleItemRange.ts +78 -0
- package/src/windowing/index.ts +3 -0
- package/src/windowing/useScrollMetrics.ts +56 -0
- package/android/src/main/java/com/nitrolist/HybridNitroList.kt +0 -27
- package/lib/module/specs/nitro-list.nitro.js +0 -4
- package/lib/typescript/src/specs/nitro-list.nitro.d.ts +0 -11
- package/lib/typescript/src/specs/nitro-list.nitro.d.ts.map +0 -1
- package/nitrogen/generated/android/c++/JHybridNitroListSpec.cpp +0 -56
- package/nitrogen/generated/android/c++/views/JHybridNitroListStateUpdater.cpp +0 -56
- package/nitrogen/generated/android/c++/views/JHybridNitroListStateUpdater.hpp +0 -49
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolist/views/HybridNitroListManager.kt +0 -50
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrolist/views/HybridNitroListStateUpdater.kt +0 -23
- package/nitrogen/generated/ios/NitroListAutolinking.mm +0 -33
- package/nitrogen/generated/ios/NitroListAutolinking.swift +0 -25
- package/nitrogen/generated/ios/c++/HybridNitroListSpecSwift.hpp +0 -74
- package/nitrogen/generated/ios/c++/views/HybridNitroListComponent.mm +0 -96
- package/nitrogen/generated/ios/swift/HybridNitroListSpec.swift +0 -56
- package/nitrogen/generated/shared/c++/views/HybridNitroListComponent.cpp +0 -88
- package/nitrogen/generated/shared/c++/views/HybridNitroListComponent.hpp +0 -107
- package/nitrogen/generated/shared/json/NitroListConfig.json +0 -10
- package/src/specs/nitro-list.nitro.ts +0 -13
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useRef, useCallback } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns a function whose identity is stable across renders,
|
|
5
|
+
* but always invokes the latest provided implementation.
|
|
6
|
+
*
|
|
7
|
+
* Invariants:
|
|
8
|
+
* - Function reference NEVER changes
|
|
9
|
+
* - Logic is ALWAYS up to date
|
|
10
|
+
*
|
|
11
|
+
* This is critical for long-lived systems such as:
|
|
12
|
+
* - Recyclers
|
|
13
|
+
* - Effects
|
|
14
|
+
* - Native bridges
|
|
15
|
+
* - Event pipelines
|
|
16
|
+
*/
|
|
17
|
+
export function usePersistentCallback<
|
|
18
|
+
T extends (...args: never[]) => unknown
|
|
19
|
+
>(fn: T): T {
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Holds the latest implementation.
|
|
23
|
+
* Updating this does NOT trigger re-renders.
|
|
24
|
+
*/
|
|
25
|
+
const fnRef = useRef(fn)
|
|
26
|
+
fnRef.current = fn
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Stable wrapper that forwards calls
|
|
30
|
+
* to the latest implementation.
|
|
31
|
+
*/
|
|
32
|
+
return useCallback(
|
|
33
|
+
((...args: never[]) =>
|
|
34
|
+
fnRef.current(...args)) as T,
|
|
35
|
+
[]
|
|
36
|
+
)
|
|
37
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import NitroListConfig from '../nitrogen/generated/shared/json/NitroListConfig.json'
|
|
3
|
-
import type {
|
|
4
|
-
NitroListProps,
|
|
5
|
-
NitroListMethods,
|
|
6
|
-
} from './specs/nitro-list.nitro'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export const NitroList = getHostComponent<NitroListProps, NitroListMethods>(
|
|
10
|
-
'NitroList',
|
|
11
|
-
() => NitroListConfig
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
export type NitroListRef = HybridRef<NitroListProps, NitroListMethods>
|
|
1
|
+
export { RecyclerList } from './recycler/RecyclerList'
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { Axis } from '../types/Axis'
|
|
2
|
+
import type { LinearLayoutInput } from '../types/layout'
|
|
3
|
+
import type { LayoutRect } from '../types/layout/LayoutRect'
|
|
4
|
+
|
|
5
|
+
import { DEFAULT_ITEM_SPACING } from './constants/layoutDefaults'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Mutable, deterministic linear layout engine.
|
|
9
|
+
*
|
|
10
|
+
* Responsibilities:
|
|
11
|
+
* - Compute absolute item geometry
|
|
12
|
+
* - Own layout truth (no React, no scroll state)
|
|
13
|
+
* - Guarantee monotonic ordering along the main axis
|
|
14
|
+
*
|
|
15
|
+
* Cross-platform equivalents:
|
|
16
|
+
* - Flutter: RenderSliver / SliverList
|
|
17
|
+
* - Android: LinearLayoutManager
|
|
18
|
+
* - iOS: UICollectionViewFlowLayout (linear)
|
|
19
|
+
*/
|
|
20
|
+
export class MutableLinearLayout {
|
|
21
|
+
private readonly isVertical: boolean
|
|
22
|
+
|
|
23
|
+
/** Absolute item layouts in index order */
|
|
24
|
+
private layouts: LayoutRect[] = []
|
|
25
|
+
|
|
26
|
+
/** Total scrollable size along main axis */
|
|
27
|
+
private contentSize = 0
|
|
28
|
+
|
|
29
|
+
constructor(axis: Axis) {
|
|
30
|
+
this.isVertical = axis === 'vertical'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Computes layout synchronously from input.
|
|
35
|
+
*
|
|
36
|
+
* This method is intentionally parameter-object based
|
|
37
|
+
* to allow future extension without breaking API.
|
|
38
|
+
*
|
|
39
|
+
* Invariants:
|
|
40
|
+
* - Layouts are monotonic along main axis
|
|
41
|
+
* - No gaps except explicit spacing
|
|
42
|
+
* - Deterministic for identical inputs
|
|
43
|
+
*/
|
|
44
|
+
compute(input: LinearLayoutInput): void {
|
|
45
|
+
const {
|
|
46
|
+
crossAxisSize,
|
|
47
|
+
itemMainAxisSizes,
|
|
48
|
+
padding,
|
|
49
|
+
itemSpacing = DEFAULT_ITEM_SPACING,
|
|
50
|
+
} = input
|
|
51
|
+
|
|
52
|
+
const itemCount = itemMainAxisSizes.length
|
|
53
|
+
const layouts: LayoutRect[] = new Array(itemCount)
|
|
54
|
+
|
|
55
|
+
let cursor = padding.start
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < itemCount; i++) {
|
|
58
|
+
const mainAxisSize = itemMainAxisSizes[i]!
|
|
59
|
+
|
|
60
|
+
layouts[i] = this.isVertical
|
|
61
|
+
? {
|
|
62
|
+
x: 0,
|
|
63
|
+
y: cursor,
|
|
64
|
+
width: crossAxisSize,
|
|
65
|
+
height: mainAxisSize,
|
|
66
|
+
}
|
|
67
|
+
: {
|
|
68
|
+
x: cursor,
|
|
69
|
+
y: 0,
|
|
70
|
+
width: mainAxisSize,
|
|
71
|
+
height: crossAxisSize,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
cursor += mainAxisSize
|
|
75
|
+
|
|
76
|
+
// Space BETWEEN items, not after the last
|
|
77
|
+
if (i < itemCount - 1) {
|
|
78
|
+
cursor += itemSpacing
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.layouts = layouts
|
|
83
|
+
this.contentSize = cursor + padding.end
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Returns computed item layouts.
|
|
88
|
+
*
|
|
89
|
+
* Order is guaranteed monotonic along the main axis,
|
|
90
|
+
* which is required for binary-search-based windowing.
|
|
91
|
+
*/
|
|
92
|
+
getLayouts(): readonly LayoutRect[] {
|
|
93
|
+
return this.layouts
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Returns total scrollable size along the main axis,
|
|
98
|
+
* including padding and inter-item spacing.
|
|
99
|
+
*/
|
|
100
|
+
getContentSize(): number {
|
|
101
|
+
return this.contentSize
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Clears internal layout state.
|
|
106
|
+
* Call on severe invalidation or teardown.
|
|
107
|
+
*/
|
|
108
|
+
reset(): void {
|
|
109
|
+
this.layouts = []
|
|
110
|
+
this.contentSize = 0
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default spacing between items along the main axis.
|
|
3
|
+
*
|
|
4
|
+
* This value is used by layout engines when no explicit
|
|
5
|
+
* itemSpacing is provided.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors:
|
|
8
|
+
* - Android RecyclerView ItemDecoration (typical 8–16dp)
|
|
9
|
+
* - iOS UICollectionViewFlowLayout minimumLineSpacing
|
|
10
|
+
* - Flutter SliverList spacing conventions
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_ITEM_SPACING = 12
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { MutableLinearLayout } from './MutableLinearLayout'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LayoutChangeEvent } from 'react-native'
|
|
2
|
+
|
|
3
|
+
export interface MeasuredLayout {
|
|
4
|
+
readonly width: number
|
|
5
|
+
readonly height: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extracts stable layout measurements from RN onLayout event.
|
|
10
|
+
*/
|
|
11
|
+
export function measureLayout(
|
|
12
|
+
event: LayoutChangeEvent
|
|
13
|
+
): MeasuredLayout {
|
|
14
|
+
const { width, height } = event.nativeEvent.layout
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
width,
|
|
18
|
+
height,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useCallback, useRef } from 'react'
|
|
2
|
+
import type { LayoutChangeEvent } from 'react-native'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook to measure a rendered item safely.
|
|
6
|
+
*
|
|
7
|
+
* Guarantees:
|
|
8
|
+
* - No duplicate height reports
|
|
9
|
+
* - No render loops
|
|
10
|
+
* - Stable callback identity
|
|
11
|
+
*/
|
|
12
|
+
export function useItemMeasurement(
|
|
13
|
+
index: number,
|
|
14
|
+
onMeasured: (index: number, height: number) => void
|
|
15
|
+
) {
|
|
16
|
+
const lastHeightRef = useRef<number | null>(null)
|
|
17
|
+
|
|
18
|
+
const onLayout = useCallback(
|
|
19
|
+
(e: LayoutChangeEvent) => {
|
|
20
|
+
const height = e.nativeEvent.layout.height
|
|
21
|
+
|
|
22
|
+
// Ignore identical measurements
|
|
23
|
+
if (lastHeightRef.current === height) return
|
|
24
|
+
|
|
25
|
+
lastHeightRef.current = height
|
|
26
|
+
onMeasured(index, height)
|
|
27
|
+
},
|
|
28
|
+
[index, onMeasured]
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
return onLayout
|
|
32
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NitroModules } from 'react-native-nitro-modules'
|
|
2
|
+
import type { NitroLayoutEngine as NitroLayoutEngineSpec } from '../specs/nitro-layout-engine.nitro'
|
|
3
|
+
|
|
4
|
+
export const NitroLayoutEngine =
|
|
5
|
+
NitroModules.createHybridObject<NitroLayoutEngineSpec>(
|
|
6
|
+
'NitroLayoutEngine'
|
|
7
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LayoutRect } from "../types/layout";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* JS runtime interface backed by Nitro (JSI).
|
|
6
|
+
* Must match native HybridNitroList exactly.
|
|
7
|
+
*/
|
|
8
|
+
export interface NitroList {
|
|
9
|
+
computeLayout(
|
|
10
|
+
containerWidth: number,
|
|
11
|
+
itemHeights: readonly number[]
|
|
12
|
+
): readonly LayoutRect[]
|
|
13
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback invoked when new items should be prefetched.
|
|
3
|
+
*/
|
|
4
|
+
export type PrefetchCallback = (indices: number[]) => void
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* PrefetchHelper
|
|
8
|
+
*
|
|
9
|
+
* Stateless with respect to layout.
|
|
10
|
+
* Only tracks what has already been prefetched to avoid duplication.
|
|
11
|
+
*
|
|
12
|
+
* Designed to be driven by *visible window*, not scroll events.
|
|
13
|
+
*/
|
|
14
|
+
export class PrefetchHelper {
|
|
15
|
+
private lastPrefetched = new Set<number>()
|
|
16
|
+
|
|
17
|
+
runPrefetch(
|
|
18
|
+
visibleIndices: readonly number[],
|
|
19
|
+
itemCount: number,
|
|
20
|
+
aheadCount: number,
|
|
21
|
+
onPrefetch: PrefetchCallback
|
|
22
|
+
) {
|
|
23
|
+
if (visibleIndices.length === 0) return
|
|
24
|
+
|
|
25
|
+
const lastVisible =
|
|
26
|
+
visibleIndices[visibleIndices.length - 1]!
|
|
27
|
+
|
|
28
|
+
const start = lastVisible + 1
|
|
29
|
+
const end = Math.min(
|
|
30
|
+
itemCount - 1,
|
|
31
|
+
start + aheadCount
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const toPrefetch: number[] = []
|
|
35
|
+
|
|
36
|
+
for (let i = start; i <= end; i++) {
|
|
37
|
+
if (!this.lastPrefetched.has(i)) {
|
|
38
|
+
this.lastPrefetched.add(i)
|
|
39
|
+
toPrefetch.push(i)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (toPrefetch.length > 0) {
|
|
44
|
+
onPrefetch(toPrefetch)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CellType } from '../types/CellType'
|
|
2
|
+
import type { RecyclerCellInstance } from '../types/recycler'
|
|
3
|
+
|
|
4
|
+
export class CellPool {
|
|
5
|
+
private readonly pools = new Map<CellType, RecyclerCellInstance[]>()
|
|
6
|
+
private readonly maxPerType = new Map<CellType, number>()
|
|
7
|
+
|
|
8
|
+
/** ✅ NEW: check if a type is registered */
|
|
9
|
+
hasType(type: CellType): boolean {
|
|
10
|
+
return this.pools.has(type)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
registerType(type: CellType, maxCount: number): void {
|
|
14
|
+
if (this.maxPerType.has(type)) return
|
|
15
|
+
this.maxPerType.set(type, maxCount)
|
|
16
|
+
this.pools.set(type, [])
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
acquire(type: CellType): RecyclerCellInstance | null {
|
|
20
|
+
const bucket = this.pools.get(type)
|
|
21
|
+
if (!bucket || bucket.length === 0) return null
|
|
22
|
+
return bucket.pop()!
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
release(cell: RecyclerCellInstance): void {
|
|
26
|
+
const { type } = cell
|
|
27
|
+
const bucket = this.pools.get(type)
|
|
28
|
+
const max = this.maxPerType.get(type)
|
|
29
|
+
|
|
30
|
+
if (!bucket || max === undefined) return
|
|
31
|
+
|
|
32
|
+
cell.index = -1
|
|
33
|
+
|
|
34
|
+
if (bucket.length >= max) return
|
|
35
|
+
bucket.push(cell)
|
|
36
|
+
}
|
|
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
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import React, { useRef, useEffect, useState } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
ScrollView,
|
|
4
|
+
View,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
type NativeSyntheticEvent,
|
|
7
|
+
type NativeScrollEvent,
|
|
8
|
+
} from 'react-native'
|
|
9
|
+
|
|
10
|
+
import type { LayoutRect, VisibleRange } from '../types'
|
|
11
|
+
import type {
|
|
12
|
+
RecyclerCellInstance,
|
|
13
|
+
RecyclerListProps,
|
|
14
|
+
} from '../types/recycler'
|
|
15
|
+
import type { CellType } from '../types/CellType'
|
|
16
|
+
|
|
17
|
+
import { MutableLinearLayout } from '../layout/MutableLinearLayout'
|
|
18
|
+
import { computeVisibleItemRange } from '../windowing'
|
|
19
|
+
import { CellPool } from './CellPool'
|
|
20
|
+
|
|
21
|
+
/* =========================================================
|
|
22
|
+
* Constants
|
|
23
|
+
* =======================================================*/
|
|
24
|
+
|
|
25
|
+
const DEFAULT_VIEWPORT_SIZE = 800
|
|
26
|
+
const DEFAULT_ITEM_SIZE = 60
|
|
27
|
+
const SCROLL_EVENT_THROTTLE = 16
|
|
28
|
+
|
|
29
|
+
/* =========================================================
|
|
30
|
+
* RecyclerList
|
|
31
|
+
* =======================================================*/
|
|
32
|
+
|
|
33
|
+
export function RecyclerList<T>(
|
|
34
|
+
props: RecyclerListProps<T>
|
|
35
|
+
): React.ReactElement {
|
|
36
|
+
const {
|
|
37
|
+
scrollDirection = 'vertical',
|
|
38
|
+
containerCrossAxisSize,
|
|
39
|
+
data,
|
|
40
|
+
itemMainAxisSizes,
|
|
41
|
+
padding = { start: 0, end: 0 },
|
|
42
|
+
itemSpacing,
|
|
43
|
+
bufferRatio = 1.3,
|
|
44
|
+
getCellType,
|
|
45
|
+
renderItem,
|
|
46
|
+
} = props
|
|
47
|
+
|
|
48
|
+
const isVertical = scrollDirection === 'vertical'
|
|
49
|
+
const itemCount = data.length
|
|
50
|
+
|
|
51
|
+
/* -------------------------------------------------------
|
|
52
|
+
* Controlled render
|
|
53
|
+
* -----------------------------------------------------*/
|
|
54
|
+
|
|
55
|
+
const [, forceRender] = useState(0)
|
|
56
|
+
|
|
57
|
+
/* -------------------------------------------------------
|
|
58
|
+
* Layout engine
|
|
59
|
+
* -----------------------------------------------------*/
|
|
60
|
+
|
|
61
|
+
const layoutEngineRef = useRef<MutableLinearLayout | null>(null)
|
|
62
|
+
const layoutSignatureRef = useRef('')
|
|
63
|
+
|
|
64
|
+
const layoutSignature = [
|
|
65
|
+
scrollDirection,
|
|
66
|
+
containerCrossAxisSize,
|
|
67
|
+
itemCount,
|
|
68
|
+
itemMainAxisSizes,
|
|
69
|
+
padding.start,
|
|
70
|
+
padding.end,
|
|
71
|
+
itemSpacing ?? 'default',
|
|
72
|
+
].join('|')
|
|
73
|
+
|
|
74
|
+
if (
|
|
75
|
+
!layoutEngineRef.current ||
|
|
76
|
+
layoutSignatureRef.current !== layoutSignature
|
|
77
|
+
) {
|
|
78
|
+
const engine = new MutableLinearLayout(scrollDirection)
|
|
79
|
+
engine.compute({
|
|
80
|
+
crossAxisSize: containerCrossAxisSize,
|
|
81
|
+
itemMainAxisSizes,
|
|
82
|
+
padding,
|
|
83
|
+
itemSpacing,
|
|
84
|
+
})
|
|
85
|
+
layoutEngineRef.current = engine
|
|
86
|
+
layoutSignatureRef.current = layoutSignature
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const layouts: readonly LayoutRect[] =
|
|
90
|
+
layoutEngineRef.current.getLayouts()
|
|
91
|
+
|
|
92
|
+
const contentSize =
|
|
93
|
+
layoutEngineRef.current.getContentSize()
|
|
94
|
+
|
|
95
|
+
/* -------------------------------------------------------
|
|
96
|
+
* Scroll metrics
|
|
97
|
+
* -----------------------------------------------------*/
|
|
98
|
+
|
|
99
|
+
const scrollOffsetRef = useRef(0)
|
|
100
|
+
const viewportSizeRef = useRef(DEFAULT_VIEWPORT_SIZE)
|
|
101
|
+
|
|
102
|
+
/* -------------------------------------------------------
|
|
103
|
+
* Cell pool + physical cells
|
|
104
|
+
* -----------------------------------------------------*/
|
|
105
|
+
|
|
106
|
+
const cellPoolRef = useRef(new CellPool())
|
|
107
|
+
const activeCellsRef = useRef<RecyclerCellInstance[]>([])
|
|
108
|
+
const nextCellKeyRef = useRef(0)
|
|
109
|
+
|
|
110
|
+
const createCell = (type: CellType): RecyclerCellInstance => ({
|
|
111
|
+
key: nextCellKeyRef.current++,
|
|
112
|
+
index: -1,
|
|
113
|
+
type,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const MAX_CELLS = Math.ceil(
|
|
117
|
+
(viewportSizeRef.current / DEFAULT_ITEM_SIZE) *
|
|
118
|
+
bufferRatio
|
|
119
|
+
) + 2
|
|
120
|
+
|
|
121
|
+
/* -------------------------------------------------------
|
|
122
|
+
* Visible cell coordinator
|
|
123
|
+
* -----------------------------------------------------*/
|
|
124
|
+
|
|
125
|
+
const updateVisibleCells = (): void => {
|
|
126
|
+
const offset = scrollOffsetRef.current
|
|
127
|
+
const viewportSize = viewportSizeRef.current
|
|
128
|
+
const buffer = viewportSize * bufferRatio
|
|
129
|
+
|
|
130
|
+
const range: VisibleRange | null =
|
|
131
|
+
computeVisibleItemRange({
|
|
132
|
+
layouts,
|
|
133
|
+
offset,
|
|
134
|
+
viewportSize,
|
|
135
|
+
buffer,
|
|
136
|
+
isVertical,
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
if (!range) return
|
|
140
|
+
|
|
141
|
+
const nextCells: RecyclerCellInstance[] = []
|
|
142
|
+
const used = new Set<RecyclerCellInstance>()
|
|
143
|
+
|
|
144
|
+
for (
|
|
145
|
+
let index = range.startIndex;
|
|
146
|
+
index <= range.endIndex;
|
|
147
|
+
index++
|
|
148
|
+
) {
|
|
149
|
+
const item = data[index]
|
|
150
|
+
if (item === undefined) continue
|
|
151
|
+
|
|
152
|
+
const type = getCellType(item, index)
|
|
153
|
+
|
|
154
|
+
// Register type once, with hard cap
|
|
155
|
+
if (!cellPoolRef.current.hasType(type)) {
|
|
156
|
+
cellPoolRef.current.registerType(type, MAX_CELLS)
|
|
157
|
+
for (let i = 0; i < MAX_CELLS; i++) {
|
|
158
|
+
cellPoolRef.current.release(createCell(type))
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let cell = cellPoolRef.current.acquire(type)
|
|
163
|
+
|
|
164
|
+
if (!cell) {
|
|
165
|
+
const reuseIndex =
|
|
166
|
+
activeCellsRef.current.findIndex(
|
|
167
|
+
c => c.type === type
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if (reuseIndex === -1) continue
|
|
171
|
+
|
|
172
|
+
cell =
|
|
173
|
+
activeCellsRef.current.splice(
|
|
174
|
+
reuseIndex,
|
|
175
|
+
1
|
|
176
|
+
)[0]!
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
cell.index = index
|
|
180
|
+
nextCells.push(cell)
|
|
181
|
+
used.add(cell)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
for (const cell of activeCellsRef.current) {
|
|
185
|
+
if (!used.has(cell)) {
|
|
186
|
+
cellPoolRef.current.release(cell)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let changed =
|
|
191
|
+
nextCells.length !== activeCellsRef.current.length
|
|
192
|
+
|
|
193
|
+
if (!changed) {
|
|
194
|
+
for (let i = 0; i < nextCells.length; i++) {
|
|
195
|
+
if (nextCells[i] !== activeCellsRef.current[i]) {
|
|
196
|
+
changed = true
|
|
197
|
+
break
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (changed) {
|
|
203
|
+
activeCellsRef.current = nextCells
|
|
204
|
+
forceRender(v => v + 1)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* -------------------------------------------------------
|
|
209
|
+
* Scroll handlers
|
|
210
|
+
* -----------------------------------------------------*/
|
|
211
|
+
|
|
212
|
+
const frameScheduledRef = useRef(false)
|
|
213
|
+
|
|
214
|
+
const onScroll = (
|
|
215
|
+
e: NativeSyntheticEvent<NativeScrollEvent>
|
|
216
|
+
): void => {
|
|
217
|
+
scrollOffsetRef.current = isVertical
|
|
218
|
+
? e.nativeEvent.contentOffset.y
|
|
219
|
+
: e.nativeEvent.contentOffset.x
|
|
220
|
+
|
|
221
|
+
if (frameScheduledRef.current) return
|
|
222
|
+
frameScheduledRef.current = true
|
|
223
|
+
|
|
224
|
+
requestAnimationFrame(() => {
|
|
225
|
+
frameScheduledRef.current = false
|
|
226
|
+
updateVisibleCells()
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const onLayout = (e: any): void => {
|
|
231
|
+
viewportSizeRef.current = isVertical
|
|
232
|
+
? e.nativeEvent.layout.height
|
|
233
|
+
: e.nativeEvent.layout.width
|
|
234
|
+
|
|
235
|
+
updateVisibleCells()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
useEffect(() => {
|
|
239
|
+
updateVisibleCells()
|
|
240
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
241
|
+
}, [])
|
|
242
|
+
|
|
243
|
+
/* -------------------------------------------------------
|
|
244
|
+
* Render
|
|
245
|
+
* -----------------------------------------------------*/
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<ScrollView
|
|
249
|
+
onScroll={onScroll}
|
|
250
|
+
onLayout={onLayout}
|
|
251
|
+
horizontal={!isVertical}
|
|
252
|
+
scrollEventThrottle={SCROLL_EVENT_THROTTLE}
|
|
253
|
+
removeClippedSubviews
|
|
254
|
+
>
|
|
255
|
+
<View
|
|
256
|
+
style={
|
|
257
|
+
isVertical
|
|
258
|
+
? { height: contentSize }
|
|
259
|
+
: { width: contentSize }
|
|
260
|
+
}
|
|
261
|
+
>
|
|
262
|
+
{activeCellsRef.current.map(cell => {
|
|
263
|
+
const index = cell.index
|
|
264
|
+
const layout = layouts[index]
|
|
265
|
+
const item = data[index]
|
|
266
|
+
|
|
267
|
+
if (!layout || item === undefined) return null
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
<View
|
|
271
|
+
key={cell.key}
|
|
272
|
+
style={[
|
|
273
|
+
styles.cell,
|
|
274
|
+
isVertical
|
|
275
|
+
? {
|
|
276
|
+
top: layout.y,
|
|
277
|
+
height: layout.height,
|
|
278
|
+
width: layout.width,
|
|
279
|
+
}
|
|
280
|
+
: {
|
|
281
|
+
left: layout.x,
|
|
282
|
+
width: layout.width,
|
|
283
|
+
height: layout.height,
|
|
284
|
+
},
|
|
285
|
+
]}
|
|
286
|
+
>
|
|
287
|
+
{renderItem({ item, index, cell })}
|
|
288
|
+
</View>
|
|
289
|
+
)
|
|
290
|
+
})}
|
|
291
|
+
</View>
|
|
292
|
+
</ScrollView>
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* =========================================================
|
|
297
|
+
* Styles
|
|
298
|
+
* =======================================================*/
|
|
299
|
+
|
|
300
|
+
const styles = StyleSheet.create({
|
|
301
|
+
cell: {
|
|
302
|
+
position: 'absolute',
|
|
303
|
+
},
|
|
304
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HybridObject } from 'react-native-nitro-modules'
|
|
2
|
+
|
|
3
|
+
export interface LayoutRect {
|
|
4
|
+
readonly x: number
|
|
5
|
+
readonly y: number
|
|
6
|
+
readonly width: number
|
|
7
|
+
readonly height: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface NitroLayoutEngine
|
|
11
|
+
extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
|
|
12
|
+
|
|
13
|
+
computeLayout(
|
|
14
|
+
containerWidth: number,
|
|
15
|
+
itemHeights: readonly number[]
|
|
16
|
+
): readonly LayoutRect[]
|
|
17
|
+
}
|