@onekeyfe/react-native-native-list 3.0.157-alpha.254 → 3.0.157-alpha.255
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/android/build.gradle +7 -0
- package/android/src/main/java/com/onekey/nativelist/HybridNativeList.kt +20 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListActivityDetailsView.kt +179 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListActivityRenderer.kt +23 -2
- package/android/src/main/java/com/onekey/nativelist/NativeListContainerSlotAdapter.kt +53 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListEndAlignment.kt +5 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListMediaPreviewSlot.kt +215 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListMediaTileRenderer.kt +15 -6
- package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +3 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListScrollPositionTracker.kt +29 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListSectionHeaderRenderer.kt +16 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +163 -26
- package/android/src/main/res/values/native_scroll_coordinator_ids.xml +4 -0
- package/android/src/test/java/com/margelo/nitro/nativelist/NativeListEndAlignmentTest.kt +34 -0
- package/android/src/test/java/com/margelo/nitro/nativelist/NativeListScrollPositionTrackerTest.kt +28 -0
- package/docs/DESIGN.md +4 -2
- package/docs/ROW_TEMPLATES.md +45 -0
- package/docs/SPEC.md +227 -1
- package/docs/STYLE_SPEC.md +8 -0
- package/ios/HybridNativeList.swift +30 -0
- package/ios/NativeListActivityDetailsView.swift +229 -0
- package/ios/NativeListActivityRenderer.swift +24 -2
- package/ios/NativeListMediaPreviewSlot.swift +199 -0
- package/ios/NativeListMediaTileRenderer.swift +10 -4
- package/ios/NativeListModels.swift +4 -1
- package/ios/NativeListScrollPositionTracker.swift +30 -0
- package/ios/NativeListSectionHeaderRenderer.swift +27 -1
- package/ios/RNCNativeListView.swift +159 -4
- package/ios/tests/NativeListActivityPresentationTests.swift +43 -0
- package/ios/tests/NativeListLifecycleTests.swift +123 -0
- package/ios/tests/NativeListMediaVisibilityTests.swift +42 -0
- package/ios/tests/NativeListScrollPositionTrackerTests.swift +25 -0
- package/lib/module/NativeList.js +56 -3
- package/lib/module/NativeList.web.js +5 -0
- package/lib/module/validation.js +64 -4
- package/lib/nitrogen/generated/android/c++/JFunc_void_bool.hpp +75 -0
- package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +39 -3
- package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +6 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +15 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/Func_void_bool.kt +78 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +26 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListManager.kt +24 -9
- package/lib/nitrogen/generated/android/nativelistOnLoad.cpp +2 -0
- package/lib/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.cpp +8 -0
- package/lib/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.hpp +37 -0
- package/lib/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +23 -2
- package/lib/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +32 -0
- package/lib/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
- package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +3 -0
- package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +54 -0
- package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +6 -0
- package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +8 -2
- package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +6 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +11 -2
- package/lib/nitrogen/generated/shared/json/NativeListConfig.json +3 -0
- package/lib/typescript/src/NativeList.nitro.d.ts +3 -0
- package/lib/typescript/src/NativeList.types.d.ts +14 -1
- package/lib/typescript/src/models.d.ts +33 -5
- package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +75 -0
- package/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +39 -3
- package/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +6 -0
- package/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/Func_void_bool.kt +78 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +26 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListManager.kt +24 -9
- package/nitrogen/generated/android/nativelistOnLoad.cpp +2 -0
- package/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.cpp +8 -0
- package/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.hpp +37 -0
- package/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +23 -2
- package/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +32 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +3 -0
- package/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +54 -0
- package/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +6 -0
- package/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +8 -2
- package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +6 -0
- package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +11 -2
- package/nitrogen/generated/shared/json/NativeListConfig.json +3 -0
- package/package.json +6 -5
- package/scripts/enable-container-slots.mjs +58 -0
- package/scripts/enable-container-slots.test.mjs +58 -0
- package/src/NativeList.nitro.ts +3 -0
- package/src/NativeList.tsx +84 -2
- package/src/NativeList.types.ts +14 -1
- package/src/NativeList.web.tsx +5 -0
- package/src/models.ts +38 -1
- package/src/validation.ts +146 -4
|
@@ -35,6 +35,11 @@ using namespace margelo::nitro::nativelist::views;
|
|
|
35
35
|
+ (BOOL)shouldBeRecycled;
|
|
36
36
|
@end
|
|
37
37
|
|
|
38
|
+
@interface UIView (NativeListContainerSlots)
|
|
39
|
+
- (void)mountContainerSlot:(UIView *)view atIndex:(NSInteger)index;
|
|
40
|
+
- (void)unmountContainerSlot:(UIView *)view;
|
|
41
|
+
@end
|
|
42
|
+
|
|
38
43
|
@implementation HybridNativeListComponent {
|
|
39
44
|
std::shared_ptr<HybridNativeListSpecSwift> _hybridView;
|
|
40
45
|
BOOL _didDropView;
|
|
@@ -59,6 +64,15 @@ using namespace margelo::nitro::nativelist::views;
|
|
|
59
64
|
return self;
|
|
60
65
|
}
|
|
61
66
|
|
|
67
|
+
// NativeList container slot mounting
|
|
68
|
+
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)child index:(NSInteger)index {
|
|
69
|
+
[self.contentView mountContainerSlot:child atIndex:index];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)child index:(NSInteger)index {
|
|
73
|
+
[self.contentView unmountContainerSlot:child];
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
- (void) updateView {
|
|
63
77
|
// 1. Get Swift part
|
|
64
78
|
NativeListModule::HybridNativeListSpec_cxx& swiftPart = _hybridView->getSwiftPart();
|
|
@@ -104,6 +118,24 @@ using namespace margelo::nitro::nativelist::views;
|
|
|
104
118
|
: !newViewProps.snapshotJson.hasSameValue(oldViewProps->snapshotJson)) {
|
|
105
119
|
swiftPart.setSnapshotJson(newViewProps.snapshotJson.get());
|
|
106
120
|
}
|
|
121
|
+
// containerSlotHeightsJson: string
|
|
122
|
+
if (oldViewProps == nullptr
|
|
123
|
+
? newViewProps.containerSlotHeightsJson.isProvided()
|
|
124
|
+
: !newViewProps.containerSlotHeightsJson.hasSameValue(oldViewProps->containerSlotHeightsJson)) {
|
|
125
|
+
swiftPart.setContainerSlotHeightsJson(newViewProps.containerSlotHeightsJson.get());
|
|
126
|
+
}
|
|
127
|
+
// scrollPositionThresholdsJson: string
|
|
128
|
+
if (oldViewProps == nullptr
|
|
129
|
+
? newViewProps.scrollPositionThresholdsJson.isProvided()
|
|
130
|
+
: !newViewProps.scrollPositionThresholdsJson.hasSameValue(oldViewProps->scrollPositionThresholdsJson)) {
|
|
131
|
+
swiftPart.setScrollPositionThresholdsJson(newViewProps.scrollPositionThresholdsJson.get());
|
|
132
|
+
}
|
|
133
|
+
// onScrollPositionThresholdChange: optional
|
|
134
|
+
if (oldViewProps == nullptr
|
|
135
|
+
? newViewProps.onScrollPositionThresholdChange.isProvided()
|
|
136
|
+
: !newViewProps.onScrollPositionThresholdChange.hasSameValue(oldViewProps->onScrollPositionThresholdChange)) {
|
|
137
|
+
swiftPart.setOnScrollPositionThresholdChange(newViewProps.onScrollPositionThresholdChange.get());
|
|
138
|
+
}
|
|
107
139
|
// keyboardDismissMode: enum
|
|
108
140
|
if (oldViewProps == nullptr
|
|
109
141
|
? newViewProps.keyboardDismissMode.isProvided()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// Func_void_bool.swift
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
import NitroModules
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Wraps a Swift `(_ isBeyondThreshold: Bool) -> Void` as a class.
|
|
12
|
+
* This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
|
|
13
|
+
*/
|
|
14
|
+
public final class Func_void_bool {
|
|
15
|
+
public typealias bridge = margelo.nitro.nativelist.bridge.swift
|
|
16
|
+
|
|
17
|
+
private let closure: (_ isBeyondThreshold: Bool) -> Void
|
|
18
|
+
|
|
19
|
+
public init(_ closure: @escaping (_ isBeyondThreshold: Bool) -> Void) {
|
|
20
|
+
self.closure = closure
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@inline(__always)
|
|
24
|
+
public func call(isBeyondThreshold: Bool) -> Void {
|
|
25
|
+
self.closure(isBeyondThreshold)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Casts this instance to a retained unsafe raw pointer.
|
|
30
|
+
* This acquires one additional strong reference on the object!
|
|
31
|
+
*/
|
|
32
|
+
@inline(__always)
|
|
33
|
+
public func toUnsafe() -> UnsafeMutableRawPointer {
|
|
34
|
+
return Unmanaged.passRetained(self).toOpaque()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Casts an unsafe pointer to a `Func_void_bool`.
|
|
39
|
+
* The pointer has to be a retained opaque `Unmanaged<Func_void_bool>`.
|
|
40
|
+
* This removes one strong reference from the object!
|
|
41
|
+
*/
|
|
42
|
+
@inline(__always)
|
|
43
|
+
public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_bool {
|
|
44
|
+
return Unmanaged<Func_void_bool>.fromOpaque(pointer).takeRetainedValue()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -11,6 +11,9 @@ import NitroModules
|
|
|
11
11
|
public protocol HybridNativeListSpec_protocol: HybridObject, HybridView {
|
|
12
12
|
// Properties
|
|
13
13
|
var snapshotJson: String { get set }
|
|
14
|
+
var containerSlotHeightsJson: String { get set }
|
|
15
|
+
var scrollPositionThresholdsJson: String { get set }
|
|
16
|
+
var onScrollPositionThresholdChange: ((_ isBeyondThreshold: Bool) -> Void)? { get set }
|
|
14
17
|
var keyboardDismissMode: NativeListKeyboardDismissMode { get set }
|
|
15
18
|
var keyboardShouldPersistTaps: NativeListKeyboardShouldPersistTaps { get set }
|
|
16
19
|
var onRowAction: ((_ payloadJson: String) -> Void)? { get set }
|
|
@@ -132,6 +132,60 @@ open class HybridNativeListSpec_cxx {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
public final var containerSlotHeightsJson: std.string {
|
|
136
|
+
@inline(__always)
|
|
137
|
+
get {
|
|
138
|
+
return std.string(self.__implementation.containerSlotHeightsJson)
|
|
139
|
+
}
|
|
140
|
+
@inline(__always)
|
|
141
|
+
set {
|
|
142
|
+
self.__implementation.containerSlotHeightsJson = String(newValue)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public final var scrollPositionThresholdsJson: std.string {
|
|
147
|
+
@inline(__always)
|
|
148
|
+
get {
|
|
149
|
+
return std.string(self.__implementation.scrollPositionThresholdsJson)
|
|
150
|
+
}
|
|
151
|
+
@inline(__always)
|
|
152
|
+
set {
|
|
153
|
+
self.__implementation.scrollPositionThresholdsJson = String(newValue)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public final var onScrollPositionThresholdChange: bridge.std__optional_std__function_void_bool____isBeyondThreshold______ {
|
|
158
|
+
@inline(__always)
|
|
159
|
+
get {
|
|
160
|
+
return { () -> bridge.std__optional_std__function_void_bool____isBeyondThreshold______ in
|
|
161
|
+
if let __unwrappedValue = self.__implementation.onScrollPositionThresholdChange {
|
|
162
|
+
return bridge.create_std__optional_std__function_void_bool____isBeyondThreshold______({ () -> bridge.Func_void_bool in
|
|
163
|
+
let __closureWrapper = Func_void_bool(__unwrappedValue)
|
|
164
|
+
return bridge.create_Func_void_bool(__closureWrapper.toUnsafe())
|
|
165
|
+
}())
|
|
166
|
+
} else {
|
|
167
|
+
return .init()
|
|
168
|
+
}
|
|
169
|
+
}()
|
|
170
|
+
}
|
|
171
|
+
@inline(__always)
|
|
172
|
+
set {
|
|
173
|
+
self.__implementation.onScrollPositionThresholdChange = { () -> ((_ isBeyondThreshold: Bool) -> Void)? in
|
|
174
|
+
if bridge.has_value_std__optional_std__function_void_bool____isBeyondThreshold______(newValue) {
|
|
175
|
+
let __unwrapped = bridge.get_std__optional_std__function_void_bool____isBeyondThreshold______(newValue)
|
|
176
|
+
return { () -> (Bool) -> Void in
|
|
177
|
+
let __wrappedFunction = bridge.wrap_Func_void_bool(__unwrapped)
|
|
178
|
+
return { (__isBeyondThreshold: Bool) -> Void in
|
|
179
|
+
__wrappedFunction.call(__isBeyondThreshold)
|
|
180
|
+
}
|
|
181
|
+
}()
|
|
182
|
+
} else {
|
|
183
|
+
return nil
|
|
184
|
+
}
|
|
185
|
+
}()
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
135
189
|
public final var keyboardDismissMode: Int32 {
|
|
136
190
|
@inline(__always)
|
|
137
191
|
get {
|
|
@@ -16,6 +16,12 @@ namespace margelo::nitro::nativelist {
|
|
|
16
16
|
registerHybrids(this, [](Prototype& prototype) {
|
|
17
17
|
prototype.registerHybridGetter("snapshotJson", &HybridNativeListSpec::getSnapshotJson);
|
|
18
18
|
prototype.registerHybridSetter("snapshotJson", &HybridNativeListSpec::setSnapshotJson);
|
|
19
|
+
prototype.registerHybridGetter("containerSlotHeightsJson", &HybridNativeListSpec::getContainerSlotHeightsJson);
|
|
20
|
+
prototype.registerHybridSetter("containerSlotHeightsJson", &HybridNativeListSpec::setContainerSlotHeightsJson);
|
|
21
|
+
prototype.registerHybridGetter("scrollPositionThresholdsJson", &HybridNativeListSpec::getScrollPositionThresholdsJson);
|
|
22
|
+
prototype.registerHybridSetter("scrollPositionThresholdsJson", &HybridNativeListSpec::setScrollPositionThresholdsJson);
|
|
23
|
+
prototype.registerHybridGetter("onScrollPositionThresholdChange", &HybridNativeListSpec::getOnScrollPositionThresholdChange);
|
|
24
|
+
prototype.registerHybridSetter("onScrollPositionThresholdChange", &HybridNativeListSpec::setOnScrollPositionThresholdChange);
|
|
19
25
|
prototype.registerHybridGetter("keyboardDismissMode", &HybridNativeListSpec::getKeyboardDismissMode);
|
|
20
26
|
prototype.registerHybridSetter("keyboardDismissMode", &HybridNativeListSpec::setKeyboardDismissMode);
|
|
21
27
|
prototype.registerHybridGetter("keyboardShouldPersistTaps", &HybridNativeListSpec::getKeyboardShouldPersistTaps);
|
|
@@ -21,10 +21,10 @@ namespace margelo::nitro::nativelist { enum class NativeListKeyboardShouldPersis
|
|
|
21
21
|
namespace margelo::nitro::nativelist { enum class NativeListScrollAlignment; }
|
|
22
22
|
|
|
23
23
|
#include <string>
|
|
24
|
-
#include "NativeListKeyboardDismissMode.hpp"
|
|
25
|
-
#include "NativeListKeyboardShouldPersistTaps.hpp"
|
|
26
24
|
#include <functional>
|
|
27
25
|
#include <optional>
|
|
26
|
+
#include "NativeListKeyboardDismissMode.hpp"
|
|
27
|
+
#include "NativeListKeyboardShouldPersistTaps.hpp"
|
|
28
28
|
#include "NativeListScrollAlignment.hpp"
|
|
29
29
|
|
|
30
30
|
namespace margelo::nitro::nativelist {
|
|
@@ -56,6 +56,12 @@ namespace margelo::nitro::nativelist {
|
|
|
56
56
|
// Properties
|
|
57
57
|
virtual std::string getSnapshotJson() = 0;
|
|
58
58
|
virtual void setSnapshotJson(const std::string& snapshotJson) = 0;
|
|
59
|
+
virtual std::string getContainerSlotHeightsJson() = 0;
|
|
60
|
+
virtual void setContainerSlotHeightsJson(const std::string& containerSlotHeightsJson) = 0;
|
|
61
|
+
virtual std::string getScrollPositionThresholdsJson() = 0;
|
|
62
|
+
virtual void setScrollPositionThresholdsJson(const std::string& scrollPositionThresholdsJson) = 0;
|
|
63
|
+
virtual std::optional<std::function<void(bool /* isBeyondThreshold */)>> getOnScrollPositionThresholdChange() = 0;
|
|
64
|
+
virtual void setOnScrollPositionThresholdChange(const std::optional<std::function<void(bool /* isBeyondThreshold */)>>& onScrollPositionThresholdChange) = 0;
|
|
59
65
|
virtual NativeListKeyboardDismissMode getKeyboardDismissMode() = 0;
|
|
60
66
|
virtual void setKeyboardDismissMode(NativeListKeyboardDismissMode keyboardDismissMode) = 0;
|
|
61
67
|
virtual NativeListKeyboardShouldPersistTaps getKeyboardShouldPersistTaps() = 0;
|
|
@@ -21,6 +21,9 @@ namespace margelo::nitro::nativelist::views {
|
|
|
21
21
|
const react::RawProps& rawProps):
|
|
22
22
|
react::ViewProps(context, sourceProps, rawProps, filterObjectKeys),
|
|
23
23
|
snapshotJson(nitro::ReactProp<std::string>::fromRawValue("NativeList", "snapshotJson", rawProps, sourceProps.snapshotJson)),
|
|
24
|
+
containerSlotHeightsJson(nitro::ReactProp<std::string>::fromRawValue("NativeList", "containerSlotHeightsJson", rawProps, sourceProps.containerSlotHeightsJson)),
|
|
25
|
+
scrollPositionThresholdsJson(nitro::ReactProp<std::string>::fromRawValue("NativeList", "scrollPositionThresholdsJson", rawProps, sourceProps.scrollPositionThresholdsJson)),
|
|
26
|
+
onScrollPositionThresholdChange(nitro::ReactProp<std::optional<std::function<void(bool /* isBeyondThreshold */)>>>::fromRawValue("NativeList", "onScrollPositionThresholdChange", rawProps, sourceProps.onScrollPositionThresholdChange)),
|
|
24
27
|
keyboardDismissMode(nitro::ReactProp<NativeListKeyboardDismissMode>::fromRawValue("NativeList", "keyboardDismissMode", rawProps, sourceProps.keyboardDismissMode)),
|
|
25
28
|
keyboardShouldPersistTaps(nitro::ReactProp<NativeListKeyboardShouldPersistTaps>::fromRawValue("NativeList", "keyboardShouldPersistTaps", rawProps, sourceProps.keyboardShouldPersistTaps)),
|
|
26
29
|
onRowAction(nitro::ReactProp<std::optional<std::function<void(const std::string& /* payloadJson */)>>>::fromRawValue("NativeList", "onRowAction", rawProps, sourceProps.onRowAction)),
|
|
@@ -34,6 +37,9 @@ namespace margelo::nitro::nativelist::views {
|
|
|
34
37
|
bool HybridNativeListProps::filterObjectKeys(const std::string& propName) {
|
|
35
38
|
switch (hashString(propName)) {
|
|
36
39
|
case hashString("snapshotJson"): return true;
|
|
40
|
+
case hashString("containerSlotHeightsJson"): return true;
|
|
41
|
+
case hashString("scrollPositionThresholdsJson"): return true;
|
|
42
|
+
case hashString("onScrollPositionThresholdChange"): return true;
|
|
37
43
|
case hashString("keyboardDismissMode"): return true;
|
|
38
44
|
case hashString("keyboardShouldPersistTaps"): return true;
|
|
39
45
|
case hashString("onRowAction"): return true;
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
#include <string>
|
|
19
19
|
|
|
20
20
|
#include <string>
|
|
21
|
-
#include "NativeListKeyboardDismissMode.hpp"
|
|
22
|
-
#include "NativeListKeyboardShouldPersistTaps.hpp"
|
|
23
21
|
#include <functional>
|
|
24
22
|
#include <optional>
|
|
23
|
+
#include "NativeListKeyboardDismissMode.hpp"
|
|
24
|
+
#include "NativeListKeyboardShouldPersistTaps.hpp"
|
|
25
25
|
#include <memory>
|
|
26
26
|
#include "HybridNativeListSpec.hpp"
|
|
27
27
|
|
|
@@ -46,6 +46,9 @@ namespace margelo::nitro::nativelist::views {
|
|
|
46
46
|
|
|
47
47
|
public:
|
|
48
48
|
nitro::ReactProp<std::string> snapshotJson;
|
|
49
|
+
nitro::ReactProp<std::string> containerSlotHeightsJson;
|
|
50
|
+
nitro::ReactProp<std::string> scrollPositionThresholdsJson;
|
|
51
|
+
nitro::ReactProp<std::optional<std::function<void(bool /* isBeyondThreshold */)>>> onScrollPositionThresholdChange;
|
|
49
52
|
nitro::ReactProp<NativeListKeyboardDismissMode> keyboardDismissMode;
|
|
50
53
|
nitro::ReactProp<NativeListKeyboardShouldPersistTaps> keyboardShouldPersistTaps;
|
|
51
54
|
nitro::ReactProp<std::optional<std::function<void(const std::string& /* payloadJson */)>>> onRowAction;
|
|
@@ -59,6 +62,9 @@ namespace margelo::nitro::nativelist::views {
|
|
|
59
62
|
[[nodiscard]]
|
|
60
63
|
bool hasSameProps(const HybridNativeListProps& other) const noexcept {
|
|
61
64
|
return snapshotJson.hasSameValue(other.snapshotJson) &&
|
|
65
|
+
containerSlotHeightsJson.hasSameValue(other.containerSlotHeightsJson) &&
|
|
66
|
+
scrollPositionThresholdsJson.hasSameValue(other.scrollPositionThresholdsJson) &&
|
|
67
|
+
onScrollPositionThresholdChange.hasSameValue(other.onScrollPositionThresholdChange) &&
|
|
62
68
|
keyboardDismissMode.hasSameValue(other.keyboardDismissMode) &&
|
|
63
69
|
keyboardShouldPersistTaps.hasSameValue(other.keyboardShouldPersistTaps) &&
|
|
64
70
|
onRowAction.hasSameValue(other.onRowAction) &&
|
|
@@ -73,6 +79,9 @@ namespace margelo::nitro::nativelist::views {
|
|
|
73
79
|
[[nodiscard]]
|
|
74
80
|
bool hasAnyProvidedProps() const noexcept {
|
|
75
81
|
return snapshotJson.isProvided() ||
|
|
82
|
+
containerSlotHeightsJson.isProvided() ||
|
|
83
|
+
scrollPositionThresholdsJson.isProvided() ||
|
|
84
|
+
onScrollPositionThresholdChange.isProvided() ||
|
|
76
85
|
keyboardDismissMode.isProvided() ||
|
|
77
86
|
keyboardShouldPersistTaps.isProvided() ||
|
|
78
87
|
onRowAction.isProvided() ||
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
"directEventTypes": {},
|
|
6
6
|
"validAttributes": {
|
|
7
7
|
"snapshotJson": true,
|
|
8
|
+
"containerSlotHeightsJson": true,
|
|
9
|
+
"scrollPositionThresholdsJson": true,
|
|
10
|
+
"onScrollPositionThresholdChange": true,
|
|
8
11
|
"keyboardDismissMode": true,
|
|
9
12
|
"keyboardShouldPersistTaps": true,
|
|
10
13
|
"onRowAction": true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-native-list",
|
|
3
|
-
"version": "3.0.157-alpha.
|
|
3
|
+
"version": "3.0.157-alpha.255",
|
|
4
4
|
"description": "Template-driven native RecyclerView and UICollectionView for React Native",
|
|
5
5
|
"source": "./src/index.ts",
|
|
6
6
|
"main": "./lib/module/index.js",
|
|
@@ -30,12 +30,13 @@
|
|
|
30
30
|
"!android/build",
|
|
31
31
|
"!android/app/build",
|
|
32
32
|
"!**/__tests__",
|
|
33
|
-
"!**/*.map"
|
|
33
|
+
"!**/*.map",
|
|
34
|
+
"scripts"
|
|
34
35
|
],
|
|
35
36
|
"scripts": {
|
|
36
37
|
"clean": "del-cli lib",
|
|
37
38
|
"prepare": "del-cli lib && bob build && cp -r nitrogen lib/nitrogen",
|
|
38
|
-
"nitrogen": "nitrogen",
|
|
39
|
+
"nitrogen": "nitrogen && node scripts/enable-container-slots.mjs",
|
|
39
40
|
"typecheck": "tsc -b",
|
|
40
41
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
41
42
|
"test": "jest --runInBand",
|
|
@@ -83,8 +84,8 @@
|
|
|
83
84
|
"typescript": "^5.9.2"
|
|
84
85
|
},
|
|
85
86
|
"peerDependencies": {
|
|
86
|
-
"@onekeyfe/react-native-image": "3.0.157-alpha.
|
|
87
|
-
"@onekeyfe/react-native-native-logger": "3.0.157-alpha.
|
|
87
|
+
"@onekeyfe/react-native-image": "3.0.157-alpha.255",
|
|
88
|
+
"@onekeyfe/react-native-native-logger": "3.0.157-alpha.255",
|
|
88
89
|
"react": "*",
|
|
89
90
|
"react-native": "*",
|
|
90
91
|
"react-native-nitro-modules": "0.37.0"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Nitro 0.37 emits leaf view managers. Container slots need explicit Fabric
|
|
2
|
+
// child ownership, reapplied deterministically after every nitrogen build.
|
|
3
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
8
|
+
const android = join(root, 'nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListManager.kt');
|
|
9
|
+
const ios = join(root, 'nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm');
|
|
10
|
+
const marker = '// NativeList container slot mounting';
|
|
11
|
+
let kotlin = readFileSync(android, 'utf8');
|
|
12
|
+
if (!kotlin.includes(marker)) {
|
|
13
|
+
if (!kotlin.includes('SimpleViewManager<View>')) throw new Error('Unexpected Nitro Android manager shape');
|
|
14
|
+
kotlin = kotlin.replace('import android.view.View\n', 'import android.view.View\nimport android.view.ViewGroup\n');
|
|
15
|
+
kotlin = kotlin.replace('import com.facebook.react.uimanager.SimpleViewManager', 'import com.facebook.react.uimanager.ViewGroupManager');
|
|
16
|
+
kotlin = kotlin.replace('SimpleViewManager<View>()', 'ViewGroupManager<ViewGroup>()');
|
|
17
|
+
kotlin = kotlin.replaceAll('view: View', 'view: ViewGroup');
|
|
18
|
+
kotlin = kotlin.replace('): View {', '): ViewGroup {').replace('): View? {', '): ViewGroup? {');
|
|
19
|
+
kotlin = kotlin.replace('val view = hybridView.view', 'val view = hybridView.view as ViewGroup');
|
|
20
|
+
kotlin = kotlin.replace('return hybridView.view', 'return hybridView.view as ViewGroup');
|
|
21
|
+
kotlin = kotlin.replace(' override fun getName(): String {', ` ${marker}
|
|
22
|
+
override fun needsCustomLayoutForChildren(): Boolean = true
|
|
23
|
+
override fun addView(parent: ViewGroup, child: View, index: Int) {
|
|
24
|
+
(parent as NativeListView).mountContainerSlot(child, index)
|
|
25
|
+
}
|
|
26
|
+
override fun getChildCount(parent: ViewGroup): Int = (parent as NativeListView).containerSlotCount()
|
|
27
|
+
override fun getChildAt(parent: ViewGroup, index: Int): View = (parent as NativeListView).containerSlotAt(index)
|
|
28
|
+
override fun removeViewAt(parent: ViewGroup, index: Int) {
|
|
29
|
+
(parent as NativeListView).unmountContainerSlot(index)
|
|
30
|
+
}
|
|
31
|
+
override fun removeAllViews(parent: ViewGroup) {
|
|
32
|
+
while (getChildCount(parent) > 0) removeViewAt(parent, getChildCount(parent) - 1)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
override fun getName(): String {`);
|
|
36
|
+
writeFileSync(android, kotlin);
|
|
37
|
+
}
|
|
38
|
+
let objc = readFileSync(ios, 'utf8');
|
|
39
|
+
if (!objc.includes(marker)) {
|
|
40
|
+
if (!objc.includes('- (void) updateView {')) throw new Error('Unexpected Nitro iOS component shape');
|
|
41
|
+
objc = objc.replace('@implementation HybridNativeListComponent {', `@interface UIView (NativeListContainerSlots)
|
|
42
|
+
- (void)mountContainerSlot:(UIView *)view atIndex:(NSInteger)index;
|
|
43
|
+
- (void)unmountContainerSlot:(UIView *)view;
|
|
44
|
+
@end
|
|
45
|
+
|
|
46
|
+
@implementation HybridNativeListComponent {`);
|
|
47
|
+
objc = objc.replace('- (void) updateView {', `${marker}
|
|
48
|
+
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)child index:(NSInteger)index {
|
|
49
|
+
[self.contentView mountContainerSlot:child atIndex:index];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)child index:(NSInteger)index {
|
|
53
|
+
[self.contentView unmountContainerSlot:child];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
- (void) updateView {`);
|
|
57
|
+
writeFileSync(ios, objc);
|
|
58
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
3
|
+
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { test } from 'node:test';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
const script = readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'enable-container-slots.mjs'), 'utf8');
|
|
10
|
+
const androidPath = 'nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListManager.kt';
|
|
11
|
+
const iosPath = 'nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm';
|
|
12
|
+
|
|
13
|
+
function fixture(android, ios) {
|
|
14
|
+
const root = mkdtempSync(join(tmpdir(), 'native-list-slots-'));
|
|
15
|
+
for (const [relative, content] of [['scripts/enable-container-slots.mjs', script], [androidPath, android], [iosPath, ios]]) {
|
|
16
|
+
const path = join(root, relative);
|
|
17
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
18
|
+
writeFileSync(path, content);
|
|
19
|
+
}
|
|
20
|
+
return root;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test('post-nitrogen child ownership transformation is idempotent', () => {
|
|
24
|
+
const root = fixture(`import android.view.View
|
|
25
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
|
26
|
+
public class HybridNativeListManager: SimpleViewManager<View>() {
|
|
27
|
+
override fun getName(): String { return "NativeList" }
|
|
28
|
+
override fun createViewInstance(reactContext: ThemedReactContext): View {
|
|
29
|
+
val view = hybridView.view
|
|
30
|
+
return view
|
|
31
|
+
}
|
|
32
|
+
}`, `@implementation HybridNativeListComponent {
|
|
33
|
+
}
|
|
34
|
+
- (void) updateView {
|
|
35
|
+
}`);
|
|
36
|
+
try {
|
|
37
|
+
const path = join(root, 'scripts/enable-container-slots.mjs');
|
|
38
|
+
execFileSync(process.execPath, [path]);
|
|
39
|
+
const firstAndroid = readFileSync(join(root, androidPath), 'utf8');
|
|
40
|
+
const firstIOS = readFileSync(join(root, iosPath), 'utf8');
|
|
41
|
+
assert.match(firstAndroid, /ViewGroupManager<ViewGroup>/);
|
|
42
|
+
assert.match(firstAndroid, /unmountContainerSlot\(index\)/);
|
|
43
|
+
assert.match(firstIOS, /mountChildComponentView/);
|
|
44
|
+
assert.match(firstIOS, /unmountContainerSlot:child/);
|
|
45
|
+
execFileSync(process.execPath, [path]);
|
|
46
|
+
assert.equal(readFileSync(join(root, androidPath), 'utf8'), firstAndroid);
|
|
47
|
+
assert.equal(readFileSync(join(root, iosPath), 'utf8'), firstIOS);
|
|
48
|
+
} finally { rmSync(root, { recursive: true, force: true }); }
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('unexpected nitrogen output fails instead of silently losing child mounting', () => {
|
|
52
|
+
const root = fixture('unrecognized generator output', '- (void) updateView {');
|
|
53
|
+
try {
|
|
54
|
+
const result = spawnSync(process.execPath, [join(root, 'scripts/enable-container-slots.mjs')]);
|
|
55
|
+
assert.notEqual(result.status, 0);
|
|
56
|
+
assert.match(result.stderr.toString(), /Unexpected Nitro Android manager shape/);
|
|
57
|
+
} finally { rmSync(root, { recursive: true, force: true }); }
|
|
58
|
+
});
|
package/src/NativeList.nitro.ts
CHANGED
|
@@ -11,6 +11,9 @@ import type {
|
|
|
11
11
|
*/
|
|
12
12
|
export interface NativeListNativeProps extends HybridViewProps {
|
|
13
13
|
snapshotJson: string;
|
|
14
|
+
containerSlotHeightsJson: string;
|
|
15
|
+
scrollPositionThresholdsJson: string;
|
|
16
|
+
onScrollPositionThresholdChange?: (isBeyondThreshold: boolean) => void;
|
|
14
17
|
keyboardDismissMode: NativeListKeyboardDismissMode;
|
|
15
18
|
keyboardShouldPersistTaps: NativeListKeyboardShouldPersistTaps;
|
|
16
19
|
onRowAction?: (payloadJson: string) => void;
|
package/src/NativeList.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import React, {
|
|
|
4
4
|
useImperativeHandle,
|
|
5
5
|
useMemo,
|
|
6
6
|
useRef,
|
|
7
|
+
useState,
|
|
7
8
|
} from 'react';
|
|
8
9
|
import {
|
|
9
10
|
OneKeyImageCache,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
NativeAvatarPrefetchModel,
|
|
14
15
|
NativeAvatarPrefetchQueue,
|
|
15
16
|
} from './avatarPrefetch';
|
|
17
|
+
import { View } from 'react-native';
|
|
16
18
|
import { callback, getHostComponent } from 'react-native-nitro-modules';
|
|
17
19
|
import type {
|
|
18
20
|
NativeListMethods,
|
|
@@ -96,6 +98,10 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
96
98
|
function NativeList(
|
|
97
99
|
{
|
|
98
100
|
snapshot,
|
|
101
|
+
listHeader,
|
|
102
|
+
listFooter,
|
|
103
|
+
listEmpty,
|
|
104
|
+
scrollPositionThresholds,
|
|
99
105
|
webVirtualizationEnabled: _webVirtualizationEnabled,
|
|
100
106
|
webSectionIndexContainerRef: _webSectionIndexContainerRef,
|
|
101
107
|
keyboardDismissMode = 'none',
|
|
@@ -106,6 +112,7 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
106
112
|
onReorder,
|
|
107
113
|
onEndReached,
|
|
108
114
|
onVisibleRangeChanged,
|
|
115
|
+
onScrollPositionThresholdChange,
|
|
109
116
|
onRefresh,
|
|
110
117
|
onScrollToIndexFailed,
|
|
111
118
|
initialScrollIndex,
|
|
@@ -116,6 +123,21 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
116
123
|
},
|
|
117
124
|
forwardedRef
|
|
118
125
|
) {
|
|
126
|
+
const [slotHeights, setSlotHeights] = useState([0, 0, 0]);
|
|
127
|
+
const containerSlotHeightsJson = JSON.stringify(slotHeights);
|
|
128
|
+
const slotInset =
|
|
129
|
+
snapshot.layout.contentPaddingHorizontal ??
|
|
130
|
+
snapshot.layout.contentPadding ??
|
|
131
|
+
0;
|
|
132
|
+
if (
|
|
133
|
+
snapshot.layout.orientation === 'horizontal' &&
|
|
134
|
+
(listHeader || listEmpty || listFooter)
|
|
135
|
+
) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
'NativeList container slots require vertical orientation'
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
const slotContents = [listHeader, listEmpty, listFooter];
|
|
119
141
|
const normalizedKeyboardShouldPersistTaps =
|
|
120
142
|
keyboardShouldPersistTaps === true
|
|
121
143
|
? 'always'
|
|
@@ -130,6 +152,7 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
130
152
|
onReorder,
|
|
131
153
|
onEndReached,
|
|
132
154
|
onVisibleRangeChanged,
|
|
155
|
+
onScrollPositionThresholdChange,
|
|
133
156
|
onRefresh,
|
|
134
157
|
onScrollToIndexFailed,
|
|
135
158
|
});
|
|
@@ -140,9 +163,29 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
140
163
|
onReorder,
|
|
141
164
|
onEndReached,
|
|
142
165
|
onVisibleRangeChanged,
|
|
166
|
+
onScrollPositionThresholdChange,
|
|
143
167
|
onRefresh,
|
|
144
168
|
onScrollToIndexFailed,
|
|
145
169
|
};
|
|
170
|
+
const scrollPositionThresholdsJson = useMemo(() => {
|
|
171
|
+
if (
|
|
172
|
+
!scrollPositionThresholds ||
|
|
173
|
+
scrollPositionThresholds.enabled === false
|
|
174
|
+
)
|
|
175
|
+
return '';
|
|
176
|
+
const { start, end } = scrollPositionThresholds;
|
|
177
|
+
if (
|
|
178
|
+
!Number.isFinite(start) ||
|
|
179
|
+
!Number.isFinite(end) ||
|
|
180
|
+
start < 0 ||
|
|
181
|
+
end <= start
|
|
182
|
+
) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
'scrollPositionThresholds requires 0 <= start < end and finite values'
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
return JSON.stringify({ start, end });
|
|
188
|
+
}, [scrollPositionThresholds]);
|
|
146
189
|
const snapshotJson = useMemo(() => serializeSnapshot(snapshot), [snapshot]);
|
|
147
190
|
const snapshotRef = useRef(snapshot);
|
|
148
191
|
const previousSnapshotJsonRef = useRef(snapshotJson);
|
|
@@ -391,8 +434,13 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
391
434
|
}),
|
|
392
435
|
onRowAction: callback((payloadJson: string) => {
|
|
393
436
|
const payload = parsePayload<RowActionEvent>(payloadJson);
|
|
394
|
-
if (
|
|
437
|
+
if (
|
|
438
|
+
payload.actionKey === 'nativeList.refresh' &&
|
|
439
|
+
payload.rowKey === undefined
|
|
440
|
+
) {
|
|
395
441
|
callbacksRef.current.onRefresh?.();
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
396
444
|
callbacksRef.current.onRowAction?.(payload);
|
|
397
445
|
}),
|
|
398
446
|
onActionAnchorInvalidated: callback((payloadJson: string) => {
|
|
@@ -427,6 +475,13 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
427
475
|
parsePayload<EndReachedEvent>(payloadJson)
|
|
428
476
|
);
|
|
429
477
|
}),
|
|
478
|
+
onScrollPositionThresholdChange: callback(
|
|
479
|
+
(isBeyondThreshold: boolean) => {
|
|
480
|
+
callbacksRef.current.onScrollPositionThresholdChange?.({
|
|
481
|
+
isBeyondThreshold,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
),
|
|
430
485
|
onVisibleRangeChanged: callback((payloadJson: string) => {
|
|
431
486
|
const payload = parsePayload<VisibleRangeChangedEvent>(payloadJson);
|
|
432
487
|
const previous = avatarRangeRef.current;
|
|
@@ -450,9 +505,36 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
450
505
|
{...viewProps}
|
|
451
506
|
{...nativeCallbacks}
|
|
452
507
|
snapshotJson={snapshotJson}
|
|
508
|
+
containerSlotHeightsJson={containerSlotHeightsJson}
|
|
509
|
+
scrollPositionThresholdsJson={scrollPositionThresholdsJson}
|
|
453
510
|
keyboardDismissMode={keyboardDismissMode}
|
|
454
511
|
keyboardShouldPersistTaps={normalizedKeyboardShouldPersistTaps}
|
|
455
|
-
|
|
512
|
+
>
|
|
513
|
+
{slotContents.map((content, index) => (
|
|
514
|
+
<View
|
|
515
|
+
key={index}
|
|
516
|
+
collapsable={false}
|
|
517
|
+
style={{
|
|
518
|
+
position: 'absolute',
|
|
519
|
+
left: slotInset,
|
|
520
|
+
right: slotInset,
|
|
521
|
+
top: 0,
|
|
522
|
+
}}
|
|
523
|
+
onLayout={(event) => {
|
|
524
|
+
const height = Math.ceil(event.nativeEvent.layout.height);
|
|
525
|
+
setSlotHeights((previous) =>
|
|
526
|
+
previous[index] === height
|
|
527
|
+
? previous
|
|
528
|
+
: previous.map((value, slot) =>
|
|
529
|
+
slot === index ? height : value
|
|
530
|
+
)
|
|
531
|
+
);
|
|
532
|
+
}}
|
|
533
|
+
>
|
|
534
|
+
{content}
|
|
535
|
+
</View>
|
|
536
|
+
))}
|
|
537
|
+
</NativeListHost>
|
|
456
538
|
);
|
|
457
539
|
}
|
|
458
540
|
);
|
package/src/NativeList.types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RefObject } from 'react';
|
|
1
|
+
import type { ReactNode, RefObject } from 'react';
|
|
2
2
|
import type { View, ViewProps } from 'react-native';
|
|
3
3
|
import type {
|
|
4
4
|
ActionAnchorInvalidatedEvent,
|
|
@@ -119,6 +119,10 @@ export type NativeListRef = Readonly<{
|
|
|
119
119
|
export type NativeListProps = Omit<ViewProps, 'children'> &
|
|
120
120
|
Readonly<{
|
|
121
121
|
snapshot: NativeListSnapshot;
|
|
122
|
+
/** Native-only, container-owned scroll content. Never rendered as a data row. */
|
|
123
|
+
listHeader?: ReactNode;
|
|
124
|
+
listFooter?: ReactNode;
|
|
125
|
+
listEmpty?: ReactNode;
|
|
122
126
|
/** Web only. Defaults to true and is ignored by the native host. */
|
|
123
127
|
webVirtualizationEnabled?: boolean;
|
|
124
128
|
/** Web only. Hosts the section index inside this container. */
|
|
@@ -136,6 +140,15 @@ export type NativeListProps = Omit<ViewProps, 'children'> &
|
|
|
136
140
|
onReorder?: (event: ReorderEvent) => void;
|
|
137
141
|
onEndReached?: (event: EndReachedEvent) => void;
|
|
138
142
|
onVisibleRangeChanged?: (event: VisibleRangeChangedEvent) => void;
|
|
143
|
+
/** Native only. Emits initial state and hysteresis transitions, not scroll frames. */
|
|
144
|
+
scrollPositionThresholds?: Readonly<{
|
|
145
|
+
start: number;
|
|
146
|
+
end: number;
|
|
147
|
+
enabled?: boolean;
|
|
148
|
+
}>;
|
|
149
|
+
onScrollPositionThresholdChange?: (
|
|
150
|
+
event: Readonly<{ isBeyondThreshold: boolean }>
|
|
151
|
+
) => void;
|
|
139
152
|
onRefresh?: () => void;
|
|
140
153
|
onScrollToIndexFailed?: (info: ScrollToIndexFailedInfo) => void;
|
|
141
154
|
}> &
|
package/src/NativeList.web.tsx
CHANGED
|
@@ -82,6 +82,11 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
82
82
|
function NativeList(
|
|
83
83
|
{
|
|
84
84
|
snapshot,
|
|
85
|
+
listHeader: _listHeader,
|
|
86
|
+
listEmpty: _listEmpty,
|
|
87
|
+
listFooter: _listFooter,
|
|
88
|
+
scrollPositionThresholds: _scrollPositionThresholds,
|
|
89
|
+
onScrollPositionThresholdChange: _onScrollPositionThresholdChange,
|
|
85
90
|
webVirtualizationEnabled = true,
|
|
86
91
|
webSectionIndexContainerRef,
|
|
87
92
|
keyboardDismissMode: _keyboardDismissMode,
|