@onekeyfe/react-native-pager-view 6.7.0-alpha.354 → 6.7.0-alpha.362

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -66,11 +66,6 @@ replacing its delegate. Android coordinates nested scroll with the active
66
66
  Web scroll child can opt in by exposing a DOM element with
67
67
  `data-collapsible-pager-scroll`. CSS scroll timelines drive header/sticky
68
68
  movement, so React is not updated on each vertical scroll frame.
69
- `pagerRef.current?.scrollHeaderBy(delta)` immediately moves the active page's
70
- collapsible header by a finite logical distance, clamped to its measured range.
71
- `scrollHeaderToOffset(offset)` targets an absolute collapse position, so a
72
- keyboard's own scroll adjustment cannot add a second relative movement.
73
- It is useful for revealing a focused field above a keyboard.
74
69
 
75
70
  On Web, `WebCollapsiblePagerView` is an explicit root export of the existing Web
76
71
  pager. It measures Header and sticky content when their heights are omitted.
@@ -142,7 +142,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
142
142
  private var transitionSourceRecyclerAnchor: RecyclerReadingAnchor? = null
143
143
  private val commandBodyKeys = HashSet<String>()
144
144
  private var explicitTopCommand: Pair<String, PageRestoreRequest>? = null
145
- private class PagerTopRequest(view: View, val key: String, val pageIndex: Int, val generation: Int, val logicalOffset: Int, val token: Int) {
145
+ private class PagerTopRequest(view: View, val key: String, val pageIndex: Int, val generation: Int, val target: Int, val token: Int) {
146
146
  val view = WeakReference(view)
147
147
  }
148
148
  private var pagerTopRequest: PagerTopRequest? = null
@@ -293,20 +293,6 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
293
293
  var onNativeSubHeaderPress: ((Int, String) -> Unit)? = null
294
294
 
295
295
  var nativeTabBarHeight = 44.0
296
- var nativeTabBarTrailingReservedWidth = 0.0
297
- set(value) {
298
- field = if (value.isFinite() && value >= 0.0) value else 0.0
299
- bringNativeHeadersToFront()
300
- requestLayout()
301
- }
302
-
303
- private fun nativeTabReservationPx(width: Int): Int {
304
- val reserved = nativeTabBarTrailingReservedWidth
305
- val density = resources.displayMetrics.density
306
- return if (reserved.isFinite() && reserved > 0.0 && width > 0 && density > 0f) {
307
- (reserved.coerceAtMost(width / density.toDouble()) * density).roundToInt()
308
- } else 0
309
- }
310
296
  var nativeTabBarContentPaddingHorizontal = 20.0
311
297
  var nativeTabBarItemSpacing = 8.0
312
298
  var nativeTabBarFontSize = 16.0
@@ -456,7 +442,6 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
456
442
  private fun bringNativeHeadersToFront() {
457
443
  nativeTabBarView.bringToFront()
458
444
  nativeSubHeaderView.bringToFront()
459
- if (nativeTabBarTrailingReservedWidth > 0.0) stickyHeaderView?.bringToFront()
460
445
  }
461
446
 
462
447
  fun addReactChild(child: View, index: Int) {
@@ -704,7 +689,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
704
689
 
705
690
  private fun layoutPagerIfRequested() {
706
691
  val recycler = pager.getChildAt(0) as? RecyclerView ?: return
707
- if (!recycler.isLayoutRequested || recycler.isLayoutSuppressed || recycler.isComputingLayout || recycler.width <= 0 || recycler.height <= 0) return
692
+ if (!recycler.isLayoutRequested || recycler.isComputingLayout || recycler.width <= 0 || recycler.height <= 0) return
708
693
  // OneKey patch: a non-animated page command queues RecyclerView layout, but RN
709
694
  // can stop that request after navigation. Drain it before showing the page.
710
695
  recycler.measure(
@@ -727,9 +712,6 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
727
712
  layout(left, top, right, bottom)
728
713
  }
729
714
 
730
- private fun isPagerRecyclerLayoutSuppressed(): Boolean =
731
- (pager.getChildAt(0) as? RecyclerView)?.isLayoutSuppressed == true
732
-
733
715
  override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
734
716
  val width = MeasureSpec.getSize(widthMeasureSpec)
735
717
  val height = MeasureSpec.getSize(heightMeasureSpec)
@@ -740,12 +722,10 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
740
722
  return
741
723
  }
742
724
  val pagerHeight = height + headerHeightPx
743
- if (!isPagerRecyclerLayoutSuppressed()) {
744
- pager.measure(
745
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
746
- MeasureSpec.makeMeasureSpec(pagerHeight, MeasureSpec.EXACTLY),
747
- )
748
- }
725
+ pager.measure(
726
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
727
+ MeasureSpec.makeMeasureSpec(pagerHeight, MeasureSpec.EXACTLY),
728
+ )
749
729
  headerView?.measure(
750
730
  MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
751
731
  MeasureSpec.makeMeasureSpec(headerHeightPx, MeasureSpec.EXACTLY),
@@ -757,9 +737,8 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
757
737
  val nativeTabHeight = if (nativeTabBarView.visibility == View.VISIBLE) {
758
738
  nativeTabBarView.preferredHeightPx()
759
739
  } else 0
760
- val tabReservedWidth = nativeTabReservationPx(width)
761
740
  nativeTabBarView.measure(
762
- MeasureSpec.makeMeasureSpec(width - tabReservedWidth, MeasureSpec.EXACTLY),
741
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
763
742
  MeasureSpec.makeMeasureSpec(nativeTabHeight, MeasureSpec.EXACTLY),
764
743
  )
765
744
  val nativeSubHeaderHeight = if (nativeSubHeaderView.visibility == View.VISIBLE) {
@@ -778,9 +757,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
778
757
  // RN ancestors allow overflow; keep translated header content inside this viewport.
779
758
  clipBounds = Rect(0, 0, width, height)
780
759
  if (width <= 0 || height <= 0) return
781
- if (!isPagerRecyclerLayoutSuppressed()) {
782
- pager.layout(0, 0, width, height + headerHeightPx)
783
- }
760
+ pager.layout(0, 0, width, height + headerHeightPx)
784
761
  headerView?.layout(0, 0, width, headerHeightPx)
785
762
  stickyHeaderView?.layout(
786
763
  0,
@@ -789,12 +766,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
789
766
  headerHeightPx + stickyHeaderHeightPx,
790
767
  )
791
768
  val nativeTabHeight = nativeTabBarView.measuredHeight
792
- val tabReservedWidth = nativeTabReservationPx(width)
793
- val tabLeft = if (nativeTabBarView.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
794
- tabReservedWidth
795
- } else 0
796
- nativeTabBarView.layout(tabLeft, headerHeightPx, tabLeft + width - tabReservedWidth,
797
- headerHeightPx + nativeTabHeight)
769
+ nativeTabBarView.layout(0, headerHeightPx, width, headerHeightPx + nativeTabHeight)
798
770
  nativeSubHeaderView.layout(
799
771
  0,
800
772
  headerHeightPx + nativeTabHeight,
@@ -856,7 +828,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
856
828
  }
857
829
  if (!reached()) {
858
830
  if (!isAttachedToWindow || !view.isAttachedToWindow || width <= 0 || height <= 0 ||
859
- view.width <= 0 || view.height <= 0 || isInLayout || view.isInLayout || view.isLayoutSuppressed || view.isComputingLayout ||
831
+ view.width <= 0 || view.height <= 0 || isInLayout || view.isInLayout || view.isComputingLayout ||
860
832
  pageKey(index) != key || primaryScrollableForPage(index) !== view || !isCurrentPageRestore(key, request)) return false
861
833
  val previousSynthetic = syntheticPageRestore
862
834
  syntheticPageRestore = true
@@ -1015,33 +987,13 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
1015
987
  if (!settled) return
1016
988
  pageOffsets[request.key] = 0
1017
989
  if (view is RecyclerView) saveRecyclerReadingAnchor(request.key, view)
1018
- setHeaderOffset(request.logicalOffset.coerceIn(0, headerHeightPx))
1019
- offsetDiag("pager-top-complete-offset=${request.logicalOffset}-token=${request.token}", view, request.key)
990
+ setHeaderOffset(if (request.target == 1) headerHeightPx else 0)
991
+ offsetDiag("pager-top-complete-target=${request.target}-token=${request.token}", view, request.key)
1020
992
  cancelPagerTop()
1021
993
  }
1022
994
 
1023
995
  internal fun scrollToTop(target: Int, animated: Boolean) {
1024
- if (target !in 0..1) return
1025
- scrollHeaderToOffset(if (target == 1) headerHeightPx else 0, animated)
1026
- }
1027
-
1028
- internal fun scrollHeaderBy(delta: Double) {
1029
- if (!delta.isFinite()) return
1030
- val density = resources.displayMetrics.density.toDouble()
1031
- val maxDip = headerHeightPx / density
1032
- val deltaPx = (delta.coerceIn(-maxDip, maxDip) * density).roundToInt()
1033
- scrollHeaderToOffset((headerOffsetPx.toLong() + deltaPx).coerceIn(0, headerHeightPx.toLong()).toInt(), false)
1034
- }
1035
-
1036
- internal fun scrollHeaderToOffset(offset: Double) {
1037
- if (!offset.isFinite()) return
1038
- val density = resources.displayMetrics.density.toDouble()
1039
- val targetPx = (offset.coerceIn(0.0, headerHeightPx / density) * density).roundToInt()
1040
- scrollHeaderToOffset(targetPx, false)
1041
- }
1042
-
1043
- private fun scrollHeaderToOffset(logicalOffset: Int, animated: Boolean) {
1044
- if (pager.scrollState != ViewPager2.SCROLL_STATE_IDLE || transitionHeaderOffsets != null ||
996
+ if (target !in 0..1 || pager.scrollState != ViewPager2.SCROLL_STATE_IDLE || transitionHeaderOffsets != null ||
1045
997
  syntheticPageRestore || selectedPage !in 0 until adapter.itemCount || !isAttachedToWindow) return
1046
998
  val view = observedRecyclerView ?: observedOwnedScroller ?: observedNativeScroller ?: return
1047
999
  val key = currentPageKey()
@@ -1068,12 +1020,11 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
1068
1020
  if (view is RecyclerView) deferredRecyclerGeometry.remove(view)
1069
1021
  if (view is ScrollView) deferredScrollerGeometry.remove(view)
1070
1022
  pageOffsets[key] = 0
1071
- val targetOffset = logicalOffset.coerceIn(0, headerHeightPx)
1072
- val request = PagerTopRequest(view, key, selectedPage, attachmentGeneration, targetOffset, ++pagerTopToken)
1023
+ val request = PagerTopRequest(view, key, selectedPage, attachmentGeneration, target, ++pagerTopToken)
1073
1024
  pagerTopRequest = request
1074
1025
  commandBodyKeys.add(key)
1075
- setHeaderOffset(targetOffset)
1076
- offsetDiag("pager-top-begin-offset=$targetOffset-animated=$animated-token=${request.token}", view, key)
1026
+ setHeaderOffset(if (target == 1) headerHeightPx else 0)
1027
+ offsetDiag("pager-top-begin-target=$target-animated=$animated-token=${request.token}", view, key)
1077
1028
  dispatchingPagerTop = true
1078
1029
  try {
1079
1030
  when (view) {
@@ -1082,7 +1033,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
1082
1033
  if (animated && !recyclerIsAtBodyTop(view)) view.smoothScrollToPosition(0)
1083
1034
  else if (manager != null && manager.orientation == RecyclerView.VERTICAL) {
1084
1035
  manager.scrollToPositionWithOffset(0, 0)
1085
- if (view.width > 0 && view.height > 0 && !isInLayout && !view.isInLayout && !view.isLayoutSuppressed && !view.isComputingLayout) {
1036
+ if (view.width > 0 && view.height > 0 && !isInLayout && !view.isInLayout && !view.isComputingLayout) {
1086
1037
  view.measure(MeasureSpec.makeMeasureSpec(view.width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(view.height, MeasureSpec.EXACTLY))
1087
1038
  view.layout(view.left, view.top, view.right, view.bottom)
1088
1039
  }
@@ -1687,7 +1638,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
1687
1638
  if (anchorOffset != null) manager?.scrollToPositionWithOffset(first, anchorOffset)
1688
1639
  // React Native can stop this nested requestLayout at its React-owned parent.
1689
1640
  // Commit the changed inset before drawing instead of waiting for a data update.
1690
- if (recycler.width > 0 && recycler.height > 0 && !recycler.isLayoutSuppressed && !recycler.isComputingLayout) {
1641
+ if (recycler.width > 0 && recycler.height > 0 && !recycler.isComputingLayout) {
1691
1642
  recycler.forceLayout()
1692
1643
  recycler.measure(
1693
1644
  MeasureSpec.makeMeasureSpec(recycler.width, MeasureSpec.EXACTLY),
@@ -2061,16 +2012,14 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
2061
2012
  }
2062
2013
  }
2063
2014
 
2064
- private fun headerRegionAt(x: Float, y: Float): String? {
2015
+ private fun headerRegionAt(y: Float): String? {
2065
2016
  val headerBottom = headerHeightPx - headerOffsetPx
2066
2017
  val stickyBottom = headerHeightPx + stickyHeaderHeightPx - headerOffsetPx
2067
2018
  return when {
2068
2019
  y < 0 || y >= stickyBottom -> null
2069
2020
  y < headerBottom -> "header"
2070
2021
  y < headerBottom + nativeTabBarView.measuredHeight &&
2071
- nativeTabBarView.visibility == View.VISIBLE ->
2072
- if (x >= nativeTabBarView.left && x < nativeTabBarView.right) "primary-tab"
2073
- else "sticky-controls"
2022
+ nativeTabBarView.visibility == View.VISIBLE -> "primary-tab"
2074
2023
  y < headerBottom + nativeTabBarView.measuredHeight + nativeSubHeaderView.measuredHeight &&
2075
2024
  nativeSubHeaderView.visibility == View.VISIBLE -> "secondary-header"
2076
2025
  else -> "sticky-controls"
@@ -2179,7 +2128,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
2179
2128
  pressCancelled = false
2180
2129
  nativeGestureStarted = false
2181
2130
  nestedNativeGestureStarted = false
2182
- headerTouchRegion = headerRegionAt(event.x, event.y) ?: "none"
2131
+ headerTouchRegion = headerRegionAt(event.y) ?: "none"
2183
2132
  headerTouchActive = headerTouchRegion != "none"
2184
2133
  if (!headerTouchActive) return super.dispatchTouchEvent(event)
2185
2134
  headerDownX = event.x
@@ -239,11 +239,6 @@ class CollapsiblePagerViewManager : ViewGroupManager<CollapsiblePagerHost>(),
239
239
  view.applyNativeHeaderStyle()
240
240
  }
241
241
 
242
- @ReactProp(name = "nativeTabBarTrailingReservedWidth", defaultDouble = 0.0)
243
- override fun setNativeTabBarTrailingReservedWidth(view: CollapsiblePagerHost?, value: Double) {
244
- view?.nativeTabBarTrailingReservedWidth = value
245
- }
246
-
247
242
  @ReactProp(name = "nativeTabBarContentPaddingHorizontal")
248
243
  override fun setNativeTabBarContentPaddingHorizontal(
249
244
  view: CollapsiblePagerHost?,
@@ -348,14 +343,6 @@ class CollapsiblePagerViewManager : ViewGroupManager<CollapsiblePagerHost>(),
348
343
  view?.scrollToTop(target, animated)
349
344
  }
350
345
 
351
- override fun scrollHeaderBy(view: CollapsiblePagerHost?, delta: Double) {
352
- view?.scrollHeaderBy(delta)
353
- }
354
-
355
- override fun scrollHeaderToOffset(view: CollapsiblePagerHost?, offset: Double) {
356
- view?.scrollHeaderToOffset(offset)
357
- }
358
-
359
346
  override fun setPage(view: CollapsiblePagerHost?, selectedPage: Int) {
360
347
  if (view != null && selectedPage in 0 until view.adapter.itemCount) {
361
348
  view.beginPageTransition(selectedPage)
@@ -37,50 +37,26 @@ class NativeScrollerView(context: Context) : FrameLayout(context) {
37
37
  setMeasuredDimension(MeasureSpec.getSize(widthSpec), MeasureSpec.getSize(heightSpec))
38
38
  }
39
39
  override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) = Unit
40
-
41
- override fun startViewTransition(view: View) {
42
- super.startViewTransition(view)
43
- if (view === reactChild) transitioningReactChild = view
44
- }
45
-
46
- override fun endViewTransition(view: View) {
47
- super.endViewTransition(view)
48
- if (transitioningReactChild === view) transitioningReactChild = null
49
- if (retainedReactChild === view) {
50
- clearRetainedGeometry()
51
- if (isAttachedToWindow) scheduleLayout()
52
- }
53
- }
54
40
  }
55
41
  private val contentFrame = object : FrameLayout(context) {
56
42
  override fun onMeasure(widthSpec: Int, heightSpec: Int) {
57
43
  setMeasuredDimension(
58
- max(MeasureSpec.getSize(widthSpec), effectiveContentWidthPx + insetLeft + insetRight),
59
- effectiveContentHeightPx + contentTop + insetBottom,
44
+ max(MeasureSpec.getSize(widthSpec), contentWidthPx + insetLeft + insetRight),
45
+ contentHeightPx + contentTop + insetBottom,
60
46
  )
61
47
  }
62
48
 
63
49
  override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
64
50
  // Only the native origin layer moves. Fabric owns its React child's layout/transform.
65
- val contentWidth = effectiveContentWidthPx
66
- val contentHeight = effectiveContentHeightPx
67
51
  contentOriginFrame.measure(
68
- MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY),
69
- MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY),
52
+ MeasureSpec.makeMeasureSpec(contentWidthPx, MeasureSpec.EXACTLY),
53
+ MeasureSpec.makeMeasureSpec(contentHeightPx, MeasureSpec.EXACTLY),
70
54
  )
71
- contentOriginFrame.layout(insetLeft, contentTop, insetLeft + contentWidth, contentTop + contentHeight)
55
+ contentOriginFrame.layout(insetLeft, contentTop, insetLeft + contentWidthPx, contentTop + contentHeightPx)
72
56
  }
73
57
  }
74
58
  private val pagerInsets = WeakHashMap<View, Int>()
75
59
  private var reactChild: View? = null
76
- private var transitioningReactChild: View? = null
77
- private var retainedReactChild: View? = null
78
- private var retainedContentWidthPx = 0
79
- private var retainedContentHeightPx = 0
80
- private val effectiveContentWidthPx: Int
81
- get() = if (reactChild == null && retainedReactChild != null) retainedContentWidthPx else contentWidthPx
82
- private val effectiveContentHeightPx: Int
83
- get() = if (reactChild == null && retainedReactChild != null) retainedContentHeightPx else contentHeightPx
84
60
  private var fabricState: StateWrapper? = null
85
61
  private var lastOriginX: Int? = null
86
62
  private var lastOriginY: Int? = null
@@ -196,8 +172,6 @@ class NativeScrollerView(context: Context) : FrameLayout(context) {
196
172
  internal fun addReactChild(child: View, index: Int) {
197
173
  require(index == 0 && reactChild == null) { "NativeScroller requires one React content view" }
198
174
  viewport.stopMotion()
199
- clearRetainedGeometry()
200
- transitioningReactChild = null
201
175
  reactChild = child
202
176
  contentOriginFrame.addView(child)
203
177
  scheduleLayout()
@@ -206,29 +180,13 @@ class NativeScrollerView(context: Context) : FrameLayout(context) {
206
180
  internal fun removeReactChild() {
207
181
  viewport.stopMotion()
208
182
  pendingRestore.cancel()
209
- reactChild?.let { child ->
210
- clearRetainedGeometry()
211
- // Fabric removes the child before its disappearing view finishes drawing.
212
- if (transitioningReactChild === child &&
213
- contentOriginFrame.width > 0 && contentOriginFrame.height > 0) {
214
- retainedReactChild = child
215
- retainedContentWidthPx = contentOriginFrame.width
216
- retainedContentHeightPx = contentOriginFrame.height
217
- }
218
- contentOriginFrame.removeView(child)
219
- }
183
+ reactChild?.let(contentOriginFrame::removeView)
220
184
  reactChild = null
221
185
  contentWidthPx = 0
222
186
  contentHeightPx = 0
223
187
  scheduleLayout()
224
188
  }
225
189
 
226
- private fun clearRetainedGeometry() {
227
- retainedReactChild = null
228
- retainedContentWidthPx = 0
229
- retainedContentHeightPx = 0
230
- }
231
-
232
190
  internal fun reactChildCount() = if (reactChild == null) 0 else 1
233
191
  internal fun reactChildAt(index: Int): View {
234
192
  require(index == 0)
@@ -423,8 +381,6 @@ class NativeScrollerView(context: Context) : FrameLayout(context) {
423
381
  lastContentViewportHeight = null
424
382
  viewport.stopMotion()
425
383
  viewport.setTag(R.id.onekey_native_scroll_coordinator_offset_listener, null)
426
- transitioningReactChild = null
427
- clearRetainedGeometry()
428
384
  super.onDetachedFromWindow()
429
385
  }
430
386
 
package/docs/SPEC.md CHANGED
@@ -74,7 +74,7 @@ the shared host before their old scroll-edge interaction is released.
74
74
  The measured overlay height is excluded from the collapsible header distance:
75
75
  `header` remains collapsible and `stickyHeader` pins immediately below the
76
76
  overlay. Selection, cancellation, reuse and disposal rebind or release the
77
- interaction and restore any prior scroll-edge style. The existing owner-scoped native sticky-height callback also reaches NativeList hosts, so their React container slots clip below the same fixed viewport region and release it when the owner detaches. Native scroll/refresh
77
+ interaction and restore any prior scroll-edge style. Native scroll/refresh
78
78
  coordination continues on the main thread without per-frame JavaScript work.
79
79
  Refresh retains the pager-owned `overlay + header + sticky` top inset but offsets
80
80
  the indicator by `header + sticky`, so its spinner begins below the fixed overlay.
@@ -171,24 +171,6 @@ intent. Real vertical consumption, a newer valid command, or owner detachment
171
171
  releases the target. Existing NativeList and NativeScroller public coordinates
172
172
  are unchanged. The same page-top/sticky-top destinations are available without smooth-header mode.
173
173
 
174
- ## Current-page partial header command
175
-
176
- `scrollHeaderBy(delta: number)` moves the current page's shared collapsible
177
- header from its displayed offset C to `clamp(C + delta, 0, D)`.
178
- `scrollHeaderToOffset(offset: number)` moves it to `clamp(offset, 0, D)`
179
- regardless of intervening native keyboard or user scrolling. D is the latest
180
- measured collapsible height. Inputs use logical points on iOS and Android and
181
- CSS pixels on Web. Non-finite values are ignored. The header and current body move immediately
182
- so a focused input becomes visible before the next keyboard frame. A valid command
183
- clears only the current page's body reading position B; it never changes
184
- another page's memory or adds permanent content/insets. It requires the same
185
- stable attached current owner as the page-top commands and is a safe no-op
186
- during horizontal transitions, before mount, or after disposal. A newer
187
- command or real user vertical consumption takes ownership from an older one.
188
- Dynamic header measurements clamp any in-flight target to the latest D.
189
- This command allows a focused input inside the shared header to move above a
190
- keyboard without replacing the pager's sole vertical scroll owner.
191
-
192
174
  ## Independent NativeScroller migration
193
175
 
194
176
  Status: implemented in source. Both native builds pass. Android focused emulator
@@ -351,32 +333,6 @@ warnings were observed. Keyboard, multi-touch, physical-device performance and
351
333
  the full lifecycle/interaction matrix remain unverified. These are local source
352
334
  builds; the currently installed npm version does not yet contain this migration.
353
335
 
354
- ## Android removal-transition geometry retention
355
-
356
- Status: implemented locally; unreleased. On the Android UI thread, during a
357
- screen removal transition,
358
- NativeList and CollapsiblePager layout entry points respect the RecyclerView
359
- suppression state established by the screen-transition owner. The owner restores
360
- the original state when its last transition ownership ends. This adds no public
361
- API and does not change Web or iOS behavior.
362
-
363
- If Fabric removes the current NativeScroller React child while its origin-frame
364
- transition remains active, Android retains the already displayed native extent
365
- for drawing. The current child and raw content dimensions still clear normally;
366
- only native content measurement and layout use the retained effective extent.
367
- The matching transition end or detach clears the private child and extent state
368
- idempotently. Adding a new child cancels prior retention, and a late end for the
369
- old child cannot alter the new content. Ordinary removal, size events and scroll
370
- commands keep their existing semantics.
371
-
372
- Local arm64 DevShell acceptance covered Base, CXO and Overview exit content,
373
- normal scrolling, RecyclerView ownership and suppression cleanup for the
374
- observed CXO exit, and NativeScroller raw-zero content dimensions with a
375
- nonzero displayed origin followed by detach cleanup. Nested-owner and cancel
376
- paths, Overview rebound/cancel, natural resize and new-child runtime cases,
377
- and the full performance matrix remain unverified. SDK publication and
378
- consumer version alignment are separate steps.
379
-
380
336
  ## iOS vertical-scroll press cancellation
381
337
 
382
338
  Status: implemented in source. A source-identical `node_modules` build was
@@ -516,28 +472,6 @@ Status: implemented; focused native-wrapper tests cover both directions,
516
472
  explicit overrides and in-flight/superseded command targets. Home applies this policy to its JS tab bar, with iOS and
517
473
  Android simulator recordings verifying distant jumps and adjacent transitions.
518
474
 
519
- ## Native tab trailing control reservation
520
-
521
- Status: implemented locally, unreleased. The focused Android host test passed
522
- (1/1); iOS hit tests had 8 passes and 1 existing skip. iOS companion-app UI
523
- acceptance remains unverified.
524
- `nativeTabBar.style.trailingReservedWidth` is an optional logical trailing
525
- reservation for a sibling React control in the full-width sticky header. It
526
- defaults to zero. JavaScript normalizes non-finite or negative input to zero;
527
- native also clamps the reservation to the actual tab viewport width. On Android
528
- and iOS, only the native tab viewport shrinks from `W` to `W - R`: its frame
529
- begins at zero in LTR and at `R` in RTL. The sticky React header, its height,
530
- and page content keep their original full-width geometry. Touches in the
531
- reserved region reach the sibling control, while native tab hit testing and
532
- last-tab visibility use the reduced viewport. Changing the reservation updates
533
- that viewport; removing it or recycling the pager restores the default zero.
534
- Web does not render the native tab and ignores this style field. For a positive
535
- reservation, the box-none sticky React control draws above the native tab so
536
- its inward fade and toolbar hit area remain visible; zero preserves the old
537
- native-tab foreground order. Acceptance requires focused native layout and hit
538
- testing for zero, nonzero, reset, and RTL reservations; type forwarding alone
539
- is insufficient.
540
-
541
475
  ## Android refresh foreground coordination
542
476
 
543
477
  Status: implemented; Android Home simulator placement verified on 2026-09-28.
@@ -18,7 +18,6 @@
18
18
 
19
19
  // OneKey patch: deduplicate structural mutation parents before scanning the page.
20
20
  #include <unordered_set>
21
- #include <cmath>
22
21
 
23
22
  using namespace facebook::react;
24
23
 
@@ -1472,7 +1471,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1472
1471
  - (void)liftSharedHeadersForPagerTransition:(NSString *)reason;
1473
1472
  - (void)restoreSharedHeadersToContainer;
1474
1473
  - (void)layoutSharedHeaders;
1475
- - (void)bringReservedStickyControlToFront;
1476
1474
  - (void)restorePinnedStickyHeader;
1477
1475
  - (void)updatePinnedStickyHeader;
1478
1476
  - (UIScrollView *)currentVerticalScrollViewForParentPager;
@@ -1560,7 +1558,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1560
1558
  NSUInteger _pendingTopInsetReboundGeneration;
1561
1559
  NSTimer *_topInsetReboundTimer;
1562
1560
  CGFloat _nativeTabBarHeight;
1563
- CGFloat _nativeTabBarTrailingReservedWidth;
1564
1561
  CGFloat _nativeSubHeaderHeight;
1565
1562
  CGFloat _headerOffset;
1566
1563
  BOOL _scrollEnabled;
@@ -2482,7 +2479,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2482
2479
  }
2483
2480
  [_nativeTabBarView updateAncestorGesturePrecedence];
2484
2481
  [_nativeSubHeaderView updateAncestorGesturePrecedence];
2485
- [self bringReservedStickyControlToFront];
2486
2482
  }
2487
2483
 
2488
2484
  - (void)restoreSharedHeadersToContainer
@@ -2588,11 +2584,10 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2588
2584
  CGFloat nativeTabBarVisibleHeight = _nativeTabBarView.hidden
2589
2585
  ? 0
2590
2586
  : MIN(_stickyHeaderHeight, _nativeTabBarHeight);
2591
- CGFloat tabReservedWidth = MIN(width, MAX(0, _nativeTabBarTrailingReservedWidth));
2592
2587
  _nativeTabBarView.frame = CGRectMake(
2593
- self.isLtrLayout ? 0 : tabReservedWidth,
2588
+ 0,
2594
2589
  stickyChildY,
2595
- width - tabReservedWidth,
2590
+ width,
2596
2591
  nativeTabBarVisibleHeight
2597
2592
  );
2598
2593
  _nativeSubHeaderView.frame = CGRectMake(
@@ -2606,26 +2601,9 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2606
2601
  );
2607
2602
  _topOverlayHostView.frame = CGRectMake(0, 0, width, self.effectiveTopOverlayHeight);
2608
2603
  _topOverlayView.frame = _topOverlayHostView.bounds;
2609
- [self bringReservedStickyControlToFront];
2610
2604
  [self applyHeaderOffset];
2611
2605
  }
2612
2606
 
2613
- - (void)bringReservedStickyControlToFront
2614
- {
2615
- if (_stickyHeaderView != nil &&
2616
- _stickyHeaderView.superview == _nativeTabBarView.superview) {
2617
- if (_nativeTabBarTrailingReservedWidth > 0) {
2618
- [_stickyHeaderView.superview bringSubviewToFront:_stickyHeaderView];
2619
- } else {
2620
- [_nativeTabBarView.superview bringSubviewToFront:_nativeTabBarView];
2621
- if (!_nativeSubHeaderView.hidden &&
2622
- _nativeSubHeaderView.superview == _nativeTabBarView.superview) {
2623
- [_nativeSubHeaderView.superview bringSubviewToFront:_nativeSubHeaderView];
2624
- }
2625
- }
2626
- }
2627
- }
2628
-
2629
2607
  - (void)updatePinnedStickyHeader
2630
2608
  {
2631
2609
  BOOL attachedToList = _nativeSmoothHeaderScrollEnabled &&
@@ -2672,10 +2650,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2672
2650
  CGFloat childY = shouldPin ? 0 : _headerHeight;
2673
2651
  CGFloat width = CGRectGetWidth(self.bounds);
2674
2652
  CGFloat tabHeight = _nativeTabBarView.hidden ? 0 : MIN(_stickyHeaderHeight, _nativeTabBarHeight);
2675
- CGFloat tabReservedWidth = MIN(width, MAX(0, _nativeTabBarTrailingReservedWidth));
2676
2653
  _stickyHeaderView.frame = CGRectMake(0, childY, width, _stickyHeaderHeight);
2677
- _nativeTabBarView.frame = CGRectMake(self.isLtrLayout ? 0 : tabReservedWidth,
2678
- childY, width - tabReservedWidth, tabHeight);
2654
+ _nativeTabBarView.frame = CGRectMake(0, childY, width, tabHeight);
2679
2655
  _nativeSubHeaderView.frame = CGRectMake(0, childY + tabHeight, width,
2680
2656
  MIN(MAX(0, _stickyHeaderHeight - tabHeight), _nativeSubHeaderHeight));
2681
2657
  _pinnedStickyHostView.hidden = !shouldPin;
@@ -2695,7 +2671,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2695
2671
  }
2696
2672
  [_nativeTabBarView updateAncestorGesturePrecedence];
2697
2673
  [_nativeSubHeaderView updateAncestorGesturePrecedence];
2698
- [self bringReservedStickyControlToFront];
2699
2674
  [self updateTopScrollEdgeForScrollView:_observedScrollView];
2700
2675
  [self logHomeVisualGeometry:shouldPin ? @"pin" : @"unpin"
2701
2676
  scrollView:_observedScrollView];
@@ -3075,7 +3050,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
3075
3050
  if (_stickyHeaderView != nil) [_sharedHeaderHostView bringSubviewToFront:_stickyHeaderView];
3076
3051
  if (!_nativeTabBarView.hidden) [_sharedHeaderHostView bringSubviewToFront:_nativeTabBarView];
3077
3052
  if (!_nativeSubHeaderView.hidden) [_sharedHeaderHostView bringSubviewToFront:_nativeSubHeaderView];
3078
- [self bringReservedStickyControlToFront];
3079
3053
  [_containerView bringSubviewToFront:_sharedHeaderHostView];
3080
3054
  if (_stickyHeaderIsPinned) {
3081
3055
  [_pinnedStickyHostView.superview bringSubviewToFront:_pinnedStickyHostView];
@@ -3288,7 +3262,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
3288
3262
  _homeVisualLastLayoutLog = 0;
3289
3263
  _homeVisualLastGeometrySignature = nil;
3290
3264
  _nativeTabBarHeight = 44;
3291
- _nativeTabBarTrailingReservedWidth = 0;
3292
3265
  _nativeSubHeaderHeight = 74;
3293
3266
  _headerOffset = 0;
3294
3267
  _currentLogicalOffset = 0;
@@ -3370,13 +3343,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
3370
3343
  _layoutDirection = RCTNSStringFromString(toString(newViewProps.layoutDirection));
3371
3344
  [_nativeTabBarView setLeftToRight:self.isLtrLayout];
3372
3345
  [_nativeSubHeaderView setLeftToRight:self.isLtrLayout];
3373
- [self setNeedsLayout];
3374
- }
3375
- if (_needsPropsReapply ||
3376
- oldViewProps.nativeTabBarTrailingReservedWidth != newViewProps.nativeTabBarTrailingReservedWidth) {
3377
- double reserved = newViewProps.nativeTabBarTrailingReservedWidth;
3378
- _nativeTabBarTrailingReservedWidth = std::isfinite(reserved) && reserved >= 0 ? reserved : 0;
3379
- [self setNeedsLayout];
3380
3346
  }
3381
3347
  BOOL insetsChanged = NO;
3382
3348
  if (_needsPropsReapply || oldViewProps.headerHeight != newViewProps.headerHeight) {
@@ -3508,7 +3474,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
3508
3474
  if (!_nativeSubHeaderView.hidden) {
3509
3475
  [_nativeSubHeaderView.superview bringSubviewToFront:_nativeSubHeaderView];
3510
3476
  }
3511
- [self bringReservedStickyControlToFront];
3512
3477
  [_sharedHeaderHostView.superview bringSubviewToFront:_sharedHeaderHostView];
3513
3478
  if (_stickyHeaderIsPinned) {
3514
3479
  [_pinnedStickyHostView.superview bringSubviewToFront:_pinnedStickyHostView];
@@ -4473,9 +4438,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
4473
4438
 
4474
4439
  - (void)updateNativeScrollerViewport:(UIScrollView *)scrollView stickyHeight:(CGFloat)height
4475
4440
  {
4476
- id host = scrollView.superview;
4477
- if ([host respondsToSelector:@selector(setPagerStickyHeight:forOwner:)]) {
4478
- [host setPagerStickyHeight:height forOwner:self];
4441
+ if ([scrollView.superview isKindOfClass:RNCNativeScrollerComponentView.class]) {
4442
+ [(RNCNativeScrollerComponentView *)scrollView.superview setPagerStickyHeight:height forOwner:self];
4479
4443
  }
4480
4444
  }
4481
4445
 
@@ -5269,13 +5233,6 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
5269
5233
  {
5270
5234
  UIScrollView *scrollView = _pagerTopScrollView;
5271
5235
  if (_dispatchingPagerTop || ![self matchesPagerTop:scrollView]) return;
5272
- CGFloat clampedTarget = MIN(MAX(_pagerTopLogicalTarget, 0), _headerHeight);
5273
- if (clampedTarget != _pagerTopLogicalTarget) {
5274
- _pagerTopLogicalTarget = clampedTarget;
5275
- CGFloat physicalTarget = RNCLimitRestoredScrollOffset(clampedTarget - scrollView.contentInset.top,
5276
- scrollView.contentSize.height, scrollView.bounds.size.height, scrollView.contentInset.top, scrollView.contentInset.bottom);
5277
- [scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, physicalTarget) animated:NO];
5278
- }
5279
5236
  CGFloat logical = scrollView.contentOffset.y + scrollView.contentInset.top;
5280
5237
  if (fabs(logical - _pagerTopLogicalTarget) > 0.5) return;
5281
5238
  _pageOffsets[_pagerTopPageKey] = @(_nativeSmoothHeaderScrollEnabled ? 0 : _pagerTopLogicalTarget);
@@ -5287,29 +5244,11 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
5287
5244
  }
5288
5245
 
5289
5246
  - (void)scrollToTop:(NSInteger)target animated:(BOOL)animated
5290
- {
5291
- if (target != 0 && target != 1) return;
5292
- [self scrollHeaderToOffset:(target == 1 ? _headerHeight : 0) animated:animated];
5293
- }
5294
-
5295
- - (void)scrollHeaderBy:(double)delta
5296
- {
5297
- if (!std::isfinite(delta)) return;
5298
- [self scrollHeaderToOffset:MIN(MAX(_headerOffset + delta, 0), _headerHeight) animated:NO];
5299
- }
5300
-
5301
- - (void)scrollHeaderToOffset:(double)offset
5302
- {
5303
- if (!std::isfinite(offset)) return;
5304
- [self scrollHeaderToOffset:MIN(MAX(offset, 0), _headerHeight) animated:NO];
5305
- }
5306
-
5307
- - (void)scrollHeaderToOffset:(CGFloat)logicalTarget animated:(BOOL)animated
5308
5247
  {
5309
5248
  [self cancelTopInsetReboundCheck];
5310
5249
  UIScrollView *scrollView = _observedScrollView;
5311
5250
  NSString *key = _observedPageKey;
5312
- if (_isBeingRecycled || _transitioning || _isPagerDragging ||
5251
+ if ((target != 0 && target != 1) || _isBeingRecycled || _transitioning || _isPagerDragging ||
5313
5252
  _transitionPageOffsets != nil || _syntheticPageRestore || scrollView == nil || scrollView.window == nil ||
5314
5253
  key == nil || ![key isEqualToString:self.currentPageKey] || _observedPageIndex != _currentIndex ||
5315
5254
  _observedPageGeneration != _generation || self.window == nil) return;
@@ -5325,7 +5264,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
5325
5264
  [_alignedBodyKeys removeObject:key];
5326
5265
  [_suspendedBodyKeys removeObject:key];
5327
5266
  [_displayBodyOverrides removeObjectForKey:key];
5328
- logicalTarget = MIN(MAX(logicalTarget, 0), _headerHeight);
5267
+ CGFloat logicalTarget = target == 1 ? _headerHeight : 0;
5329
5268
  _pageOffsets[key] = @(_nativeSmoothHeaderScrollEnabled ? 0 : logicalTarget);
5330
5269
  _pagerTopScrollView = scrollView;
5331
5270
  _pagerTopPageKey = [key copy];
@@ -2,22 +2,13 @@
2
2
 
3
3
  #import "RNCCollapsiblePagerViewComponentView.h"
4
4
  #import "RNCPagerViewComponentView.h"
5
- #import <React/RCTViewComponentView.h>
6
5
  #import <react/renderer/components/pagerview/Props.h>
7
- #import <react/renderer/components/view/ViewProps.h>
8
6
 
9
7
  using namespace facebook::react;
10
8
 
11
9
  @interface RNCCollapsiblePagerViewComponentView (ScrollEdgeTesting)
12
10
  - (void)updateTopScrollEdgeForScrollView:(UIScrollView *)scrollView;
13
11
  - (void)updateScrollEdgeContentMasks;
14
- - (void)layoutSharedHeaders;
15
- @end
16
-
17
- @interface UIView (NativeTabReservationTesting)
18
- - (void)updateItemsJSON:(NSString *)itemsJSON;
19
- - (void)setProgress:(CGFloat)progress;
20
- - (void)setLeftToRight:(BOOL)leftToRight;
21
12
  @end
22
13
 
23
14
  @interface RNCCollapsiblePagerHitTestTests : XCTestCase
@@ -65,84 +56,6 @@ using namespace facebook::react;
65
56
  XCTAssertEqual([self.root hitTest:CGPointMake(20, 80) withEvent:nil], self.toolbar);
66
57
  }
67
58
 
68
- - (void)testNativeTabReservationKeepsReactToolbarAboveTabsAndResetsInRTL
69
- {
70
- UIView *sharedHost = [self.pager valueForKey:@"sharedHeaderHostView"];
71
- UIView *tabBar = [self.pager valueForKey:@"nativeTabBarView"];
72
- [tabBar updateItemsJSON:@"[{\"key\":\"a\",\"title\":\"Overview\"},"
73
- @"{\"key\":\"b\",\"title\":\"Ethereum\"},"
74
- @"{\"key\":\"c\",\"title\":\"BNB Chain\"},"
75
- @"{\"key\":\"d\",\"title\":\"Polygon\"}]"];
76
- RCTViewComponentView *sticky = [RCTViewComponentView new];
77
- auto stickyProps = std::make_shared<ViewProps>();
78
- stickyProps->pointerEvents = PointerEventsMode::BoxNone;
79
- [sticky updateProps:stickyProps oldProps:nullptr];
80
- UIButton *trailingControl = [[UIButton alloc] initWithFrame:CGRectMake(250, 0, 70, 44)];
81
- [sticky addSubview:trailingControl];
82
- [sharedHost addSubview:sticky];
83
- [sharedHost addSubview:tabBar];
84
- [self.pager setValue:sticky forKey:@"stickyHeaderView"];
85
- [self.pager setValue:@0 forKey:@"headerHeight"];
86
- [self.pager setValue:@44 forKey:@"stickyHeaderHeight"];
87
- tabBar.hidden = NO;
88
-
89
- [self.pager setValue:@64 forKey:@"nativeTabBarTrailingReservedWidth"];
90
- [self.pager layoutSharedHeaders];
91
- XCTAssertEqualWithAccuracy(CGRectGetWidth(sticky.frame), 320, 0.001);
92
- XCTAssertEqualWithAccuracy(CGRectGetWidth(tabBar.frame), 256, 0.001);
93
- XCTAssertEqualWithAccuracy(CGRectGetMinX(tabBar.frame), 0, 0.001);
94
- XCTAssertEqual(sharedHost.subviews.lastObject, sticky);
95
- XCTAssertEqual([sharedHost hitTest:CGPointMake(300, 22) withEvent:nil], trailingControl);
96
- XCTAssertTrue([tabBar pointInside:CGPointMake(250, 22) withEvent:nil]);
97
- [tabBar layoutIfNeeded];
98
- [tabBar setProgress:3];
99
- UIScrollView *tabScroll = (UIScrollView *)tabBar.subviews.firstObject;
100
- NSArray<UIButton *> *buttons = [tabScroll.subviews filteredArrayUsingPredicate:
101
- [NSPredicate predicateWithBlock:^BOOL(UIView *view, NSDictionary *bindings) {
102
- return [view isKindOfClass:UIButton.class];
103
- }]];
104
- UIButton *lastTab = buttons.lastObject;
105
- XCTAssertNotNil(lastTab);
106
- XCTAssertGreaterThanOrEqual(CGRectGetMinX(lastTab.frame), tabScroll.contentOffset.x - 1);
107
- XCTAssertLessThanOrEqual(CGRectGetMaxX(lastTab.frame),
108
- tabScroll.contentOffset.x + CGRectGetWidth(tabScroll.bounds) + 1);
109
-
110
- [self.pager setValue:@"rtl" forKey:@"layoutDirection"];
111
- [tabBar setLeftToRight:NO];
112
- trailingControl.frame = CGRectMake(0, 0, 70, 44);
113
- [self.pager layoutSharedHeaders];
114
- XCTAssertEqualWithAccuracy(CGRectGetMinX(tabBar.frame), 64, 0.001);
115
- XCTAssertEqualWithAccuracy(CGRectGetMaxX(tabBar.frame), 320, 0.001);
116
- XCTAssertEqual([sharedHost hitTest:CGPointMake(20, 22) withEvent:nil], trailingControl);
117
-
118
- [self.pager setValue:@500 forKey:@"nativeTabBarTrailingReservedWidth"];
119
- [self.pager layoutSharedHeaders];
120
- XCTAssertEqualWithAccuracy(CGRectGetMinX(tabBar.frame), 320, 0.001);
121
- XCTAssertEqualWithAccuracy(CGRectGetWidth(tabBar.frame), 0, 0.001);
122
-
123
- [self.pager setValue:@0 forKey:@"nativeTabBarTrailingReservedWidth"];
124
- [self.pager layoutSharedHeaders];
125
- XCTAssertEqualWithAccuracy(CGRectGetMinX(tabBar.frame), 0, 0.001);
126
- XCTAssertEqualWithAccuracy(CGRectGetWidth(tabBar.frame), 320, 0.001);
127
- XCTAssertEqual(sharedHost.subviews.lastObject, tabBar);
128
- }
129
-
130
- - (void)testNativeTabReservationPropsRestoreDefaultOnRemoval
131
- {
132
- auto reservedProps = std::make_shared<RNCCollapsiblePagerViewProps>();
133
- reservedProps->nativeSmoothHeaderScrollEnabled = true;
134
- reservedProps->nativeTabBarTrailingReservedWidth = 64;
135
- [self.pager updateProps:reservedProps oldProps:nullptr];
136
- XCTAssertEqualWithAccuracy(
137
- [[self.pager valueForKey:@"nativeTabBarTrailingReservedWidth"] doubleValue], 64, 0.001);
138
-
139
- auto defaultProps = std::make_shared<RNCCollapsiblePagerViewProps>();
140
- defaultProps->nativeSmoothHeaderScrollEnabled = true;
141
- [self.pager updateProps:defaultProps oldProps:reservedProps];
142
- XCTAssertEqualWithAccuracy(
143
- [[self.pager valueForKey:@"nativeTabBarTrailingReservedWidth"] doubleValue], 0, 0.001);
144
- }
145
-
146
59
  - (void)testPinnedChildOutsideHeaderHostStillReceivesViewportTouches
147
60
  {
148
61
  XCTAssertEqual([self.pager hitTest:CGPointMake(20, 90) withEvent:nil], self.sticky);
@@ -82,12 +82,6 @@ export class CollapsiblePagerView extends React.PureComponent {
82
82
  scrollToPageTop = options => {
83
83
  if (this.nativeRef) CollapsiblePagerViewNativeCommands.scrollToTop(this.nativeRef, 0, options?.animated ?? true);
84
84
  };
85
- scrollHeaderBy = delta => {
86
- if (this.nativeRef && Number.isFinite(delta)) CollapsiblePagerViewNativeCommands.scrollHeaderBy(this.nativeRef, delta);
87
- };
88
- scrollHeaderToOffset = offset => {
89
- if (this.nativeRef && Number.isFinite(offset)) CollapsiblePagerViewNativeCommands.scrollHeaderToOffset(this.nativeRef, offset);
90
- };
91
85
  scrollToStickyTop = options => {
92
86
  if (this.nativeRef) CollapsiblePagerViewNativeCommands.scrollToTop(this.nativeRef, 1, options?.animated ?? true);
93
87
  };
@@ -102,7 +96,7 @@ export class CollapsiblePagerView extends React.PureComponent {
102
96
  nativeTabBarEnabled() {
103
97
  return (Platform.OS === "ios" || Platform.OS === "android") && !!this.props.nativeTabBar?.items.length;
104
98
  }
105
- dispatchNativeTabPageCommand(position, animated, expectedKey) {
99
+ dispatchNativeTabPageCommand(position, animated) {
106
100
  const commandId = ++this.nativeTabCommandId;
107
101
  this.nativeTabCommandQueuedDuringScroll = this.nativePagerScrollState !== "idle";
108
102
  this.latestNativeTabTarget = position;
@@ -110,7 +104,7 @@ export class CollapsiblePagerView extends React.PureComponent {
110
104
  transientRetainedPages: state.transientRetainedPages.includes(position) ? state.transientRetainedPages : [...state.transientRetainedPages, position]
111
105
  }), () => {
112
106
  requestAnimationFrame(() => {
113
- if (commandId !== this.nativeTabCommandId || !this.nativeRef || expectedKey !== undefined && (!this.nativeTabBarEnabled() || this.props.nativeTabBar?.items[position]?.key !== expectedKey)) {
107
+ if (commandId !== this.nativeTabCommandId || !this.nativeRef) {
114
108
  return;
115
109
  }
116
110
  this.dispatchedNativeTabTarget = position;
@@ -192,13 +186,10 @@ export class CollapsiblePagerView extends React.PureComponent {
192
186
  };
193
187
  onNativeTabPress = event => {
194
188
  const position = pageIndex(event.nativeEvent.position);
195
- if (!this.nativeTabBarEnabled() || position === null || this.props.nativeTabBar?.items[position]?.key !== event.nativeEvent.key) {
196
- return;
197
- }
198
- if (position >= 0 && position < this.pages().length && (position !== this.state.selectedPage || this.latestNativeTabTarget !== null)) {
189
+ if (position !== null && position >= 0 && position < this.pages().length && (position !== this.state.selectedPage || this.latestNativeTabTarget !== null)) {
199
190
  // Selection state lags an in-flight transition until native reports it.
200
191
  const currentPage = this.dispatchedNativeTabTarget ?? this.state.selectedPage;
201
- this.dispatchNativeTabPageCommand(position, this.props.nativeTabPressAnimationEnabled ?? Math.abs(position - currentPage) <= 1, event.nativeEvent.key);
192
+ this.dispatchNativeTabPageCommand(position, this.props.nativeTabPressAnimationEnabled ?? Math.abs(position - currentPage) <= 1);
202
193
  }
203
194
  this.props.onNativeTabPress?.(event);
204
195
  };
@@ -242,8 +233,6 @@ export class CollapsiblePagerView extends React.PureComponent {
242
233
  const deducedLayoutDirection = !layoutDirection || layoutDirection === "locale" ? I18nManager.isRTL ? "rtl" : "ltr" : layoutDirection;
243
234
  const nativeTabBarEnabled = (Platform.OS === "ios" || Platform.OS === "android") && !!nativeTabBar?.items.length;
244
235
  const nativeTabBarStyle = nativeTabBar?.style;
245
- const requestedTrailingReservedWidth = nativeTabBarStyle?.trailingReservedWidth;
246
- const nativeTabBarTrailingReservedWidth = typeof requestedTrailingReservedWidth === "number" && Number.isFinite(requestedTrailingReservedWidth) && requestedTrailingReservedWidth >= 0 ? requestedTrailingReservedWidth : 0;
247
236
  const nativeSubHeaderEnabled = (Platform.OS === "ios" || Platform.OS === "android") && !!nativeSubHeader?.items.length;
248
237
  const nativeSubHeaderStyle = nativeSubHeader?.style;
249
238
  const nativeSubHeaderConfig = nativeSubHeaderEnabled ? JSON.stringify({
@@ -274,7 +263,6 @@ export class CollapsiblePagerView extends React.PureComponent {
274
263
  onPageScrollStateChanged: this.onPageScrollStateChanged,
275
264
  nativeTabBarItems: nativeTabBarEnabled ? JSON.stringify(nativeTabBar.items) : undefined,
276
265
  nativeTabBarHeight: nativeTabBarEnabled ? nativeTabBarStyle?.height ?? 44 : undefined,
277
- nativeTabBarTrailingReservedWidth: nativeTabBarEnabled ? nativeTabBarTrailingReservedWidth : 0,
278
266
  nativeTabBarContentPaddingHorizontal: nativeTabBarEnabled ? nativeTabBarStyle?.contentPaddingHorizontal ?? 20 : undefined,
279
267
  nativeTabBarItemSpacing: nativeTabBarEnabled ? nativeTabBarStyle?.itemSpacing ?? 8 : undefined,
280
268
  nativeTabBarFontSize: nativeTabBarEnabled ? nativeTabBarStyle?.fontSize ?? 16 : undefined,
@@ -335,15 +335,6 @@ export class CollapsiblePagerView extends React.PureComponent {
335
335
  scrollToPageTop = options => {
336
336
  this.scrollCurrentPageToTop(0, options?.animated ?? true);
337
337
  };
338
- scrollHeaderBy = delta => {
339
- if (!Number.isFinite(delta)) return;
340
- const offset = Math.min(this.headerHeight, Math.max(0, this.sharedHeaderOffset + delta));
341
- this.scrollCurrentPageToTop(offset, false);
342
- };
343
- scrollHeaderToOffset = offset => {
344
- if (!Number.isFinite(offset)) return;
345
- this.scrollCurrentPageToTop(Math.min(this.headerHeight, Math.max(0, offset)), false);
346
- };
347
338
  scrollToStickyTop = options => {
348
339
  this.scrollCurrentPageToTop(this.headerHeight, options?.animated ?? true);
349
340
  };
@@ -395,13 +386,13 @@ export class CollapsiblePagerView extends React.PureComponent {
395
386
  this.pendingTopFrame = null;
396
387
  if (!element.isConnected || this.horizontalTouch || this.transitionTimer !== null || this.pageScrollElement(this.state.selectedPage) !== element || this.pages().map(pageKey)[this.state.selectedPage] !== key) return;
397
388
  this.pageOffsets.set(key, 0);
398
- this.sharedHeaderOffset = Math.min(this.headerHeight, Math.max(0, offset));
389
+ this.sharedHeaderOffset = offset;
399
390
  this.bodyAlignment = {
400
391
  element,
401
392
  key
402
393
  };
403
394
  scrollElementTo(element, {
404
- top: this.sharedHeaderOffset,
395
+ top: offset,
405
396
  behavior: animated ? "smooth" : "instant"
406
397
  });
407
398
  this.syncFallbackHeader();
@@ -67,7 +67,6 @@ export interface NativeProps extends ViewProps {
67
67
  retainedPages?: string;
68
68
  nativeTabBarItems?: string;
69
69
  nativeTabBarHeight?: Double;
70
- nativeTabBarTrailingReservedWidth?: WithDefault<Double, 0>;
71
70
  nativeTabBarContentPaddingHorizontal?: Double;
72
71
  nativeTabBarItemSpacing?: Double;
73
72
  nativeTabBarFontSize?: Double;
@@ -92,14 +91,6 @@ export interface NativeProps extends ViewProps {
92
91
  type CollapsiblePagerViewNativeType = HostComponent<NativeProps>;
93
92
 
94
93
  export interface NativeCommands {
95
- scrollHeaderToOffset: (
96
- viewRef: React.ElementRef<CollapsiblePagerViewNativeType>,
97
- offset: Double
98
- ) => void;
99
- scrollHeaderBy: (
100
- viewRef: React.ElementRef<CollapsiblePagerViewNativeType>,
101
- delta: Double
102
- ) => void;
103
94
  scrollToTop: (
104
95
  viewRef: React.ElementRef<CollapsiblePagerViewNativeType>,
105
96
  target: Int32,
@@ -121,8 +112,6 @@ export interface NativeCommands {
121
112
 
122
113
  export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
123
114
  supportedCommands: [
124
- "scrollHeaderToOffset",
125
- "scrollHeaderBy",
126
115
  "scrollToTop",
127
116
  "setPage",
128
117
  "setPageWithoutAnimation",
@@ -17,7 +17,6 @@ export type CollapsiblePagerNativeTabBarItem = Readonly<{
17
17
  }>;
18
18
  export type CollapsiblePagerNativeTabBarStyle = Readonly<{
19
19
  height?: number;
20
- trailingReservedWidth?: number;
21
20
  contentPaddingHorizontal?: number;
22
21
  itemSpacing?: number;
23
22
  fontSize?: number;
@@ -57,7 +56,7 @@ export type CollapsiblePagerNativeSubHeaderConfig = Readonly<{
57
56
  }>;
58
57
  export type CollapsiblePagerViewOnNativeTabPressEvent = ReactNative.NativeSyntheticEvent<OnNativeTabPressEventData>;
59
58
  export type CollapsiblePagerViewOnNativeSubHeaderPressEvent = ReactNative.NativeSyntheticEvent<OnNativeTabPressEventData>;
60
- export interface CollapsiblePagerViewProps extends Omit<NativeProps, "children" | "headerHeight" | "stickyHeaderHeight" | "topScrollEdgeEffect" | "topOverlayHeight" | "externalTopOverlayTag" | "pageKeys" | "retainedPages" | "nativeTabBarItems" | "nativeTabBarHeight" | "nativeTabBarTrailingReservedWidth" | "nativeTabBarContentPaddingHorizontal" | "nativeTabBarItemSpacing" | "nativeTabBarFontSize" | "nativeTabBarFontFamily" | "nativeTabBarBackgroundColor" | "nativeTabBarActiveTextColor" | "nativeTabBarInactiveTextColor" | "nativeTabBarIndicatorColor" | "nativeTabBarIndicatorHeight" | "nativeTabBarIndicatorBottom" | "nativeSubHeaderConfig" | "nativeSubHeaderSelectedBackgroundColor" | "onNativeTabPress" | "onNativeSubHeaderPress" | "layoutDirection"> {
59
+ export interface CollapsiblePagerViewProps extends Omit<NativeProps, "children" | "headerHeight" | "stickyHeaderHeight" | "topScrollEdgeEffect" | "topOverlayHeight" | "externalTopOverlayTag" | "pageKeys" | "retainedPages" | "nativeTabBarItems" | "nativeTabBarHeight" | "nativeTabBarContentPaddingHorizontal" | "nativeTabBarItemSpacing" | "nativeTabBarFontSize" | "nativeTabBarFontFamily" | "nativeTabBarBackgroundColor" | "nativeTabBarActiveTextColor" | "nativeTabBarInactiveTextColor" | "nativeTabBarIndicatorColor" | "nativeTabBarIndicatorHeight" | "nativeTabBarIndicatorBottom" | "nativeSubHeaderConfig" | "nativeSubHeaderSelectedBackgroundColor" | "onNativeTabPress" | "onNativeSubHeaderPress" | "layoutDirection"> {
61
60
  /** Collapsible content rendered above the sticky bar. */
62
61
  header: React.ReactNode;
63
62
  /** Bar which remains pinned after the header has collapsed. */
@@ -129,8 +128,6 @@ export declare class CollapsiblePagerView extends React.PureComponent<Collapsibl
129
128
  scrollToPageTop: (options?: Readonly<{
130
129
  animated?: boolean;
131
130
  }>) => void;
132
- scrollHeaderBy: (delta: number) => void;
133
- scrollHeaderToOffset: (offset: number) => void;
134
131
  scrollToStickyTop: (options?: Readonly<{
135
132
  animated?: boolean;
136
133
  }>) => void;
@@ -132,8 +132,6 @@ export declare class CollapsiblePagerView extends React.PureComponent<Collapsibl
132
132
  scrollToPageTop: (options?: Readonly<{
133
133
  animated?: boolean;
134
134
  }>) => void;
135
- scrollHeaderBy: (delta: number) => void;
136
- scrollHeaderToOffset: (offset: number) => void;
137
135
  scrollToStickyTop: (options?: Readonly<{
138
136
  animated?: boolean;
139
137
  }>) => void;
@@ -50,7 +50,6 @@ export interface NativeProps extends ViewProps {
50
50
  retainedPages?: string;
51
51
  nativeTabBarItems?: string;
52
52
  nativeTabBarHeight?: Double;
53
- nativeTabBarTrailingReservedWidth?: WithDefault<Double, 0>;
54
53
  nativeTabBarContentPaddingHorizontal?: Double;
55
54
  nativeTabBarItemSpacing?: Double;
56
55
  nativeTabBarFontSize?: Double;
@@ -73,8 +72,6 @@ export interface NativeProps extends ViewProps {
73
72
  }
74
73
  type CollapsiblePagerViewNativeType = HostComponent<NativeProps>;
75
74
  export interface NativeCommands {
76
- scrollHeaderToOffset: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, offset: Double) => void;
77
- scrollHeaderBy: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, delta: Double) => void;
78
75
  scrollToTop: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, target: Int32, animated: boolean) => void;
79
76
  setPage: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, selectedPage: Int32) => void;
80
77
  setPageWithoutAnimation: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, selectedPage: Int32) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-pager-view",
3
- "version": "6.7.0-alpha.354",
3
+ "version": "6.7.0-alpha.362",
4
4
  "description": "React Native wrapper for Android and iOS ViewPager",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",
@@ -73,7 +73,7 @@
73
73
  "typescript": "^5.9.2"
74
74
  },
75
75
  "peerDependencies": {
76
- "@onekeyfe/react-native-native-logger": "6.7.0-alpha.354",
76
+ "@onekeyfe/react-native-native-logger": "6.7.0-alpha.362",
77
77
  "react": "*",
78
78
  "react-native": "*"
79
79
  },
@@ -33,7 +33,6 @@ export type CollapsiblePagerNativeTabBarItem = Readonly<{
33
33
 
34
34
  export type CollapsiblePagerNativeTabBarStyle = Readonly<{
35
35
  height?: number;
36
- trailingReservedWidth?: number;
37
36
  contentPaddingHorizontal?: number;
38
37
  itemSpacing?: number;
39
38
  fontSize?: number;
@@ -95,7 +94,6 @@ export interface CollapsiblePagerViewProps
95
94
  | "retainedPages"
96
95
  | "nativeTabBarItems"
97
96
  | "nativeTabBarHeight"
98
- | "nativeTabBarTrailingReservedWidth"
99
97
  | "nativeTabBarContentPaddingHorizontal"
100
98
  | "nativeTabBarItemSpacing"
101
99
  | "nativeTabBarFontSize"
@@ -292,22 +290,6 @@ export class CollapsiblePagerView extends React.PureComponent<
292
290
  );
293
291
  };
294
292
 
295
- public scrollHeaderBy = (delta: number) => {
296
- if (this.nativeRef && Number.isFinite(delta))
297
- CollapsiblePagerViewNativeCommands.scrollHeaderBy(
298
- this.nativeRef,
299
- delta
300
- );
301
- };
302
-
303
- public scrollHeaderToOffset = (offset: number) => {
304
- if (this.nativeRef && Number.isFinite(offset))
305
- CollapsiblePagerViewNativeCommands.scrollHeaderToOffset(
306
- this.nativeRef,
307
- offset
308
- );
309
- };
310
-
311
293
  public scrollToStickyTop = (options?: Readonly<{ animated?: boolean }>) => {
312
294
  if (this.nativeRef)
313
295
  CollapsiblePagerViewNativeCommands.scrollToTop(
@@ -337,11 +319,7 @@ export class CollapsiblePagerView extends React.PureComponent<
337
319
  );
338
320
  }
339
321
 
340
- private dispatchNativeTabPageCommand(
341
- position: number,
342
- animated: boolean,
343
- expectedKey?: string
344
- ) {
322
+ private dispatchNativeTabPageCommand(position: number, animated: boolean) {
345
323
  const commandId = ++this.nativeTabCommandId;
346
324
  this.nativeTabCommandQueuedDuringScroll =
347
325
  this.nativePagerScrollState !== "idle";
@@ -354,13 +332,7 @@ export class CollapsiblePagerView extends React.PureComponent<
354
332
  }),
355
333
  () => {
356
334
  requestAnimationFrame(() => {
357
- if (
358
- commandId !== this.nativeTabCommandId ||
359
- !this.nativeRef ||
360
- (expectedKey !== undefined &&
361
- (!this.nativeTabBarEnabled() ||
362
- this.props.nativeTabBar?.items[position]?.key !== expectedKey))
363
- ) {
335
+ if (commandId !== this.nativeTabCommandId || !this.nativeRef) {
364
336
  return;
365
337
  }
366
338
  this.dispatchedNativeTabTarget = position;
@@ -486,13 +458,7 @@ export class CollapsiblePagerView extends React.PureComponent<
486
458
  ) => {
487
459
  const position = pageIndex(event.nativeEvent.position);
488
460
  if (
489
- !this.nativeTabBarEnabled() ||
490
- position === null ||
491
- this.props.nativeTabBar?.items[position]?.key !== event.nativeEvent.key
492
- ) {
493
- return;
494
- }
495
- if (
461
+ position !== null &&
496
462
  position >= 0 &&
497
463
  position < this.pages().length &&
498
464
  (position !== this.state.selectedPage ||
@@ -504,8 +470,7 @@ export class CollapsiblePagerView extends React.PureComponent<
504
470
  this.dispatchNativeTabPageCommand(
505
471
  position,
506
472
  this.props.nativeTabPressAnimationEnabled ??
507
- Math.abs(position - currentPage) <= 1,
508
- event.nativeEvent.key
473
+ Math.abs(position - currentPage) <= 1
509
474
  );
510
475
  }
511
476
  this.props.onNativeTabPress?.(event);
@@ -568,14 +533,6 @@ export class CollapsiblePagerView extends React.PureComponent<
568
533
  (Platform.OS === "ios" || Platform.OS === "android") &&
569
534
  !!nativeTabBar?.items.length;
570
535
  const nativeTabBarStyle = nativeTabBar?.style;
571
- const requestedTrailingReservedWidth =
572
- nativeTabBarStyle?.trailingReservedWidth;
573
- const nativeTabBarTrailingReservedWidth =
574
- typeof requestedTrailingReservedWidth === "number" &&
575
- Number.isFinite(requestedTrailingReservedWidth) &&
576
- requestedTrailingReservedWidth >= 0
577
- ? requestedTrailingReservedWidth
578
- : 0;
579
536
  const nativeSubHeaderEnabled =
580
537
  (Platform.OS === "ios" || Platform.OS === "android") &&
581
538
  !!nativeSubHeader?.items.length;
@@ -629,9 +586,6 @@ export class CollapsiblePagerView extends React.PureComponent<
629
586
  nativeTabBarHeight={
630
587
  nativeTabBarEnabled ? nativeTabBarStyle?.height ?? 44 : undefined
631
588
  }
632
- nativeTabBarTrailingReservedWidth={
633
- nativeTabBarEnabled ? nativeTabBarTrailingReservedWidth : 0
634
- }
635
589
  nativeTabBarContentPaddingHorizontal={
636
590
  nativeTabBarEnabled
637
591
  ? nativeTabBarStyle?.contentPaddingHorizontal ?? 20
@@ -580,23 +580,6 @@ export class CollapsiblePagerView extends React.PureComponent<
580
580
  this.scrollCurrentPageToTop(0, options?.animated ?? true);
581
581
  };
582
582
 
583
- public scrollHeaderBy = (delta: number) => {
584
- if (!Number.isFinite(delta)) return;
585
- const offset = Math.min(
586
- this.headerHeight,
587
- Math.max(0, this.sharedHeaderOffset + delta)
588
- );
589
- this.scrollCurrentPageToTop(offset, false);
590
- };
591
-
592
- public scrollHeaderToOffset = (offset: number) => {
593
- if (!Number.isFinite(offset)) return;
594
- this.scrollCurrentPageToTop(
595
- Math.min(this.headerHeight, Math.max(0, offset)),
596
- false
597
- );
598
- };
599
-
600
583
  public scrollToStickyTop = (options?: Readonly<{ animated?: boolean }>) => {
601
584
  this.scrollCurrentPageToTop(
602
585
  this.headerHeight,
@@ -691,10 +674,10 @@ export class CollapsiblePagerView extends React.PureComponent<
691
674
  )
692
675
  return;
693
676
  this.pageOffsets.set(key, 0);
694
- this.sharedHeaderOffset = Math.min(this.headerHeight, Math.max(0, offset));
677
+ this.sharedHeaderOffset = offset;
695
678
  this.bodyAlignment = { element, key };
696
679
  scrollElementTo(element, {
697
- top: this.sharedHeaderOffset,
680
+ top: offset,
698
681
  behavior: animated ? "smooth" : "instant",
699
682
  });
700
683
  this.syncFallbackHeader();
@@ -67,7 +67,6 @@ export interface NativeProps extends ViewProps {
67
67
  retainedPages?: string;
68
68
  nativeTabBarItems?: string;
69
69
  nativeTabBarHeight?: Double;
70
- nativeTabBarTrailingReservedWidth?: WithDefault<Double, 0>;
71
70
  nativeTabBarContentPaddingHorizontal?: Double;
72
71
  nativeTabBarItemSpacing?: Double;
73
72
  nativeTabBarFontSize?: Double;
@@ -92,14 +91,6 @@ export interface NativeProps extends ViewProps {
92
91
  type CollapsiblePagerViewNativeType = HostComponent<NativeProps>;
93
92
 
94
93
  export interface NativeCommands {
95
- scrollHeaderToOffset: (
96
- viewRef: React.ElementRef<CollapsiblePagerViewNativeType>,
97
- offset: Double
98
- ) => void;
99
- scrollHeaderBy: (
100
- viewRef: React.ElementRef<CollapsiblePagerViewNativeType>,
101
- delta: Double
102
- ) => void;
103
94
  scrollToTop: (
104
95
  viewRef: React.ElementRef<CollapsiblePagerViewNativeType>,
105
96
  target: Int32,
@@ -121,8 +112,6 @@ export interface NativeCommands {
121
112
 
122
113
  export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
123
114
  supportedCommands: [
124
- "scrollHeaderToOffset",
125
- "scrollHeaderBy",
126
115
  "scrollToTop",
127
116
  "setPage",
128
117
  "setPageWithoutAnimation",
@@ -1,7 +0,0 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
- <application android:supportsRtl="true">
3
- <activity
4
- android:name="com.reactnativepagerview.CollapsiblePagerHitTestActivity"
5
- android:exported="false" />
6
- </application>
7
- </manifest>
@@ -1,136 +0,0 @@
1
- package com.reactnativepagerview
2
-
3
- import android.app.Activity
4
- import android.content.Intent
5
- import android.content.pm.ApplicationInfo
6
- import android.graphics.Bitmap
7
- import android.graphics.Canvas
8
- import android.graphics.Color
9
- import android.test.InstrumentationTestCase
10
- import android.view.MotionEvent
11
- import android.view.View
12
- import android.view.ViewGroup
13
- import android.widget.HorizontalScrollView
14
- import android.widget.TextView
15
- import com.facebook.react.uimanager.PointerEvents
16
- import com.facebook.react.views.view.ReactViewGroup
17
- import java.util.concurrent.atomic.AtomicInteger
18
- import kotlin.math.roundToInt
19
-
20
- class CollapsiblePagerHitTestActivity : Activity()
21
-
22
- class CollapsiblePagerTabReservationTest : InstrumentationTestCase() {
23
- fun testTabViewportToolbarHitAndResetInBothDirections() {
24
- val context = instrumentation.targetContext
25
- assertTrue(context.applicationInfo.flags and ApplicationInfo.FLAG_SUPPORTS_RTL != 0)
26
- val activity = instrumentation.startActivitySync(
27
- Intent(context, CollapsiblePagerHitTestActivity::class.java)
28
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
29
- ) as CollapsiblePagerHitTestActivity
30
- val toolbarPresses = AtomicInteger()
31
- val tabPresses = AtomicInteger()
32
- try {
33
- instrumentation.runOnMainSync {
34
- val density = context.resources.displayMetrics.density
35
- fun dp(value: Int) = (value * density).roundToInt()
36
- val width = dp(320)
37
- val height = dp(500)
38
- val reserved = dp(64)
39
- val tabHeight = dp(44)
40
- val host = CollapsiblePagerHost(context)
41
- activity.setContentView(host)
42
- assertTrue(host.isAttachedToWindow)
43
- val sticky = ReactViewGroup(context).apply { pointerEvents = PointerEvents.BOX_NONE }
44
- val fade = View(context).apply { setBackgroundColor(Color.RED) }
45
- val toolbar = View(context).apply {
46
- setBackgroundColor(Color.BLUE)
47
- isClickable = true
48
- }
49
- sticky.addView(fade)
50
- sticky.addView(toolbar)
51
- host.addReactChild(View(context), 0)
52
- host.addReactChild(sticky, 1)
53
- host.headerHeightPx = 0
54
- host.stickyHeaderHeightPx = tabHeight
55
- host.nativeTabBarBackgroundColor = Color.GREEN
56
- host.applyNativeHeaderStyle()
57
- host.updateNativeTabBarItems(
58
- """[{"key":"a","title":"Overview"},{"key":"b","title":"Ethereum"},""" +
59
- """{"key":"c","title":"BNB Chain"},{"key":"d","title":"Polygon"}]""",
60
- )
61
- val tab = (0 until host.childCount).map(host::getChildAt)
62
- .first { it.javaClass.simpleName == "CollapsiblePagerNativeTabBarView" } as ViewGroup
63
- val row = tab.getChildAt(0) as HorizontalScrollView
64
- toolbar.setOnClickListener { toolbarPresses.incrementAndGet() }
65
- host.onNativeTabPress = { _, _ -> tabPresses.incrementAndGet() }
66
-
67
- fun layout() {
68
- host.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
69
- View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))
70
- host.layout(0, 0, width, height)
71
- }
72
- fun tap(x: Int) {
73
- val time = android.os.SystemClock.uptimeMillis()
74
- val down = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN,
75
- x.toFloat(), (tabHeight / 2).toFloat(), 0)
76
- val up = MotionEvent.obtain(time, time + 20, MotionEvent.ACTION_UP,
77
- x.toFloat(), (tabHeight / 2).toFloat(), 0)
78
- host.dispatchTouchEvent(down)
79
- host.dispatchTouchEvent(up)
80
- down.recycle()
81
- up.recycle()
82
- }
83
- fun colorAt(x: Int): Int {
84
- val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
85
- host.draw(Canvas(bitmap))
86
- val color = bitmap.getPixel(x, tabHeight / 2)
87
- bitmap.recycle()
88
- return color
89
- }
90
-
91
- host.nativeTabBarTrailingReservedWidth = 64.0
92
- layout()
93
- fade.layout(width - reserved - dp(20), 0, width - reserved, tabHeight)
94
- toolbar.layout(width - reserved, 0, width, tabHeight)
95
- assertEquals(width - reserved, tab.width)
96
- assertEquals(0, tab.left)
97
- assertEquals(width, sticky.width)
98
- assertEquals(Color.RED, colorAt(width - reserved - dp(10)))
99
- tap(width - dp(10))
100
- tap(dp(28))
101
- host.updateNativeTabProgress(3, 0f)
102
- val content = row.getChildAt(0) as ViewGroup
103
- val lastTab = (0 until content.childCount).map(content::getChildAt)
104
- .filterIsInstance<TextView>().last()
105
- assertTrue(lastTab.left >= row.scrollX - dp(1))
106
- assertTrue(lastTab.right <= row.scrollX + row.width + dp(1))
107
-
108
- host.setPagerLayoutDirection(View.LAYOUT_DIRECTION_RTL)
109
- host.nativeTabBarTrailingReservedWidth = 64.0
110
- layout()
111
- assertEquals(View.LAYOUT_DIRECTION_RTL, tab.layoutDirection)
112
- toolbar.layout(0, 0, reserved, tabHeight)
113
- fade.layout(reserved, 0, reserved + dp(20), tabHeight)
114
- assertEquals(reserved, tab.left)
115
- assertEquals(width, tab.right)
116
- assertEquals(Color.RED, colorAt(reserved + dp(10)))
117
- tap(dp(10))
118
-
119
- host.nativeTabBarTrailingReservedWidth = 500.0
120
- layout()
121
- assertEquals(0, tab.width)
122
- host.nativeTabBarTrailingReservedWidth = 0.0
123
- layout()
124
- assertEquals(0, tab.left)
125
- assertEquals(width, tab.width)
126
- assertTrue(host.indexOfChild(sticky) < host.indexOfChild(tab))
127
- }
128
- instrumentation.waitForIdleSync()
129
- assertEquals(2, toolbarPresses.get())
130
- assertEquals(1, tabPresses.get())
131
- } finally {
132
- instrumentation.runOnMainSync { activity.finish() }
133
- instrumentation.waitForIdleSync()
134
- }
135
- }
136
- }