@onekeyfe/react-native-native-list 3.0.177 → 3.0.179-alpha.20261015
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/src/main/java/com/onekey/nativelist/HybridNativeList.kt +3 -0
- package/docs/SPEC.md +19 -0
- package/ios/HybridNativeList.swift +18 -0
- package/ios/RNCNativeListView.swift +87 -0
- package/lib/module/NativeList.js +24 -5
- package/lib/module/NativeList.web.js +2 -0
- package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +18 -0
- package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +4 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +10 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +12 -0
- package/lib/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +12 -0
- package/lib/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +12 -0
- package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +2 -0
- package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +22 -0
- package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +4 -0
- package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +4 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +4 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +6 -0
- package/lib/nitrogen/generated/shared/json/NativeListConfig.json +2 -0
- package/lib/typescript/src/NativeList.nitro.d.ts +2 -0
- package/lib/typescript/src/NativeList.types.d.ts +4 -0
- package/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +18 -0
- package/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +4 -0
- package/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +10 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +12 -0
- package/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +12 -0
- package/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +12 -0
- package/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +22 -0
- package/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +4 -0
- package/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +4 -0
- package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +4 -0
- package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +6 -0
- package/nitrogen/generated/shared/json/NativeListConfig.json +2 -0
- package/package.json +3 -3
- package/src/NativeList.nitro.ts +2 -0
- package/src/NativeList.tsx +25 -1
- package/src/NativeList.types.ts +4 -0
- package/src/NativeList.web.tsx +2 -0
|
@@ -31,6 +31,9 @@ class HybridNativeList(context: ThemedReactContext) : HybridNativeListSpec() {
|
|
|
31
31
|
dispatchToUi { hostView.setContainerSlotHeightsJson(value) }
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
override var topScrollEdgeHard: Boolean = false
|
|
35
|
+
override var topOverlayHeight: Double = 0.0
|
|
36
|
+
|
|
34
37
|
override var snapshotJson: String = ""
|
|
35
38
|
get() = field
|
|
36
39
|
set(value) {
|
package/docs/SPEC.md
CHANGED
|
@@ -26,6 +26,25 @@ prevention; there is no automatic fitting or shrinking pass.
|
|
|
26
26
|
|
|
27
27
|
## Public API and defaults
|
|
28
28
|
|
|
29
|
+
### iOS 26 fixed top scroll-edge overlay
|
|
30
|
+
|
|
31
|
+
Standalone `NativeList` accepts optional `topScrollEdgeEffect="hard"` and
|
|
32
|
+
`topOverlay` React content. Both are disabled by default. On iOS 26 or newer,
|
|
33
|
+
the fixed overlay is a fourth native container slot, outside the row stream and
|
|
34
|
+
outside the existing scrolling `listHeader`/`listEmpty`/`listFooter` slots.
|
|
35
|
+
Fabric measures its height; the list owns an equal top inset on its collection
|
|
36
|
+
view and binds UIKit's `UIScrollEdgeElementContainerInteraction` to that view,
|
|
37
|
+
using the hard top edge style. The list restores its original inset and effect
|
|
38
|
+
on removal, disposal and reuse. The UI thread owns all view and UIKit state;
|
|
39
|
+
there is no persistence, background-runtime state or per-frame JS callback.
|
|
40
|
+
An invisible, noninteractive native label fills the overlay host so UIKit
|
|
41
|
+
recognizes React-drawn content as a scroll-edge element.
|
|
42
|
+
Inside a CollapsiblePagerView, the pager owns this effect and inset, and child
|
|
43
|
+
lists do not opt in separately. iOS before 26, Android and Web retain their
|
|
44
|
+
existing layout and scrolling. Missing overlay or style changes nothing.
|
|
45
|
+
Acceptance requires actual iOS 26 initial layout, long/short/empty scrolling,
|
|
46
|
+
refresh and overlay replacement/removal, including dark mode.
|
|
47
|
+
|
|
29
48
|
[README.md](../README.md) documents the snapshot, patch, selection, events and
|
|
30
49
|
imperative API; [STYLE_SPEC.md](STYLE_SPEC.md) defines normalization, omitted
|
|
31
50
|
style defaults and platform differences. Height precedence is
|
|
@@ -36,6 +36,24 @@ final class HybridNativeList: HybridNativeListSpec {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
var topScrollEdgeHard = false {
|
|
40
|
+
didSet {
|
|
41
|
+
runOnMain { [weak self] in
|
|
42
|
+
self?.hostView?.setTopScrollEdge(hard: self?.topScrollEdgeHard ?? false,
|
|
43
|
+
overlayHeight: CGFloat(self?.topOverlayHeight ?? 0))
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
var topOverlayHeight: Double = 0 {
|
|
49
|
+
didSet {
|
|
50
|
+
runOnMain { [weak self] in
|
|
51
|
+
self?.hostView?.setTopScrollEdge(hard: self?.topScrollEdgeHard ?? false,
|
|
52
|
+
overlayHeight: CGFloat(self?.topOverlayHeight ?? 0))
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
var snapshotJson: String = "" {
|
|
40
58
|
didSet {
|
|
41
59
|
guard snapshotJson != oldValue else { return }
|
|
@@ -11,11 +11,27 @@ final class NativeListView: UIView {
|
|
|
11
11
|
// Each root therefore lives in a list-owned host whose origin the list alone controls, so a
|
|
12
12
|
// React layout update can never move a slot out of its native position, even for a frame.
|
|
13
13
|
private var reactContainerSlots: [(content: UIView, host: UIView)] = []
|
|
14
|
+
private let topOverlayHost = UIView()
|
|
15
|
+
private weak var topOverlayContent: UIView?
|
|
16
|
+
private var topEdgeInteraction: UIInteraction?
|
|
17
|
+
private var topEdgeMarker: UILabel?
|
|
18
|
+
private var originalTopEdgeStyle: AnyObject?
|
|
19
|
+
private var topScrollEdgeHard = false
|
|
20
|
+
private var requestedTopOverlayHeight: CGFloat = 0
|
|
21
|
+
private var appliedTopOverlayInset: CGFloat = 0
|
|
22
|
+
|
|
14
23
|
private var lastLaidOutContentHeight: CGFloat?
|
|
15
24
|
private var needsContentShrinkCorrection = false
|
|
16
25
|
private var contentShrinkCorrectionScheduled = false
|
|
17
26
|
|
|
18
27
|
@objc func mountContainerSlot(_ view: UIView, atIndex index: Int) {
|
|
28
|
+
if index == 3 {
|
|
29
|
+
topOverlayContent?.removeFromSuperview()
|
|
30
|
+
topOverlayContent = view
|
|
31
|
+
topOverlayHost.addSubview(view)
|
|
32
|
+
updateTopScrollEdge()
|
|
33
|
+
return
|
|
34
|
+
}
|
|
19
35
|
let host = UIView()
|
|
20
36
|
// Clipping to the reported height keeps new content from overlapping rows until the list
|
|
21
37
|
// has reserved space for it.
|
|
@@ -28,6 +44,12 @@ final class NativeListView: UIView {
|
|
|
28
44
|
}
|
|
29
45
|
|
|
30
46
|
@objc func unmountContainerSlot(_ view: UIView) {
|
|
47
|
+
if topOverlayContent === view {
|
|
48
|
+
view.removeFromSuperview()
|
|
49
|
+
topOverlayContent = nil
|
|
50
|
+
updateTopScrollEdge()
|
|
51
|
+
return
|
|
52
|
+
}
|
|
31
53
|
reactContainerSlots.removeAll { slot in
|
|
32
54
|
guard slot.content === view else { return false }
|
|
33
55
|
view.removeFromSuperview()
|
|
@@ -36,6 +58,66 @@ final class NativeListView: UIView {
|
|
|
36
58
|
}
|
|
37
59
|
}
|
|
38
60
|
|
|
61
|
+
func setTopScrollEdge(hard: Bool, overlayHeight: CGFloat) {
|
|
62
|
+
topScrollEdgeHard = hard
|
|
63
|
+
requestedTopOverlayHeight = overlayHeight.isFinite ? max(0, overlayHeight) : 0
|
|
64
|
+
updateTopScrollEdge()
|
|
65
|
+
setNeedsLayout()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private func updateTopScrollEdge() {
|
|
69
|
+
if #available(iOS 26.0, *) {
|
|
70
|
+
let hasOverlay = topScrollEdgeHard && topOverlayContent != nil
|
|
71
|
+
let enabled = hasOverlay && window != nil
|
|
72
|
+
let nextInset = hasOverlay ? requestedTopOverlayHeight : 0
|
|
73
|
+
if nextInset != appliedTopOverlayInset {
|
|
74
|
+
// Do not interrupt UIKit's rebound after a refresh has ended.
|
|
75
|
+
let wasReturningFromPull =
|
|
76
|
+
collectionView.contentOffset.y < -collectionView.adjustedContentInset.top - 0.5
|
|
77
|
+
let logicalOffset = collectionView.contentOffset.y + collectionView.contentInset.top
|
|
78
|
+
var inset = collectionView.contentInset
|
|
79
|
+
inset.top += nextInset - appliedTopOverlayInset
|
|
80
|
+
collectionView.contentInset = inset
|
|
81
|
+
appliedTopOverlayInset = nextInset
|
|
82
|
+
if !wasReturningFromPull {
|
|
83
|
+
collectionView.setContentOffset(
|
|
84
|
+
CGPoint(x: collectionView.contentOffset.x, y: logicalOffset - inset.top),
|
|
85
|
+
animated: false)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if enabled && topEdgeInteraction == nil {
|
|
89
|
+
originalTopEdgeStyle = collectionView.topEdgeEffect.style
|
|
90
|
+
// UIKit needs a native element to shape the edge effect around React-drawn content.
|
|
91
|
+
let marker = UILabel(frame: topOverlayHost.bounds)
|
|
92
|
+
marker.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
93
|
+
marker.text = " "
|
|
94
|
+
marker.textColor = .clear
|
|
95
|
+
marker.backgroundColor = .clear
|
|
96
|
+
marker.isUserInteractionEnabled = false
|
|
97
|
+
marker.isAccessibilityElement = false
|
|
98
|
+
marker.accessibilityElementsHidden = true
|
|
99
|
+
topOverlayHost.addSubview(marker)
|
|
100
|
+
topEdgeMarker = marker
|
|
101
|
+
let interaction = UIScrollEdgeElementContainerInteraction()
|
|
102
|
+
interaction.edge = .top
|
|
103
|
+
interaction.scrollView = collectionView
|
|
104
|
+
topOverlayHost.addInteraction(interaction)
|
|
105
|
+
topEdgeInteraction = interaction
|
|
106
|
+
} else if !enabled, let interaction = topEdgeInteraction {
|
|
107
|
+
topOverlayHost.removeInteraction(interaction)
|
|
108
|
+
(interaction as? UIScrollEdgeElementContainerInteraction)?.scrollView = nil
|
|
109
|
+
topEdgeInteraction = nil
|
|
110
|
+
topEdgeMarker?.removeFromSuperview()
|
|
111
|
+
topEdgeMarker = nil
|
|
112
|
+
if let originalTopEdgeStyle = originalTopEdgeStyle as? UIScrollEdgeEffect.Style {
|
|
113
|
+
collectionView.topEdgeEffect.style = originalTopEdgeStyle
|
|
114
|
+
}
|
|
115
|
+
originalTopEdgeStyle = nil
|
|
116
|
+
}
|
|
117
|
+
if enabled { collectionView.topEdgeEffect.style = .hard }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
39
121
|
func setContainerSlotHeightsJson(_ json: String) {
|
|
40
122
|
guard let data = json.data(using: .utf8),
|
|
41
123
|
let heights = try? JSONSerialization.jsonObject(with: data) as? [Double],
|
|
@@ -271,6 +353,7 @@ final class NativeListView: UIView {
|
|
|
271
353
|
addSubview(footerContainer)
|
|
272
354
|
addSubview(sectionIndexView)
|
|
273
355
|
addSubview(sectionIndexPreview)
|
|
356
|
+
addSubview(topOverlayHost)
|
|
274
357
|
footerContainer.addSubview(footerCell)
|
|
275
358
|
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
|
276
359
|
footerContainer.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -422,11 +505,14 @@ final class NativeListView: UIView {
|
|
|
422
505
|
|
|
423
506
|
override func didMoveToWindow() {
|
|
424
507
|
super.didMoveToWindow()
|
|
508
|
+
updateTopScrollEdge()
|
|
425
509
|
updateSectionIndexAttachment()
|
|
426
510
|
}
|
|
427
511
|
|
|
428
512
|
override func layoutSubviews() {
|
|
429
513
|
super.layoutSubviews()
|
|
514
|
+
topOverlayHost.frame = CGRect(x: 0, y: 0, width: collectionView.bounds.width,
|
|
515
|
+
height: requestedTopOverlayHeight)
|
|
430
516
|
updateSectionIndexAttachment()
|
|
431
517
|
let direction = effectiveUserInterfaceLayoutDirection
|
|
432
518
|
if let lastLayoutSize,
|
|
@@ -1998,6 +2084,7 @@ final class NativeListView: UIView {
|
|
|
1998
2084
|
|
|
1999
2085
|
func dispose() {
|
|
2000
2086
|
guard !disposed else { return }
|
|
2087
|
+
setTopScrollEdge(hard: false, overlayHeight: 0)
|
|
2001
2088
|
disposed = true
|
|
2002
2089
|
|
|
2003
2090
|
disposeActionAnchor()
|
package/lib/module/NativeList.js
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { OneKeyImageCache, OneKeyImageCachePolicy } from '@onekeyfe/react-native-image';
|
|
5
5
|
import { NativeAvatarPrefetchModel, NativeAvatarPrefetchQueue } from "./avatarPrefetch.js";
|
|
6
|
-
import { View } from 'react-native';
|
|
6
|
+
import { Platform, View } from 'react-native';
|
|
7
7
|
import { callback, getHostComponent } from 'react-native-nitro-modules';
|
|
8
8
|
import { isSelectableRow } from "./selection.js";
|
|
9
9
|
import { normalizeIndexScroll, normalizeKeyScroll, normalizePositionScroll, resolveLocationIndex, scrollFailure, validateOffset } from "./scrolling.js";
|
|
10
10
|
import { applyRowPatches, serializePatches, serializeSnapshot } from "./validation.js";
|
|
11
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
12
|
const NativeListConfig = require('../nitrogen/generated/shared/json/NativeListConfig.json');
|
|
13
13
|
const NativeListHost = getHostComponent('NativeList', () => NativeListConfig);
|
|
14
14
|
export function preloadNativeListAvatarImages(sources) {
|
|
@@ -30,6 +30,8 @@ export const NativeList = /*#__PURE__*/forwardRef(function NativeList({
|
|
|
30
30
|
listHeader,
|
|
31
31
|
listFooter,
|
|
32
32
|
listEmpty,
|
|
33
|
+
topOverlay,
|
|
34
|
+
topScrollEdgeEffect,
|
|
33
35
|
scrollPositionThresholds,
|
|
34
36
|
webVirtualizationEnabled: _webVirtualizationEnabled,
|
|
35
37
|
webSectionIndexContainerRef: _webSectionIndexContainerRef,
|
|
@@ -51,6 +53,8 @@ export const NativeList = /*#__PURE__*/forwardRef(function NativeList({
|
|
|
51
53
|
...viewProps
|
|
52
54
|
}, forwardedRef) {
|
|
53
55
|
const [slotHeights, setSlotHeights] = useState([0, 0, 0]);
|
|
56
|
+
const [topOverlayHeight, setTopOverlayHeight] = useState(0);
|
|
57
|
+
const hasTopOverlay = Platform?.OS === 'ios' && Number(Platform.Version) >= 26 && topScrollEdgeEffect === 'hard' && topOverlay != null;
|
|
54
58
|
const containerSlotHeightsJson = JSON.stringify(slotHeights);
|
|
55
59
|
const slotInset = snapshot.layout.contentPaddingHorizontal ?? snapshot.layout.contentPadding ?? 0;
|
|
56
60
|
if (snapshot.layout.orientation === 'horizontal' && (listHeader || listEmpty || listFooter)) {
|
|
@@ -313,15 +317,17 @@ export const NativeList = /*#__PURE__*/forwardRef(function NativeList({
|
|
|
313
317
|
callbacksRef.current.onVisibleRangeChanged?.(payload);
|
|
314
318
|
})
|
|
315
319
|
}), []);
|
|
316
|
-
return /*#__PURE__*/
|
|
320
|
+
return /*#__PURE__*/_jsxs(NativeListHost, {
|
|
317
321
|
...viewProps,
|
|
318
322
|
...nativeCallbacks,
|
|
319
323
|
snapshotJson: snapshotJson,
|
|
320
324
|
containerSlotHeightsJson: containerSlotHeightsJson,
|
|
325
|
+
topScrollEdgeHard: hasTopOverlay,
|
|
326
|
+
topOverlayHeight: hasTopOverlay ? topOverlayHeight : 0,
|
|
321
327
|
scrollPositionThresholdsJson: scrollPositionThresholdsJson,
|
|
322
328
|
keyboardDismissMode: keyboardDismissMode,
|
|
323
329
|
keyboardShouldPersistTaps: normalizedKeyboardShouldPersistTaps,
|
|
324
|
-
children: slotContents.map((content, index) => /*#__PURE__*/_jsx(View, {
|
|
330
|
+
children: [slotContents.map((content, index) => /*#__PURE__*/_jsx(View, {
|
|
325
331
|
collapsable: false,
|
|
326
332
|
style: {
|
|
327
333
|
position: 'absolute',
|
|
@@ -334,7 +340,20 @@ export const NativeList = /*#__PURE__*/forwardRef(function NativeList({
|
|
|
334
340
|
setSlotHeights(previous => previous[index] === height ? previous : previous.map((value, slot) => slot === index ? height : value));
|
|
335
341
|
},
|
|
336
342
|
children: content
|
|
337
|
-
}, index))
|
|
343
|
+
}, index)), hasTopOverlay ? /*#__PURE__*/_jsx(View, {
|
|
344
|
+
collapsable: false,
|
|
345
|
+
style: {
|
|
346
|
+
position: 'absolute',
|
|
347
|
+
top: 0,
|
|
348
|
+
left: 0,
|
|
349
|
+
right: 0
|
|
350
|
+
},
|
|
351
|
+
onLayout: event => {
|
|
352
|
+
const height = Math.ceil(event.nativeEvent.layout.height);
|
|
353
|
+
setTopOverlayHeight(previous => previous === height ? previous : height);
|
|
354
|
+
},
|
|
355
|
+
children: topOverlay
|
|
356
|
+
}) : null]
|
|
338
357
|
});
|
|
339
358
|
});
|
|
340
359
|
//# sourceMappingURL=NativeList.js.map
|
|
@@ -25,6 +25,8 @@ export const NativeList = /*#__PURE__*/forwardRef(function NativeList({
|
|
|
25
25
|
listHeader: _listHeader,
|
|
26
26
|
listEmpty: _listEmpty,
|
|
27
27
|
listFooter: _listFooter,
|
|
28
|
+
topOverlay: _topOverlay,
|
|
29
|
+
topScrollEdgeEffect: _topScrollEdgeEffect,
|
|
28
30
|
scrollPositionThresholds: _scrollPositionThresholds,
|
|
29
31
|
onScrollPositionThresholdChange: _onScrollPositionThresholdChange,
|
|
30
32
|
webVirtualizationEnabled = true,
|
|
@@ -75,6 +75,24 @@ namespace margelo::nitro::nativelist {
|
|
|
75
75
|
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<jni::JString> /* containerSlotHeightsJson */)>("setContainerSlotHeightsJson");
|
|
76
76
|
method(_javaPart, jni::make_jstring(containerSlotHeightsJson));
|
|
77
77
|
}
|
|
78
|
+
bool JHybridNativeListSpec::getTopScrollEdgeHard() {
|
|
79
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jboolean()>("getTopScrollEdgeHard");
|
|
80
|
+
auto __result = method(_javaPart);
|
|
81
|
+
return static_cast<bool>(__result);
|
|
82
|
+
}
|
|
83
|
+
void JHybridNativeListSpec::setTopScrollEdgeHard(bool topScrollEdgeHard) {
|
|
84
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jboolean /* topScrollEdgeHard */)>("setTopScrollEdgeHard");
|
|
85
|
+
method(_javaPart, topScrollEdgeHard);
|
|
86
|
+
}
|
|
87
|
+
double JHybridNativeListSpec::getTopOverlayHeight() {
|
|
88
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<double()>("getTopOverlayHeight");
|
|
89
|
+
auto __result = method(_javaPart);
|
|
90
|
+
return __result;
|
|
91
|
+
}
|
|
92
|
+
void JHybridNativeListSpec::setTopOverlayHeight(double topOverlayHeight) {
|
|
93
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<void(double /* topOverlayHeight */)>("setTopOverlayHeight");
|
|
94
|
+
method(_javaPart, topOverlayHeight);
|
|
95
|
+
}
|
|
78
96
|
std::string JHybridNativeListSpec::getScrollPositionThresholdsJson() {
|
|
79
97
|
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<jni::JString>()>("getScrollPositionThresholdsJson");
|
|
80
98
|
auto __result = method(_javaPart);
|
|
@@ -54,6 +54,10 @@ namespace margelo::nitro::nativelist {
|
|
|
54
54
|
void setSnapshotJson(const std::string& snapshotJson) override;
|
|
55
55
|
std::string getContainerSlotHeightsJson() override;
|
|
56
56
|
void setContainerSlotHeightsJson(const std::string& containerSlotHeightsJson) override;
|
|
57
|
+
bool getTopScrollEdgeHard() override;
|
|
58
|
+
void setTopScrollEdgeHard(bool topScrollEdgeHard) override;
|
|
59
|
+
double getTopOverlayHeight() override;
|
|
60
|
+
void setTopOverlayHeight(double topOverlayHeight) override;
|
|
57
61
|
std::string getScrollPositionThresholdsJson() override;
|
|
58
62
|
void setScrollPositionThresholdsJson(const std::string& scrollPositionThresholdsJson) override;
|
|
59
63
|
std::optional<std::function<void(bool /* isBeyondThreshold */)>> getOnScrollPositionThresholdChange() override;
|
|
@@ -63,6 +63,16 @@ void JHybridNativeListStateUpdater::updateViewProps(jni::alias_ref<jni::JClass>
|
|
|
63
63
|
: !newProps->containerSlotHeightsJson.hasSameValue(oldProps->containerSlotHeightsJson)) {
|
|
64
64
|
hybridView->setContainerSlotHeightsJson(newProps->containerSlotHeightsJson.get());
|
|
65
65
|
}
|
|
66
|
+
if (oldProps == nullptr
|
|
67
|
+
? newProps->topScrollEdgeHard.isProvided()
|
|
68
|
+
: !newProps->topScrollEdgeHard.hasSameValue(oldProps->topScrollEdgeHard)) {
|
|
69
|
+
hybridView->setTopScrollEdgeHard(newProps->topScrollEdgeHard.get());
|
|
70
|
+
}
|
|
71
|
+
if (oldProps == nullptr
|
|
72
|
+
? newProps->topOverlayHeight.isProvided()
|
|
73
|
+
: !newProps->topOverlayHeight.hasSameValue(oldProps->topOverlayHeight)) {
|
|
74
|
+
hybridView->setTopOverlayHeight(newProps->topOverlayHeight.get());
|
|
75
|
+
}
|
|
66
76
|
if (oldProps == nullptr
|
|
67
77
|
? newProps->scrollPositionThresholdsJson.isProvided()
|
|
68
78
|
: !newProps->scrollPositionThresholdsJson.hasSameValue(oldProps->scrollPositionThresholdsJson)) {
|
package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt
CHANGED
|
@@ -39,6 +39,18 @@ abstract class HybridNativeListSpec: HybridView() {
|
|
|
39
39
|
@set:Keep
|
|
40
40
|
abstract var containerSlotHeightsJson: String
|
|
41
41
|
|
|
42
|
+
@get:DoNotStrip
|
|
43
|
+
@get:Keep
|
|
44
|
+
@set:DoNotStrip
|
|
45
|
+
@set:Keep
|
|
46
|
+
abstract var topScrollEdgeHard: Boolean
|
|
47
|
+
|
|
48
|
+
@get:DoNotStrip
|
|
49
|
+
@get:Keep
|
|
50
|
+
@set:DoNotStrip
|
|
51
|
+
@set:Keep
|
|
52
|
+
abstract var topOverlayHeight: Double
|
|
53
|
+
|
|
42
54
|
@get:DoNotStrip
|
|
43
55
|
@get:Keep
|
|
44
56
|
@set:DoNotStrip
|
|
@@ -86,6 +86,18 @@ namespace margelo::nitro::nativelist {
|
|
|
86
86
|
inline void setContainerSlotHeightsJson(const std::string& containerSlotHeightsJson) noexcept override {
|
|
87
87
|
_swiftPart.setContainerSlotHeightsJson(containerSlotHeightsJson);
|
|
88
88
|
}
|
|
89
|
+
inline bool getTopScrollEdgeHard() noexcept override {
|
|
90
|
+
return _swiftPart.getTopScrollEdgeHard();
|
|
91
|
+
}
|
|
92
|
+
inline void setTopScrollEdgeHard(bool topScrollEdgeHard) noexcept override {
|
|
93
|
+
_swiftPart.setTopScrollEdgeHard(std::forward<decltype(topScrollEdgeHard)>(topScrollEdgeHard));
|
|
94
|
+
}
|
|
95
|
+
inline double getTopOverlayHeight() noexcept override {
|
|
96
|
+
return _swiftPart.getTopOverlayHeight();
|
|
97
|
+
}
|
|
98
|
+
inline void setTopOverlayHeight(double topOverlayHeight) noexcept override {
|
|
99
|
+
_swiftPart.setTopOverlayHeight(std::forward<decltype(topOverlayHeight)>(topOverlayHeight));
|
|
100
|
+
}
|
|
89
101
|
inline std::string getScrollPositionThresholdsJson() noexcept override {
|
|
90
102
|
auto __result = _swiftPart.getScrollPositionThresholdsJson();
|
|
91
103
|
return __result;
|
|
@@ -124,6 +124,18 @@ using namespace margelo::nitro::nativelist::views;
|
|
|
124
124
|
: !newViewProps.containerSlotHeightsJson.hasSameValue(oldViewProps->containerSlotHeightsJson)) {
|
|
125
125
|
swiftPart.setContainerSlotHeightsJson(newViewProps.containerSlotHeightsJson.get());
|
|
126
126
|
}
|
|
127
|
+
// topScrollEdgeHard: boolean
|
|
128
|
+
if (oldViewProps == nullptr
|
|
129
|
+
? newViewProps.topScrollEdgeHard.isProvided()
|
|
130
|
+
: !newViewProps.topScrollEdgeHard.hasSameValue(oldViewProps->topScrollEdgeHard)) {
|
|
131
|
+
swiftPart.setTopScrollEdgeHard(newViewProps.topScrollEdgeHard.get());
|
|
132
|
+
}
|
|
133
|
+
// topOverlayHeight: number
|
|
134
|
+
if (oldViewProps == nullptr
|
|
135
|
+
? newViewProps.topOverlayHeight.isProvided()
|
|
136
|
+
: !newViewProps.topOverlayHeight.hasSameValue(oldViewProps->topOverlayHeight)) {
|
|
137
|
+
swiftPart.setTopOverlayHeight(newViewProps.topOverlayHeight.get());
|
|
138
|
+
}
|
|
127
139
|
// scrollPositionThresholdsJson: string
|
|
128
140
|
if (oldViewProps == nullptr
|
|
129
141
|
? newViewProps.scrollPositionThresholdsJson.isProvided()
|
|
@@ -12,6 +12,8 @@ public protocol HybridNativeListSpec_protocol: HybridObject, HybridView {
|
|
|
12
12
|
// Properties
|
|
13
13
|
var snapshotJson: String { get set }
|
|
14
14
|
var containerSlotHeightsJson: String { get set }
|
|
15
|
+
var topScrollEdgeHard: Bool { get set }
|
|
16
|
+
var topOverlayHeight: Double { get set }
|
|
15
17
|
var scrollPositionThresholdsJson: String { get set }
|
|
16
18
|
var onScrollPositionThresholdChange: ((_ isBeyondThreshold: Bool) -> Void)? { get set }
|
|
17
19
|
var keyboardDismissMode: NativeListKeyboardDismissMode { get set }
|
|
@@ -143,6 +143,28 @@ open class HybridNativeListSpec_cxx {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
public final var topScrollEdgeHard: Bool {
|
|
147
|
+
@inline(__always)
|
|
148
|
+
get {
|
|
149
|
+
return self.__implementation.topScrollEdgeHard
|
|
150
|
+
}
|
|
151
|
+
@inline(__always)
|
|
152
|
+
set {
|
|
153
|
+
self.__implementation.topScrollEdgeHard = newValue
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public final var topOverlayHeight: Double {
|
|
158
|
+
@inline(__always)
|
|
159
|
+
get {
|
|
160
|
+
return self.__implementation.topOverlayHeight
|
|
161
|
+
}
|
|
162
|
+
@inline(__always)
|
|
163
|
+
set {
|
|
164
|
+
self.__implementation.topOverlayHeight = newValue
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
146
168
|
public final var scrollPositionThresholdsJson: std.string {
|
|
147
169
|
@inline(__always)
|
|
148
170
|
get {
|
|
@@ -18,6 +18,10 @@ namespace margelo::nitro::nativelist {
|
|
|
18
18
|
prototype.registerHybridSetter("snapshotJson", &HybridNativeListSpec::setSnapshotJson);
|
|
19
19
|
prototype.registerHybridGetter("containerSlotHeightsJson", &HybridNativeListSpec::getContainerSlotHeightsJson);
|
|
20
20
|
prototype.registerHybridSetter("containerSlotHeightsJson", &HybridNativeListSpec::setContainerSlotHeightsJson);
|
|
21
|
+
prototype.registerHybridGetter("topScrollEdgeHard", &HybridNativeListSpec::getTopScrollEdgeHard);
|
|
22
|
+
prototype.registerHybridSetter("topScrollEdgeHard", &HybridNativeListSpec::setTopScrollEdgeHard);
|
|
23
|
+
prototype.registerHybridGetter("topOverlayHeight", &HybridNativeListSpec::getTopOverlayHeight);
|
|
24
|
+
prototype.registerHybridSetter("topOverlayHeight", &HybridNativeListSpec::setTopOverlayHeight);
|
|
21
25
|
prototype.registerHybridGetter("scrollPositionThresholdsJson", &HybridNativeListSpec::getScrollPositionThresholdsJson);
|
|
22
26
|
prototype.registerHybridSetter("scrollPositionThresholdsJson", &HybridNativeListSpec::setScrollPositionThresholdsJson);
|
|
23
27
|
prototype.registerHybridGetter("onScrollPositionThresholdChange", &HybridNativeListSpec::getOnScrollPositionThresholdChange);
|
|
@@ -58,6 +58,10 @@ namespace margelo::nitro::nativelist {
|
|
|
58
58
|
virtual void setSnapshotJson(const std::string& snapshotJson) = 0;
|
|
59
59
|
virtual std::string getContainerSlotHeightsJson() = 0;
|
|
60
60
|
virtual void setContainerSlotHeightsJson(const std::string& containerSlotHeightsJson) = 0;
|
|
61
|
+
virtual bool getTopScrollEdgeHard() = 0;
|
|
62
|
+
virtual void setTopScrollEdgeHard(bool topScrollEdgeHard) = 0;
|
|
63
|
+
virtual double getTopOverlayHeight() = 0;
|
|
64
|
+
virtual void setTopOverlayHeight(double topOverlayHeight) = 0;
|
|
61
65
|
virtual std::string getScrollPositionThresholdsJson() = 0;
|
|
62
66
|
virtual void setScrollPositionThresholdsJson(const std::string& scrollPositionThresholdsJson) = 0;
|
|
63
67
|
virtual std::optional<std::function<void(bool /* isBeyondThreshold */)>> getOnScrollPositionThresholdChange() = 0;
|
|
@@ -22,6 +22,8 @@ namespace margelo::nitro::nativelist::views {
|
|
|
22
22
|
react::ViewProps(context, sourceProps, rawProps, filterObjectKeys),
|
|
23
23
|
snapshotJson(nitro::ReactProp<std::string>::fromRawValue("NativeList", "snapshotJson", rawProps, sourceProps.snapshotJson)),
|
|
24
24
|
containerSlotHeightsJson(nitro::ReactProp<std::string>::fromRawValue("NativeList", "containerSlotHeightsJson", rawProps, sourceProps.containerSlotHeightsJson)),
|
|
25
|
+
topScrollEdgeHard(nitro::ReactProp<bool>::fromRawValue("NativeList", "topScrollEdgeHard", rawProps, sourceProps.topScrollEdgeHard)),
|
|
26
|
+
topOverlayHeight(nitro::ReactProp<double>::fromRawValue("NativeList", "topOverlayHeight", rawProps, sourceProps.topOverlayHeight)),
|
|
25
27
|
scrollPositionThresholdsJson(nitro::ReactProp<std::string>::fromRawValue("NativeList", "scrollPositionThresholdsJson", rawProps, sourceProps.scrollPositionThresholdsJson)),
|
|
26
28
|
onScrollPositionThresholdChange(nitro::ReactProp<std::optional<std::function<void(bool /* isBeyondThreshold */)>>>::fromRawValue("NativeList", "onScrollPositionThresholdChange", rawProps, sourceProps.onScrollPositionThresholdChange)),
|
|
27
29
|
keyboardDismissMode(nitro::ReactProp<NativeListKeyboardDismissMode>::fromRawValue("NativeList", "keyboardDismissMode", rawProps, sourceProps.keyboardDismissMode)),
|
|
@@ -38,6 +40,8 @@ namespace margelo::nitro::nativelist::views {
|
|
|
38
40
|
switch (hashString(propName)) {
|
|
39
41
|
case hashString("snapshotJson"): return true;
|
|
40
42
|
case hashString("containerSlotHeightsJson"): return true;
|
|
43
|
+
case hashString("topScrollEdgeHard"): return true;
|
|
44
|
+
case hashString("topOverlayHeight"): return true;
|
|
41
45
|
case hashString("scrollPositionThresholdsJson"): return true;
|
|
42
46
|
case hashString("onScrollPositionThresholdChange"): return true;
|
|
43
47
|
case hashString("keyboardDismissMode"): return true;
|
|
@@ -47,6 +47,8 @@ namespace margelo::nitro::nativelist::views {
|
|
|
47
47
|
public:
|
|
48
48
|
nitro::ReactProp<std::string> snapshotJson;
|
|
49
49
|
nitro::ReactProp<std::string> containerSlotHeightsJson;
|
|
50
|
+
nitro::ReactProp<bool> topScrollEdgeHard;
|
|
51
|
+
nitro::ReactProp<double> topOverlayHeight;
|
|
50
52
|
nitro::ReactProp<std::string> scrollPositionThresholdsJson;
|
|
51
53
|
nitro::ReactProp<std::optional<std::function<void(bool /* isBeyondThreshold */)>>> onScrollPositionThresholdChange;
|
|
52
54
|
nitro::ReactProp<NativeListKeyboardDismissMode> keyboardDismissMode;
|
|
@@ -63,6 +65,8 @@ namespace margelo::nitro::nativelist::views {
|
|
|
63
65
|
bool hasSameProps(const HybridNativeListProps& other) const noexcept {
|
|
64
66
|
return snapshotJson.hasSameValue(other.snapshotJson) &&
|
|
65
67
|
containerSlotHeightsJson.hasSameValue(other.containerSlotHeightsJson) &&
|
|
68
|
+
topScrollEdgeHard.hasSameValue(other.topScrollEdgeHard) &&
|
|
69
|
+
topOverlayHeight.hasSameValue(other.topOverlayHeight) &&
|
|
66
70
|
scrollPositionThresholdsJson.hasSameValue(other.scrollPositionThresholdsJson) &&
|
|
67
71
|
onScrollPositionThresholdChange.hasSameValue(other.onScrollPositionThresholdChange) &&
|
|
68
72
|
keyboardDismissMode.hasSameValue(other.keyboardDismissMode) &&
|
|
@@ -80,6 +84,8 @@ namespace margelo::nitro::nativelist::views {
|
|
|
80
84
|
bool hasAnyProvidedProps() const noexcept {
|
|
81
85
|
return snapshotJson.isProvided() ||
|
|
82
86
|
containerSlotHeightsJson.isProvided() ||
|
|
87
|
+
topScrollEdgeHard.isProvided() ||
|
|
88
|
+
topOverlayHeight.isProvided() ||
|
|
83
89
|
scrollPositionThresholdsJson.isProvided() ||
|
|
84
90
|
onScrollPositionThresholdChange.isProvided() ||
|
|
85
91
|
keyboardDismissMode.isProvided() ||
|
|
@@ -7,6 +7,8 @@ import type { HybridView, HybridViewMethods, HybridViewProps } from 'react-nativ
|
|
|
7
7
|
export interface NativeListNativeProps extends HybridViewProps {
|
|
8
8
|
snapshotJson: string;
|
|
9
9
|
containerSlotHeightsJson: string;
|
|
10
|
+
topScrollEdgeHard: boolean;
|
|
11
|
+
topOverlayHeight: number;
|
|
10
12
|
scrollPositionThresholdsJson: string;
|
|
11
13
|
onScrollPositionThresholdChange?: (isBeyondThreshold: boolean) => void;
|
|
12
14
|
keyboardDismissMode: NativeListKeyboardDismissMode;
|
|
@@ -79,6 +79,10 @@ export type NativeListProps = Omit<ViewProps, 'children'> & Readonly<{
|
|
|
79
79
|
listHeader?: ReactNode;
|
|
80
80
|
listFooter?: ReactNode;
|
|
81
81
|
listEmpty?: ReactNode;
|
|
82
|
+
/** Fixed overlay above scrolling rows on iOS 26 and newer. */
|
|
83
|
+
topOverlay?: ReactNode;
|
|
84
|
+
/** Enables UIKit's hard top scroll-edge effect on iOS 26 and newer. */
|
|
85
|
+
topScrollEdgeEffect?: 'hard';
|
|
82
86
|
/** Web only. Defaults to true and is ignored by the native host. */
|
|
83
87
|
webVirtualizationEnabled?: boolean;
|
|
84
88
|
/** Web only. Hosts the section index inside this container. */
|
|
@@ -75,6 +75,24 @@ namespace margelo::nitro::nativelist {
|
|
|
75
75
|
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<jni::JString> /* containerSlotHeightsJson */)>("setContainerSlotHeightsJson");
|
|
76
76
|
method(_javaPart, jni::make_jstring(containerSlotHeightsJson));
|
|
77
77
|
}
|
|
78
|
+
bool JHybridNativeListSpec::getTopScrollEdgeHard() {
|
|
79
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jboolean()>("getTopScrollEdgeHard");
|
|
80
|
+
auto __result = method(_javaPart);
|
|
81
|
+
return static_cast<bool>(__result);
|
|
82
|
+
}
|
|
83
|
+
void JHybridNativeListSpec::setTopScrollEdgeHard(bool topScrollEdgeHard) {
|
|
84
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jboolean /* topScrollEdgeHard */)>("setTopScrollEdgeHard");
|
|
85
|
+
method(_javaPart, topScrollEdgeHard);
|
|
86
|
+
}
|
|
87
|
+
double JHybridNativeListSpec::getTopOverlayHeight() {
|
|
88
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<double()>("getTopOverlayHeight");
|
|
89
|
+
auto __result = method(_javaPart);
|
|
90
|
+
return __result;
|
|
91
|
+
}
|
|
92
|
+
void JHybridNativeListSpec::setTopOverlayHeight(double topOverlayHeight) {
|
|
93
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<void(double /* topOverlayHeight */)>("setTopOverlayHeight");
|
|
94
|
+
method(_javaPart, topOverlayHeight);
|
|
95
|
+
}
|
|
78
96
|
std::string JHybridNativeListSpec::getScrollPositionThresholdsJson() {
|
|
79
97
|
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<jni::JString>()>("getScrollPositionThresholdsJson");
|
|
80
98
|
auto __result = method(_javaPart);
|
|
@@ -54,6 +54,10 @@ namespace margelo::nitro::nativelist {
|
|
|
54
54
|
void setSnapshotJson(const std::string& snapshotJson) override;
|
|
55
55
|
std::string getContainerSlotHeightsJson() override;
|
|
56
56
|
void setContainerSlotHeightsJson(const std::string& containerSlotHeightsJson) override;
|
|
57
|
+
bool getTopScrollEdgeHard() override;
|
|
58
|
+
void setTopScrollEdgeHard(bool topScrollEdgeHard) override;
|
|
59
|
+
double getTopOverlayHeight() override;
|
|
60
|
+
void setTopOverlayHeight(double topOverlayHeight) override;
|
|
57
61
|
std::string getScrollPositionThresholdsJson() override;
|
|
58
62
|
void setScrollPositionThresholdsJson(const std::string& scrollPositionThresholdsJson) override;
|
|
59
63
|
std::optional<std::function<void(bool /* isBeyondThreshold */)>> getOnScrollPositionThresholdChange() override;
|
|
@@ -63,6 +63,16 @@ void JHybridNativeListStateUpdater::updateViewProps(jni::alias_ref<jni::JClass>
|
|
|
63
63
|
: !newProps->containerSlotHeightsJson.hasSameValue(oldProps->containerSlotHeightsJson)) {
|
|
64
64
|
hybridView->setContainerSlotHeightsJson(newProps->containerSlotHeightsJson.get());
|
|
65
65
|
}
|
|
66
|
+
if (oldProps == nullptr
|
|
67
|
+
? newProps->topScrollEdgeHard.isProvided()
|
|
68
|
+
: !newProps->topScrollEdgeHard.hasSameValue(oldProps->topScrollEdgeHard)) {
|
|
69
|
+
hybridView->setTopScrollEdgeHard(newProps->topScrollEdgeHard.get());
|
|
70
|
+
}
|
|
71
|
+
if (oldProps == nullptr
|
|
72
|
+
? newProps->topOverlayHeight.isProvided()
|
|
73
|
+
: !newProps->topOverlayHeight.hasSameValue(oldProps->topOverlayHeight)) {
|
|
74
|
+
hybridView->setTopOverlayHeight(newProps->topOverlayHeight.get());
|
|
75
|
+
}
|
|
66
76
|
if (oldProps == nullptr
|
|
67
77
|
? newProps->scrollPositionThresholdsJson.isProvided()
|
|
68
78
|
: !newProps->scrollPositionThresholdsJson.hasSameValue(oldProps->scrollPositionThresholdsJson)) {
|
package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt
CHANGED
|
@@ -39,6 +39,18 @@ abstract class HybridNativeListSpec: HybridView() {
|
|
|
39
39
|
@set:Keep
|
|
40
40
|
abstract var containerSlotHeightsJson: String
|
|
41
41
|
|
|
42
|
+
@get:DoNotStrip
|
|
43
|
+
@get:Keep
|
|
44
|
+
@set:DoNotStrip
|
|
45
|
+
@set:Keep
|
|
46
|
+
abstract var topScrollEdgeHard: Boolean
|
|
47
|
+
|
|
48
|
+
@get:DoNotStrip
|
|
49
|
+
@get:Keep
|
|
50
|
+
@set:DoNotStrip
|
|
51
|
+
@set:Keep
|
|
52
|
+
abstract var topOverlayHeight: Double
|
|
53
|
+
|
|
42
54
|
@get:DoNotStrip
|
|
43
55
|
@get:Keep
|
|
44
56
|
@set:DoNotStrip
|
|
@@ -86,6 +86,18 @@ namespace margelo::nitro::nativelist {
|
|
|
86
86
|
inline void setContainerSlotHeightsJson(const std::string& containerSlotHeightsJson) noexcept override {
|
|
87
87
|
_swiftPart.setContainerSlotHeightsJson(containerSlotHeightsJson);
|
|
88
88
|
}
|
|
89
|
+
inline bool getTopScrollEdgeHard() noexcept override {
|
|
90
|
+
return _swiftPart.getTopScrollEdgeHard();
|
|
91
|
+
}
|
|
92
|
+
inline void setTopScrollEdgeHard(bool topScrollEdgeHard) noexcept override {
|
|
93
|
+
_swiftPart.setTopScrollEdgeHard(std::forward<decltype(topScrollEdgeHard)>(topScrollEdgeHard));
|
|
94
|
+
}
|
|
95
|
+
inline double getTopOverlayHeight() noexcept override {
|
|
96
|
+
return _swiftPart.getTopOverlayHeight();
|
|
97
|
+
}
|
|
98
|
+
inline void setTopOverlayHeight(double topOverlayHeight) noexcept override {
|
|
99
|
+
_swiftPart.setTopOverlayHeight(std::forward<decltype(topOverlayHeight)>(topOverlayHeight));
|
|
100
|
+
}
|
|
89
101
|
inline std::string getScrollPositionThresholdsJson() noexcept override {
|
|
90
102
|
auto __result = _swiftPart.getScrollPositionThresholdsJson();
|
|
91
103
|
return __result;
|
|
@@ -124,6 +124,18 @@ using namespace margelo::nitro::nativelist::views;
|
|
|
124
124
|
: !newViewProps.containerSlotHeightsJson.hasSameValue(oldViewProps->containerSlotHeightsJson)) {
|
|
125
125
|
swiftPart.setContainerSlotHeightsJson(newViewProps.containerSlotHeightsJson.get());
|
|
126
126
|
}
|
|
127
|
+
// topScrollEdgeHard: boolean
|
|
128
|
+
if (oldViewProps == nullptr
|
|
129
|
+
? newViewProps.topScrollEdgeHard.isProvided()
|
|
130
|
+
: !newViewProps.topScrollEdgeHard.hasSameValue(oldViewProps->topScrollEdgeHard)) {
|
|
131
|
+
swiftPart.setTopScrollEdgeHard(newViewProps.topScrollEdgeHard.get());
|
|
132
|
+
}
|
|
133
|
+
// topOverlayHeight: number
|
|
134
|
+
if (oldViewProps == nullptr
|
|
135
|
+
? newViewProps.topOverlayHeight.isProvided()
|
|
136
|
+
: !newViewProps.topOverlayHeight.hasSameValue(oldViewProps->topOverlayHeight)) {
|
|
137
|
+
swiftPart.setTopOverlayHeight(newViewProps.topOverlayHeight.get());
|
|
138
|
+
}
|
|
127
139
|
// scrollPositionThresholdsJson: string
|
|
128
140
|
if (oldViewProps == nullptr
|
|
129
141
|
? newViewProps.scrollPositionThresholdsJson.isProvided()
|
|
@@ -12,6 +12,8 @@ public protocol HybridNativeListSpec_protocol: HybridObject, HybridView {
|
|
|
12
12
|
// Properties
|
|
13
13
|
var snapshotJson: String { get set }
|
|
14
14
|
var containerSlotHeightsJson: String { get set }
|
|
15
|
+
var topScrollEdgeHard: Bool { get set }
|
|
16
|
+
var topOverlayHeight: Double { get set }
|
|
15
17
|
var scrollPositionThresholdsJson: String { get set }
|
|
16
18
|
var onScrollPositionThresholdChange: ((_ isBeyondThreshold: Bool) -> Void)? { get set }
|
|
17
19
|
var keyboardDismissMode: NativeListKeyboardDismissMode { get set }
|
|
@@ -143,6 +143,28 @@ open class HybridNativeListSpec_cxx {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
public final var topScrollEdgeHard: Bool {
|
|
147
|
+
@inline(__always)
|
|
148
|
+
get {
|
|
149
|
+
return self.__implementation.topScrollEdgeHard
|
|
150
|
+
}
|
|
151
|
+
@inline(__always)
|
|
152
|
+
set {
|
|
153
|
+
self.__implementation.topScrollEdgeHard = newValue
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public final var topOverlayHeight: Double {
|
|
158
|
+
@inline(__always)
|
|
159
|
+
get {
|
|
160
|
+
return self.__implementation.topOverlayHeight
|
|
161
|
+
}
|
|
162
|
+
@inline(__always)
|
|
163
|
+
set {
|
|
164
|
+
self.__implementation.topOverlayHeight = newValue
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
146
168
|
public final var scrollPositionThresholdsJson: std.string {
|
|
147
169
|
@inline(__always)
|
|
148
170
|
get {
|
|
@@ -18,6 +18,10 @@ namespace margelo::nitro::nativelist {
|
|
|
18
18
|
prototype.registerHybridSetter("snapshotJson", &HybridNativeListSpec::setSnapshotJson);
|
|
19
19
|
prototype.registerHybridGetter("containerSlotHeightsJson", &HybridNativeListSpec::getContainerSlotHeightsJson);
|
|
20
20
|
prototype.registerHybridSetter("containerSlotHeightsJson", &HybridNativeListSpec::setContainerSlotHeightsJson);
|
|
21
|
+
prototype.registerHybridGetter("topScrollEdgeHard", &HybridNativeListSpec::getTopScrollEdgeHard);
|
|
22
|
+
prototype.registerHybridSetter("topScrollEdgeHard", &HybridNativeListSpec::setTopScrollEdgeHard);
|
|
23
|
+
prototype.registerHybridGetter("topOverlayHeight", &HybridNativeListSpec::getTopOverlayHeight);
|
|
24
|
+
prototype.registerHybridSetter("topOverlayHeight", &HybridNativeListSpec::setTopOverlayHeight);
|
|
21
25
|
prototype.registerHybridGetter("scrollPositionThresholdsJson", &HybridNativeListSpec::getScrollPositionThresholdsJson);
|
|
22
26
|
prototype.registerHybridSetter("scrollPositionThresholdsJson", &HybridNativeListSpec::setScrollPositionThresholdsJson);
|
|
23
27
|
prototype.registerHybridGetter("onScrollPositionThresholdChange", &HybridNativeListSpec::getOnScrollPositionThresholdChange);
|
|
@@ -58,6 +58,10 @@ namespace margelo::nitro::nativelist {
|
|
|
58
58
|
virtual void setSnapshotJson(const std::string& snapshotJson) = 0;
|
|
59
59
|
virtual std::string getContainerSlotHeightsJson() = 0;
|
|
60
60
|
virtual void setContainerSlotHeightsJson(const std::string& containerSlotHeightsJson) = 0;
|
|
61
|
+
virtual bool getTopScrollEdgeHard() = 0;
|
|
62
|
+
virtual void setTopScrollEdgeHard(bool topScrollEdgeHard) = 0;
|
|
63
|
+
virtual double getTopOverlayHeight() = 0;
|
|
64
|
+
virtual void setTopOverlayHeight(double topOverlayHeight) = 0;
|
|
61
65
|
virtual std::string getScrollPositionThresholdsJson() = 0;
|
|
62
66
|
virtual void setScrollPositionThresholdsJson(const std::string& scrollPositionThresholdsJson) = 0;
|
|
63
67
|
virtual std::optional<std::function<void(bool /* isBeyondThreshold */)>> getOnScrollPositionThresholdChange() = 0;
|
|
@@ -22,6 +22,8 @@ namespace margelo::nitro::nativelist::views {
|
|
|
22
22
|
react::ViewProps(context, sourceProps, rawProps, filterObjectKeys),
|
|
23
23
|
snapshotJson(nitro::ReactProp<std::string>::fromRawValue("NativeList", "snapshotJson", rawProps, sourceProps.snapshotJson)),
|
|
24
24
|
containerSlotHeightsJson(nitro::ReactProp<std::string>::fromRawValue("NativeList", "containerSlotHeightsJson", rawProps, sourceProps.containerSlotHeightsJson)),
|
|
25
|
+
topScrollEdgeHard(nitro::ReactProp<bool>::fromRawValue("NativeList", "topScrollEdgeHard", rawProps, sourceProps.topScrollEdgeHard)),
|
|
26
|
+
topOverlayHeight(nitro::ReactProp<double>::fromRawValue("NativeList", "topOverlayHeight", rawProps, sourceProps.topOverlayHeight)),
|
|
25
27
|
scrollPositionThresholdsJson(nitro::ReactProp<std::string>::fromRawValue("NativeList", "scrollPositionThresholdsJson", rawProps, sourceProps.scrollPositionThresholdsJson)),
|
|
26
28
|
onScrollPositionThresholdChange(nitro::ReactProp<std::optional<std::function<void(bool /* isBeyondThreshold */)>>>::fromRawValue("NativeList", "onScrollPositionThresholdChange", rawProps, sourceProps.onScrollPositionThresholdChange)),
|
|
27
29
|
keyboardDismissMode(nitro::ReactProp<NativeListKeyboardDismissMode>::fromRawValue("NativeList", "keyboardDismissMode", rawProps, sourceProps.keyboardDismissMode)),
|
|
@@ -38,6 +40,8 @@ namespace margelo::nitro::nativelist::views {
|
|
|
38
40
|
switch (hashString(propName)) {
|
|
39
41
|
case hashString("snapshotJson"): return true;
|
|
40
42
|
case hashString("containerSlotHeightsJson"): return true;
|
|
43
|
+
case hashString("topScrollEdgeHard"): return true;
|
|
44
|
+
case hashString("topOverlayHeight"): return true;
|
|
41
45
|
case hashString("scrollPositionThresholdsJson"): return true;
|
|
42
46
|
case hashString("onScrollPositionThresholdChange"): return true;
|
|
43
47
|
case hashString("keyboardDismissMode"): return true;
|
|
@@ -47,6 +47,8 @@ namespace margelo::nitro::nativelist::views {
|
|
|
47
47
|
public:
|
|
48
48
|
nitro::ReactProp<std::string> snapshotJson;
|
|
49
49
|
nitro::ReactProp<std::string> containerSlotHeightsJson;
|
|
50
|
+
nitro::ReactProp<bool> topScrollEdgeHard;
|
|
51
|
+
nitro::ReactProp<double> topOverlayHeight;
|
|
50
52
|
nitro::ReactProp<std::string> scrollPositionThresholdsJson;
|
|
51
53
|
nitro::ReactProp<std::optional<std::function<void(bool /* isBeyondThreshold */)>>> onScrollPositionThresholdChange;
|
|
52
54
|
nitro::ReactProp<NativeListKeyboardDismissMode> keyboardDismissMode;
|
|
@@ -63,6 +65,8 @@ namespace margelo::nitro::nativelist::views {
|
|
|
63
65
|
bool hasSameProps(const HybridNativeListProps& other) const noexcept {
|
|
64
66
|
return snapshotJson.hasSameValue(other.snapshotJson) &&
|
|
65
67
|
containerSlotHeightsJson.hasSameValue(other.containerSlotHeightsJson) &&
|
|
68
|
+
topScrollEdgeHard.hasSameValue(other.topScrollEdgeHard) &&
|
|
69
|
+
topOverlayHeight.hasSameValue(other.topOverlayHeight) &&
|
|
66
70
|
scrollPositionThresholdsJson.hasSameValue(other.scrollPositionThresholdsJson) &&
|
|
67
71
|
onScrollPositionThresholdChange.hasSameValue(other.onScrollPositionThresholdChange) &&
|
|
68
72
|
keyboardDismissMode.hasSameValue(other.keyboardDismissMode) &&
|
|
@@ -80,6 +84,8 @@ namespace margelo::nitro::nativelist::views {
|
|
|
80
84
|
bool hasAnyProvidedProps() const noexcept {
|
|
81
85
|
return snapshotJson.isProvided() ||
|
|
82
86
|
containerSlotHeightsJson.isProvided() ||
|
|
87
|
+
topScrollEdgeHard.isProvided() ||
|
|
88
|
+
topOverlayHeight.isProvided() ||
|
|
83
89
|
scrollPositionThresholdsJson.isProvided() ||
|
|
84
90
|
onScrollPositionThresholdChange.isProvided() ||
|
|
85
91
|
keyboardDismissMode.isProvided() ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-native-list",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.179-alpha.20261015",
|
|
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",
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"typescript": "^5.9.2"
|
|
88
88
|
},
|
|
89
89
|
"peerDependencies": {
|
|
90
|
-
"@onekeyfe/react-native-image": "3.0.
|
|
91
|
-
"@onekeyfe/react-native-native-logger": "3.0.
|
|
90
|
+
"@onekeyfe/react-native-image": "3.0.179-alpha.20261015",
|
|
91
|
+
"@onekeyfe/react-native-native-logger": "3.0.179-alpha.20261015",
|
|
92
92
|
"react": "*",
|
|
93
93
|
"react-native": "*",
|
|
94
94
|
"react-native-nitro-modules": "0.37.0"
|
package/src/NativeList.nitro.ts
CHANGED
|
@@ -12,6 +12,8 @@ import type {
|
|
|
12
12
|
export interface NativeListNativeProps extends HybridViewProps {
|
|
13
13
|
snapshotJson: string;
|
|
14
14
|
containerSlotHeightsJson: string;
|
|
15
|
+
topScrollEdgeHard: boolean;
|
|
16
|
+
topOverlayHeight: number;
|
|
15
17
|
scrollPositionThresholdsJson: string;
|
|
16
18
|
onScrollPositionThresholdChange?: (isBeyondThreshold: boolean) => void;
|
|
17
19
|
keyboardDismissMode: NativeListKeyboardDismissMode;
|
package/src/NativeList.tsx
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
NativeAvatarPrefetchModel,
|
|
15
15
|
NativeAvatarPrefetchQueue,
|
|
16
16
|
} from './avatarPrefetch';
|
|
17
|
-
import { View } from 'react-native';
|
|
17
|
+
import { Platform, View } from 'react-native';
|
|
18
18
|
import { callback, getHostComponent } from 'react-native-nitro-modules';
|
|
19
19
|
import type {
|
|
20
20
|
NativeListMethods,
|
|
@@ -101,6 +101,8 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
101
101
|
listHeader,
|
|
102
102
|
listFooter,
|
|
103
103
|
listEmpty,
|
|
104
|
+
topOverlay,
|
|
105
|
+
topScrollEdgeEffect,
|
|
104
106
|
scrollPositionThresholds,
|
|
105
107
|
webVirtualizationEnabled: _webVirtualizationEnabled,
|
|
106
108
|
webSectionIndexContainerRef: _webSectionIndexContainerRef,
|
|
@@ -124,6 +126,12 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
124
126
|
forwardedRef
|
|
125
127
|
) {
|
|
126
128
|
const [slotHeights, setSlotHeights] = useState([0, 0, 0]);
|
|
129
|
+
const [topOverlayHeight, setTopOverlayHeight] = useState(0);
|
|
130
|
+
const hasTopOverlay =
|
|
131
|
+
Platform?.OS === 'ios' &&
|
|
132
|
+
Number(Platform.Version) >= 26 &&
|
|
133
|
+
topScrollEdgeEffect === 'hard' &&
|
|
134
|
+
topOverlay != null;
|
|
127
135
|
const containerSlotHeightsJson = JSON.stringify(slotHeights);
|
|
128
136
|
const slotInset =
|
|
129
137
|
snapshot.layout.contentPaddingHorizontal ??
|
|
@@ -506,6 +514,8 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
506
514
|
{...nativeCallbacks}
|
|
507
515
|
snapshotJson={snapshotJson}
|
|
508
516
|
containerSlotHeightsJson={containerSlotHeightsJson}
|
|
517
|
+
topScrollEdgeHard={hasTopOverlay}
|
|
518
|
+
topOverlayHeight={hasTopOverlay ? topOverlayHeight : 0}
|
|
509
519
|
scrollPositionThresholdsJson={scrollPositionThresholdsJson}
|
|
510
520
|
keyboardDismissMode={keyboardDismissMode}
|
|
511
521
|
keyboardShouldPersistTaps={normalizedKeyboardShouldPersistTaps}
|
|
@@ -534,6 +544,20 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
534
544
|
{content}
|
|
535
545
|
</View>
|
|
536
546
|
))}
|
|
547
|
+
{hasTopOverlay ? (
|
|
548
|
+
<View
|
|
549
|
+
collapsable={false}
|
|
550
|
+
style={{ position: 'absolute', top: 0, left: 0, right: 0 }}
|
|
551
|
+
onLayout={(event) => {
|
|
552
|
+
const height = Math.ceil(event.nativeEvent.layout.height);
|
|
553
|
+
setTopOverlayHeight((previous) =>
|
|
554
|
+
previous === height ? previous : height
|
|
555
|
+
);
|
|
556
|
+
}}
|
|
557
|
+
>
|
|
558
|
+
{topOverlay}
|
|
559
|
+
</View>
|
|
560
|
+
) : null}
|
|
537
561
|
</NativeListHost>
|
|
538
562
|
);
|
|
539
563
|
}
|
package/src/NativeList.types.ts
CHANGED
|
@@ -123,6 +123,10 @@ export type NativeListProps = Omit<ViewProps, 'children'> &
|
|
|
123
123
|
listHeader?: ReactNode;
|
|
124
124
|
listFooter?: ReactNode;
|
|
125
125
|
listEmpty?: ReactNode;
|
|
126
|
+
/** Fixed overlay above scrolling rows on iOS 26 and newer. */
|
|
127
|
+
topOverlay?: ReactNode;
|
|
128
|
+
/** Enables UIKit's hard top scroll-edge effect on iOS 26 and newer. */
|
|
129
|
+
topScrollEdgeEffect?: 'hard';
|
|
126
130
|
/** Web only. Defaults to true and is ignored by the native host. */
|
|
127
131
|
webVirtualizationEnabled?: boolean;
|
|
128
132
|
/** Web only. Hosts the section index inside this container. */
|
package/src/NativeList.web.tsx
CHANGED
|
@@ -85,6 +85,8 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
85
85
|
listHeader: _listHeader,
|
|
86
86
|
listEmpty: _listEmpty,
|
|
87
87
|
listFooter: _listFooter,
|
|
88
|
+
topOverlay: _topOverlay,
|
|
89
|
+
topScrollEdgeEffect: _topScrollEdgeEffect,
|
|
88
90
|
scrollPositionThresholds: _scrollPositionThresholds,
|
|
89
91
|
onScrollPositionThresholdChange: _onScrollPositionThresholdChange,
|
|
90
92
|
webVirtualizationEnabled = true,
|