@onekeyfe/react-native-native-overlay 6.7.0-alpha.344 → 6.7.0-alpha.354

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.
@@ -69,8 +69,9 @@ internal sealed class OverlayMotion {
69
69
  }
70
70
 
71
71
  /**
72
- * Analytic step response of `m·x'' + c·x' + k·x = 0`, identical to
73
- * Reanimated and `springProgressAt` in JS. Driving one ValueAnimator with it
72
+ * Analytic step response of `m·x'' + c·x' + k·x = 0`, with overdamped
73
+ * responses capped at critical damping to match UIKit's sheet response.
74
+ * Driving one ValueAnimator with it
74
75
  * keeps alpha, translation, scale and backdrop on the same clock; androidx
75
76
  * SpringAnimation would need one animation per property.
76
77
  */
@@ -89,19 +90,13 @@ internal class SpringInterpolator(private val spring: OverlayMotion.Spring) : Ti
89
90
 
90
91
  fun progressAt(spring: OverlayMotion.Spring, t: Double): Double {
91
92
  val w0 = sqrt(spring.stiffness / spring.mass)
92
- val zeta = spring.damping / (2 * sqrt(spring.stiffness * spring.mass))
93
+ val zeta = minOf(1.0, spring.damping / (2 * sqrt(spring.stiffness * spring.mass)))
93
94
  return when {
94
95
  zeta < 1 -> {
95
96
  val wd = w0 * sqrt(1 - zeta * zeta)
96
97
  1 - exp(-zeta * w0 * t) * (cos(wd * t) + (zeta * w0 / wd) * sin(wd * t))
97
98
  }
98
- zeta == 1.0 -> 1 - exp(-w0 * t) * (1 + w0 * t)
99
- else -> {
100
- val root = sqrt(zeta * zeta - 1)
101
- val r1 = -w0 * (zeta - root)
102
- val r2 = -w0 * (zeta + root)
103
- 1 - (r2 * exp(r1 * t) - r1 * exp(r2 * t)) / (r2 - r1)
104
- }
99
+ else -> 1 - exp(-w0 * t) * (1 + w0 * t)
105
100
  }
106
101
  }
107
102
 
@@ -60,7 +60,12 @@ internal class NativeOverlayEntryRootView(
60
60
  * below), non-blocking ones let it continue past the empty area.
61
61
  */
62
62
  override val pointerEvents: PointerEvents
63
- get() = if (blocking && isShownForInput) PointerEvents.AUTO else PointerEvents.BOX_NONE
63
+ get() = when {
64
+ blocking && isClosing -> PointerEvents.BOX_ONLY
65
+ isClosing -> PointerEvents.NONE
66
+ blocking && isShownForInput -> PointerEvents.AUTO
67
+ else -> PointerEvents.BOX_NONE
68
+ }
64
69
  var stackOrder = 0
65
70
  var levelOrder = 0
66
71
  /** Page scope: the owning page, used by the page host to hide the entry. */
@@ -74,6 +79,19 @@ internal class NativeOverlayEntryRootView(
74
79
  var dismissOnBackPress = true
75
80
  var dismissOnBackdropPress = false
76
81
  var isShownForInput = false
82
+ private var contentAccessibilityBeforeClose: Int? = null
83
+ var isClosing = false
84
+ set(value) {
85
+ if (field == value) return
86
+ field = value
87
+ if (value) {
88
+ contentAccessibilityBeforeClose = contentView.importantForAccessibility
89
+ contentView.importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
90
+ } else {
91
+ contentAccessibilityBeforeClose?.let { contentView.importantForAccessibility = it }
92
+ contentAccessibilityBeforeClose = null
93
+ }
94
+ }
77
95
  var onRequestDismiss: ((String) -> Unit)? = null
78
96
 
79
97
  val backdropView = View(reactContext).apply { alpha = 0f }
@@ -90,12 +108,13 @@ internal class NativeOverlayEntryRootView(
90
108
  /** `sheet` presentation: the draggable surface; otherwise null. */
91
109
  var sheetView: FrameLayout? = null
92
110
  private set
111
+ private var sheetAnimationView: CoordinatorLayout? = null
93
112
  private var sheetBehavior: BottomSheetBehavior<FrameLayout>? = null
94
113
  private var handleView: View? = null
95
114
  var onSheetHiddenByPan: (() -> Unit)? = null
96
115
 
97
116
  /** The view that enter / exit animations move. */
98
- val animatedView: View get() = sheetView ?: contentView
117
+ val animatedView: View get() = sheetAnimationView ?: contentView
99
118
 
100
119
  private val touchDispatcher = JSTouchDispatcher(this)
101
120
  private val pointerDispatcher = if (ReactFeatureFlags.dispatchPointerEvents) {
@@ -220,6 +239,7 @@ internal class NativeOverlayEntryRootView(
220
239
  },
221
240
  )
222
241
  sheetView = sheet
242
+ sheetAnimationView = coordinator
223
243
  sheetBehavior = behavior
224
244
  handleView = handle
225
245
  return sheet
@@ -350,6 +370,7 @@ internal class NativeOverlayEntryRootView(
350
370
  }
351
371
 
352
372
  override fun dispatchTouchEvent(event: MotionEvent): Boolean {
373
+ if (isClosing) return blocking
353
374
  if (!isShownForInput) return false
354
375
  if (event.actionMasked == MotionEvent.ACTION_DOWN) {
355
376
  // Non-blocking overlays (toasts) only take touches that land on content.
@@ -50,7 +50,7 @@ internal class NativeOverlayHost private constructor(
50
50
  private val backCallback = object : OnBackPressedCallback(false) {
51
51
  override fun handleOnBackPressed() {
52
52
  topBackEntry()?.let { entry ->
53
- if (entry.dismissOnBackPress) entry.onRequestDismiss?.invoke("back")
53
+ if (!entry.isClosing && entry.dismissOnBackPress) entry.onRequestDismiss?.invoke("back")
54
54
  }
55
55
  }
56
56
  }
@@ -68,7 +68,7 @@ internal class NativeOverlayHost private constructor(
68
68
  */
69
69
  fun handleBackKey(event: KeyEvent): Boolean {
70
70
  val entry = topBackEntry() ?: return false
71
- if (event.action == KeyEvent.ACTION_UP && !event.isCanceled && entry.dismissOnBackPress) {
71
+ if (event.action == KeyEvent.ACTION_UP && !event.isCanceled && !entry.isClosing && entry.dismissOnBackPress) {
72
72
  entry.onRequestDismiss?.invoke("back")
73
73
  }
74
74
  return true
@@ -135,10 +135,12 @@ internal class NativeOverlayHost private constructor(
135
135
 
136
136
  /** Global overlays win; otherwise the topmost visible page overlay. */
137
137
  private fun topBackEntry(): NativeOverlayEntryRootView? =
138
- orderedEntries().lastOrNull { (it.blocking || it.dismissOnBackPress) && it.isShownForInput }
138
+ orderedEntries().lastOrNull {
139
+ (it.blocking || it.dismissOnBackPress) && it.isShownForInput && (!it.isClosing || it.blocking)
140
+ }
139
141
  ?: shownPageHosts()
140
142
  .flatMap { it.shownEntries() }
141
- .lastOrNull { it.blocking || it.dismissOnBackPress }
143
+ .lastOrNull { (it.blocking || it.dismissOnBackPress) && (!it.isClosing || it.blocking) }
142
144
 
143
145
  private fun shownPageHosts(): List<NativeOverlayPageHostView> =
144
146
  NativeOverlayPageHostView.allHosts().filter {
@@ -200,6 +200,7 @@ class NativeOverlayView(
200
200
  }
201
201
  cancelAnimators()
202
202
  phase = Phase.ENTERING
203
+ target.isClosing = false
203
204
  target.isShownForInput = true
204
205
  notifyHostEntriesChanged()
205
206
  if (blocking) {
@@ -226,7 +227,7 @@ class NativeOverlayView(
226
227
  val target = entry ?: return finishDismiss("programmatic")
227
228
  cancelAnimators()
228
229
  phase = Phase.EXITING
229
- target.isShownForInput = false
230
+ target.isClosing = true
230
231
  notifyHostEntriesChanged()
231
232
  val transition = config.exit.reduceMotionAdjusted(isReduceMotionEnabled())
232
233
  if (transition.kind == "none") return finishDismiss("programmatic")
@@ -241,6 +242,7 @@ class NativeOverlayView(
241
242
  phase = Phase.HIDDEN
242
243
  entry?.let { target ->
243
244
  target.isShownForInput = false
245
+ target.isClosing = false
244
246
  stageContent()
245
247
  host?.detach(target)
246
248
  pageHost?.detach(target)
@@ -1,11 +1,15 @@
1
1
  package com.onekey.nativeoverlay
2
2
 
3
3
  import android.view.KeyEvent
4
+ import android.view.MotionEvent
4
5
  import android.view.View
5
6
  import android.widget.FrameLayout
6
7
  import androidx.activity.ComponentActivity
7
8
  import com.facebook.react.bridge.BridgeReactContext
9
+ import com.facebook.react.uimanager.PointerEvents
8
10
  import com.facebook.react.uimanager.ThemedReactContext
11
+ import com.facebook.react.uimanager.TouchTargetHelper
12
+ import com.facebook.react.views.view.ReactViewGroup
9
13
  import java.lang.ref.WeakReference
10
14
  import java.util.WeakHashMap
11
15
  import org.junit.Assert.assertEquals
@@ -124,6 +128,140 @@ class NativeOverlayBackTest {
124
128
  pageHost.invalidateHost()
125
129
  }
126
130
 
131
+ @Test
132
+ fun closingGlobalBlockerKeepsTouchBackAndAccessibilityOwnership() {
133
+ var pageTouches = 0
134
+ val page = View(activity).apply {
135
+ setOnTouchListener { _, _ ->
136
+ pageTouches += 1
137
+ true
138
+ }
139
+ }
140
+ activity.setContentView(page)
141
+ val lower = entry("lower", true, true)
142
+ val top = entry("top", true, true, 1)
143
+ host.attach(lower, NativeOverlayLevel.MODAL)
144
+ host.attach(top, NativeOverlayLevel.MODAL)
145
+ top.isClosing = true
146
+ host.onEntriesChanged()
147
+
148
+ assertEquals(PointerEvents.BOX_ONLY, top.pointerEvents)
149
+ assertEquals(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, top.contentView.importantForAccessibility)
150
+ assertEquals(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, page.importantForAccessibility)
151
+ assertEquals(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, lower.importantForAccessibility)
152
+ assertTrue(back())
153
+ assertEquals(emptyList<String>(), dismissals)
154
+
155
+ val content = activity.findViewById<FrameLayout>(android.R.id.content)
156
+ val size = View.MeasureSpec.makeMeasureSpec(300, View.MeasureSpec.EXACTLY)
157
+ content.measure(size, size)
158
+ content.layout(0, 0, 300, 300)
159
+ val down = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 150f, 150f, 0)
160
+ try {
161
+ assertTrue(content.dispatchTouchEvent(down))
162
+ assertEquals(0, pageTouches)
163
+ } finally {
164
+ down.recycle()
165
+ }
166
+
167
+ top.isClosing = false
168
+ host.onEntriesChanged()
169
+ assertEquals(PointerEvents.AUTO, top.pointerEvents)
170
+ assertEquals(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, top.contentView.importantForAccessibility)
171
+ assertTrue(back())
172
+ assertEquals(listOf("top:back"), dismissals)
173
+ }
174
+
175
+ @Test
176
+ fun closingPageBlockerStillTakesBack() {
177
+ val pageHost = NativeOverlayPageHostView(context).apply { hostKey = "closing-page" }
178
+ activity.setContentView(pageHost)
179
+ val top = entry("page", true, true)
180
+ pageHost.attach(top)
181
+ top.isClosing = true
182
+ host.onEntriesChanged()
183
+
184
+ assertEquals(PointerEvents.BOX_ONLY, top.pointerEvents)
185
+ assertTrue(back())
186
+ assertEquals(emptyList<String>(), dismissals)
187
+ top.isClosing = false
188
+ host.onEntriesChanged()
189
+ assertEquals(PointerEvents.AUTO, top.pointerEvents)
190
+ assertTrue(back())
191
+ assertEquals(listOf("page:back"), dismissals)
192
+ pageHost.invalidateHost()
193
+ }
194
+
195
+ @Test
196
+ fun pageEntryHitTestingKeepsClosingContentAndLowerPageSeparate() {
197
+ val root = ReactViewGroup(context).apply { id = 101 }
198
+ val below = ReactViewGroup(context).apply { id = 102 }
199
+ val child = ReactViewGroup(context).apply { id = 103 }
200
+ val pageHost = NativeOverlayPageHostView(context).apply { hostKey = "page-hit-test" }
201
+ root.addView(below)
202
+ root.addView(pageHost)
203
+ activity.setContentView(root)
204
+ val overlay = entry("page", true, true)
205
+ overlay.contentView.addView(child)
206
+ pageHost.attach(overlay)
207
+
208
+ val size = View.MeasureSpec.makeMeasureSpec(300, View.MeasureSpec.EXACTLY)
209
+ root.measure(size, size)
210
+ root.layout(0, 0, 300, 300)
211
+ below.layout(0, 0, 300, 300)
212
+ pageHost.layout(0, 0, 300, 300)
213
+ child.layout(0, 0, 300, 300)
214
+
215
+ fun target() = TouchTargetHelper.findTargetTagForTouch(150f, 150f, root)
216
+ assertEquals(child.id, target())
217
+ overlay.isClosing = true
218
+ assertEquals(root.id, target())
219
+ overlay.updateBlocking(false)
220
+ assertEquals(below.id, target())
221
+ overlay.updateBlocking(true)
222
+ overlay.isClosing = false
223
+ assertEquals(child.id, target())
224
+ pageHost.invalidateHost()
225
+ }
226
+
227
+ @Test
228
+ fun reopeningCoveredClosingEntryRestoresItsContentAccessibility() {
229
+ activity.setContentView(View(activity))
230
+ val first = entry("first", true, true)
231
+ val second = entry("second", true, true)
232
+ host.attach(first, NativeOverlayLevel.MODAL)
233
+ first.isClosing = true
234
+ host.onEntriesChanged()
235
+ host.attach(second, NativeOverlayLevel.SECURE)
236
+
237
+ first.isClosing = false
238
+ host.onEntriesChanged()
239
+ assertEquals(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, first.importantForAccessibility)
240
+ host.detach(second)
241
+ assertEquals(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, first.importantForAccessibility)
242
+ assertEquals(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, first.contentView.importantForAccessibility)
243
+ }
244
+
245
+ @Test
246
+ fun closingNonBlockingEntryPassesTouchAndBackToLowerBlocker() {
247
+ val lower = entry("lower", true, true)
248
+ val toast = entry("toast", false, true)
249
+ host.attach(lower, NativeOverlayLevel.MODAL)
250
+ host.attach(toast, NativeOverlayLevel.TOAST)
251
+ toast.isClosing = true
252
+ host.onEntriesChanged()
253
+
254
+ assertEquals(PointerEvents.NONE, toast.pointerEvents)
255
+ val down = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 150f, 150f, 0)
256
+ try {
257
+ assertFalse(toast.dispatchTouchEvent(down))
258
+ } finally {
259
+ down.recycle()
260
+ }
261
+ assertTrue(back())
262
+ assertEquals(listOf("lower:back"), dismissals)
263
+ }
264
+
127
265
  @Test
128
266
  fun hostCacheReusesLiveHostAndSeparatesActivities() {
129
267
  assertSame(host, NativeOverlayHost.of(activity))
@@ -0,0 +1,101 @@
1
+ package com.onekey.nativeoverlay
2
+
3
+ import android.graphics.Color
4
+ import android.os.Looper
5
+ import android.view.View
6
+ import androidx.activity.ComponentActivity
7
+ import androidx.coordinatorlayout.widget.CoordinatorLayout
8
+ import com.facebook.react.bridge.BridgeReactContext
9
+ import com.facebook.react.uimanager.ThemedReactContext
10
+ import com.google.android.material.bottomsheet.BottomSheetBehavior
11
+ import org.junit.Assert.assertEquals
12
+ import org.junit.Assert.assertFalse
13
+ import org.junit.Assert.assertNotSame
14
+ import org.junit.Assert.assertSame
15
+ import org.junit.Assert.assertTrue
16
+ import org.junit.Test
17
+ import org.junit.runner.RunWith
18
+ import org.robolectric.Robolectric
19
+ import org.robolectric.RobolectricTestRunner
20
+ import org.robolectric.Shadows
21
+ import org.robolectric.annotation.Config
22
+ import java.time.Duration
23
+
24
+ @RunWith(RobolectricTestRunner::class)
25
+ @Config(sdk = [34])
26
+ class NativeOverlayMotionTest {
27
+ @Test
28
+ fun sheetAnimationMovesCoordinatorWithoutTakingMaterialsSheetTranslation() {
29
+ val activity = Robolectric.buildActivity(ComponentActivity::class.java).setup().get()
30
+ val reactContext = BridgeReactContext(activity)
31
+ reactContext.onHostResume(activity)
32
+ val context = ThemedReactContext(reactContext, activity, "OverlayMotionTest", 1)
33
+ val entry = NativeOverlayEntryRootView(context)
34
+ assertSame(entry.contentView, entry.animatedView)
35
+
36
+ entry.configureSheet(500, 16f, Color.WHITE, showHandle = true, draggable = true)
37
+ val sheet = entry.sheetView!!
38
+ val animated = entry.animatedView
39
+ assertTrue(animated is CoordinatorLayout)
40
+ assertSame(animated, sheet.parent)
41
+ assertNotSame(sheet, animated)
42
+ assertTrue((sheet.layoutParams as CoordinatorLayout.LayoutParams).behavior is BottomSheetBehavior<*>)
43
+
44
+ animated.translationY = 42f
45
+ assertEquals(0f, sheet.translationY)
46
+ sheet.translationY = 7f
47
+ assertEquals(42f, animated.translationY)
48
+ assertEquals(7f, sheet.translationY)
49
+ }
50
+
51
+ @Test
52
+ fun quickMatchesMeasuredCriticalResponseWithoutChangingUnderdampedResponse() {
53
+ val quick = OverlayMotion.QUICK
54
+ assertEquals(292L, quick.durationMs())
55
+ assertEquals(0.8238140348, SpringInterpolator.progressAt(quick, 0.1), 0.000001)
56
+ assertEquals(0.9990003945, SpringInterpolator.progressAt(quick, 0.292), 0.000001)
57
+
58
+ val underdamped = OverlayMotion.Spring(1.0, 100.0, 5.0)
59
+ assertEquals(0.3929451508, SpringInterpolator.progressAt(underdamped, 0.1), 0.000001)
60
+ val timing = OverlayMotion.Timing(250L, 0.25f, 0.1f, 0.25f, 1f)
61
+ assertEquals(250L, timing.durationMs())
62
+ }
63
+
64
+ @Test
65
+ fun reopeningTheSameEntryDuringExitRetargetsItsAnimation() {
66
+ val activity = Robolectric.buildActivity(ComponentActivity::class.java).setup().visible().get()
67
+ val reactContext = BridgeReactContext(activity)
68
+ reactContext.onHostResume(activity)
69
+ val context = ThemedReactContext(reactContext, activity, "OverlayMotionTest", 1)
70
+ val overlay = NativeOverlayView(context).apply {
71
+ presentation = "sheet"
72
+ sheetHeight = 200.0
73
+ addReactChild(View(context))
74
+ }
75
+ activity.setContentView(overlay)
76
+ var dismissals = 0
77
+ overlay.onDismissed = { dismissals++ }
78
+ overlay.visible = true
79
+ overlay.commitConfiguration()
80
+
81
+ val entryField = NativeOverlayView::class.java.getDeclaredField("entry").apply { isAccessible = true }
82
+ val original = entryField.get(overlay) as NativeOverlayEntryRootView
83
+ overlay.visible = false
84
+ overlay.commitConfiguration()
85
+ assertTrue(original.isClosing)
86
+
87
+ overlay.visible = true
88
+ overlay.commitConfiguration()
89
+ assertSame(original, entryField.get(overlay))
90
+ assertTrue(original.isShownForInput)
91
+ assertFalse(original.isClosing)
92
+ assertEquals(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, original.contentView.importantForAccessibility)
93
+ Shadows.shadowOf(Looper.getMainLooper()).idleFor(Duration.ofMillis(350))
94
+ assertEquals(0, dismissals)
95
+
96
+ overlay.visible = false
97
+ overlay.commitConfiguration()
98
+ Shadows.shadowOf(Looper.getMainLooper()).idleFor(Duration.ofMillis(350))
99
+ assertEquals(1, dismissals)
100
+ }
101
+ }
package/docs/SPEC.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # react-native-native-overlay SPEC
2
2
 
3
3
  Status: Implemented through the P5 caller migration in app-monorepo. Android
4
- and Web runtime checks passed there; iOS runtime, physical-device and iPad
5
- acceptance remain unverified. Packaging in app-modules is validated separately.
4
+ and Web runtime checks passed there; focused iOS 26.5 simulator and Android 16
5
+ emulator checks are recorded below. Broader iOS runtime, physical-device and
6
+ iPad acceptance remain unverified. Packaging in app-modules is validated
7
+ separately.
6
8
 
7
9
  The implementation is migrated from app-monorepo commit
8
10
  `376633bd9f1c410dbccb01fb6a0492be9a5c3f38`, package
@@ -39,13 +41,15 @@ attribution in source comments.
39
41
 
40
42
  ### Release boundary
41
43
 
42
- The earlier `3.0.162-alpha.272` release remains immutable. This branch now
43
- includes the current main SDK baseline, with all publishable workspaces aligned
44
- before release. Use `package-publish` with `npm_dist_tag=next` and an empty
45
- `only_workspace` to generate a fresh `6.7.0-alpha.N` preview, publish every
46
- workspace, and verify every registry version. Preserve `latest` and keep both
47
- repository PRs open. App consumers pin the verified exact preview version and
48
- refresh dependency and native locks before installed-version acceptance.
44
+ The earlier `3.0.162-alpha.272` release remains immutable. This branch is
45
+ prepared at `6.7.0-9`: the repository's release validator requires all 43
46
+ publishable workspace manifests and exact internal pins to use the same
47
+ version. Publish only `@onekeyfe/react-native-native-overlay` through the
48
+ manual `package-publish` workflow with `npm_dist_tag=latest` and
49
+ `only_workspace=@onekeyfe/react-native-native-overlay`; verify that exact
50
+ registry tarball before the App pins it and refreshes its dependency and native
51
+ locks. The other workspace version changes are release metadata, not a request
52
+ to publish their packages in this run. Leave the source PR open for review.
49
53
 
50
54
  ## 5.1 Purpose, scope, and non-goals
51
55
 
@@ -168,8 +172,20 @@ visible)`, `removePage(ownerKey)`.
168
172
  - `motion` is a preset name or a concrete `{ type: 'spring', spring: { mass, stiffness, damping, overshootClamping } }`
169
173
  or `{ type: 'timing', timing: { durationMs, easing: cubicBezier } }`.
170
174
  - Presets are resolved per platform:
171
- - Native mirrors the Reanimated driver: `quick` is a spring with m 0.1,
172
- k 100, c 20 (damping ratio ≈ 3.16).
175
+ - Native `quick` supplies spring m 0.1, k 100, c 20. The iOS sheet's
176
+ measured position and dimming response use the critically damped curve
177
+ at angular frequency `sqrt(k/m)`; UIKit reports an animator duration near
178
+ 292 ms. Android caps an overdamped spring's effective damping ratio at 1
179
+ while preserving m and k; underdamped and timing motions retain their
180
+ existing response. This rule
181
+ also applies to custom overdamped springs as a platform compatibility
182
+ inference; only the default `quick` sheet path has been measured on iOS.
183
+ Android's implementation is covered by native tests. In the consumer
184
+ shells, the default `quick` Dialog and ActionList sheet enter/exit motion
185
+ and dimming were compared over three cycles per direction on Android 16
186
+ and iOS 26.5; their normalized visible curves met the recorded parity
187
+ tolerance. This does not verify custom springs, exact cross-device
188
+ rectangles, or callback timing.
173
189
  - Web mirrors the CSS driver: `quick` is 150 ms `cubic-bezier(.25,.1,.25,1)`.
174
190
  - Converting native springs for web is opt-in (`springToCssLinear`), because
175
191
  it would slow web overlays down 4–9×.
@@ -183,6 +199,13 @@ visible)`, `removePage(ownerKey)`.
183
199
  | fullscreen | fade, `quick` | none |
184
200
  | anchored | scale 0.95 + fade from the resolved anchor placement, `popoverQuick` | fade |
185
201
 
202
+ - **Android sheet transform ownership (Implemented; Android 16 consumer-shell runtime verified):**
203
+ the overlay's enter/exit transform belongs to the sheet's CoordinatorLayout
204
+ parent. Material's IME insets callback owns the child sheet's `translationY`,
205
+ and the keyboard layer owns the IME avoidance lift. These transforms compose
206
+ while the child sheet remains the BottomSheetBehavior drag target. The
207
+ overlay must not animate the child sheet's `translationY` during IME motion.
208
+
186
209
  - Special cases the callers set explicitly:
187
210
  - App lock: `enter: none`, `exit: fade lockFade`. The web tamper check reloads
188
211
  the page if the lock fades in from opacity 0.
@@ -216,6 +239,22 @@ request ─► queued ─► active ─► (enter anim) presented
216
239
  its current value. It does not restart.
217
240
  - `onRemoved` fires exactly once per entry, even when a finalize and the
218
241
  timeout race.
242
+ - **Android exit ownership (Implemented; blocking page/global touch and global
243
+ Back runtime verified on Android 16; accessibility native-test verified,
244
+ TalkBack device verification pending):** A
245
+ blocking entry keeps ownership of touch, Back, and accessibility isolation
246
+ until its exit animation completes and the native host detaches it. During
247
+ exit, the entry consumes touches and Back without forwarding them to its
248
+ closing React content or requesting a
249
+ second dismissal. Its content is hidden from TalkBack while lower content
250
+ remains isolated. A reopen during exit restores content input and
251
+ accessibility without briefly exposing the page below. Closing non-blocking
252
+ entries pass through touch and Back to the entry below.
253
+ Consumer-shell recordings verify that page and global blocking entries keep
254
+ lower touch targets inert during their visible exit, then release them, and
255
+ that Back does not navigate beneath a closing global entry. Native tests
256
+ cover closing non-blocking entries and same-entry reopen; those paths and
257
+ TalkBack reading during exit have not been verified on device.
219
258
  - Page entries:
220
259
  - They are suspended (hidden but kept) while the owner page is covered or
221
260
  detached.
@@ -460,6 +499,8 @@ P2 sheet notes:
460
499
  flick dismisses, never from scrolled content). Reopening kept content after
461
500
  a pan dismissal clears the sheet transform, transition and backdrop opacity;
462
501
  the next dismissal follows the normal exit path and restores focus.
502
+ Cancelling a tracked pointer resets its drag offset, transition and backdrop
503
+ without dismissing the sheet or swallowing a later click.
463
504
  - A fitted sheet measures its content with `onLayout` before presenting.
464
505
  Hidden content stays mounted inside the zero-width host (clipped on iOS,
465
506
  INVISIBLE on Android), and the host height stays auto: a fixed 0 height
@@ -111,10 +111,27 @@ export function useSheetDrag({
111
111
  once: true
112
112
  });
113
113
  };
114
+ const onPointerCancel = event => {
115
+ if (!tracking || event.pointerId !== pointerId) {
116
+ return;
117
+ }
118
+ tracking = false;
119
+ dragging = false;
120
+ velocity = 0;
121
+ if (sheet.hasPointerCapture?.(pointerId)) {
122
+ sheet.releasePointerCapture(pointerId);
123
+ }
124
+ pointerId = -1;
125
+ sheet.style.transform = '';
126
+ sheet.style.transition = '';
127
+ if (backdropRef.current) {
128
+ backdropRef.current.style.opacity = '1';
129
+ }
130
+ };
114
131
  sheet.addEventListener('pointerdown', onPointerDown);
115
132
  sheet.addEventListener('pointermove', onPointerMove);
116
133
  sheet.addEventListener('pointerup', onPointerUp);
117
- sheet.addEventListener('pointercancel', onPointerUp);
134
+ sheet.addEventListener('pointercancel', onPointerCancel);
118
135
  return () => {
119
136
  if (sheet.hasPointerCapture?.(pointerId)) {
120
137
  sheet.releasePointerCapture(pointerId);
@@ -122,7 +139,7 @@ export function useSheetDrag({
122
139
  sheet.removeEventListener('pointerdown', onPointerDown);
123
140
  sheet.removeEventListener('pointermove', onPointerMove);
124
141
  sheet.removeEventListener('pointerup', onPointerUp);
125
- sheet.removeEventListener('pointercancel', onPointerUp);
142
+ sheet.removeEventListener('pointercancel', onPointerCancel);
126
143
  };
127
144
  }, [sheetRef, backdropRef, enabled]);
128
145
  }
@@ -1 +1 @@
1
- {"version":3,"names":["useEffect","useRef","DRAG_START_THRESHOLD","DISMISS_DISTANCE_RATIO","DISMISS_VELOCITY","UPWARD_RESISTANCE","SETTLE_TRANSITION","scrolledAncestorWithin","target","sheet","node","HTMLElement","scrollTop","scrollHeight","clientHeight","parentElement","undefined","useSheetDrag","sheetRef","backdropRef","enabled","onDismissByPan","onDismissRef","current","startY","lastY","lastTime","velocity","tracking","dragging","pointerId","setOffset","offset","style","transform","backdrop","opacity","String","Math","max","offsetHeight","onPointerDown","event","button","clientY","timeStamp","onPointerMove","dy","setPointerCapture","transition","dt","onPointerUp","height","addEventListener","clickEvent","stopPropagation","preventDefault","capture","once","hasPointerCapture","releasePointerCapture","removeEventListener"],"sourceRoot":"../../../src","sources":["web/useSheetDrag.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useEffect","useRef","DRAG_START_THRESHOLD","DISMISS_DISTANCE_RATIO","DISMISS_VELOCITY","UPWARD_RESISTANCE","SETTLE_TRANSITION","scrolledAncestorWithin","target","sheet","node","HTMLElement","scrollTop","scrollHeight","clientHeight","parentElement","undefined","useSheetDrag","sheetRef","backdropRef","enabled","onDismissByPan","onDismissRef","current","startY","lastY","lastTime","velocity","tracking","dragging","pointerId","setOffset","offset","style","transform","backdrop","opacity","String","Math","max","offsetHeight","onPointerDown","event","button","clientY","timeStamp","onPointerMove","dy","setPointerCapture","transition","dt","onPointerUp","height","addEventListener","clickEvent","stopPropagation","preventDefault","capture","once","onPointerCancel","hasPointerCapture","releasePointerCapture","removeEventListener"],"sourceRoot":"../../../src","sources":["web/useSheetDrag.ts"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"useSheetDrag.d.ts","sourceRoot":"","sources":["../../../../src/web/useSheetDrag.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAsBvC;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,WAAW,EACX,OAAO,EACP,cAAc,GACf,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC3C,WAAW,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B,QA2GA"}
1
+ {"version":3,"file":"useSheetDrag.d.ts","sourceRoot":"","sources":["../../../../src/web/useSheetDrag.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAsBvC;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,WAAW,EACX,OAAO,EACP,cAAc,GACf,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC3C,WAAW,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B,QA6HA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-native-overlay",
3
- "version": "6.7.0-alpha.344",
3
+ "version": "6.7.0-alpha.354",
4
4
  "description": "Native and Web overlay hosts with ordered levels, page scope and Fabric measurement",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -129,10 +129,28 @@ export function useSheetDrag({
129
129
  );
130
130
  };
131
131
 
132
+ const onPointerCancel = (event: PointerEvent) => {
133
+ if (!tracking || event.pointerId !== pointerId) {
134
+ return;
135
+ }
136
+ tracking = false;
137
+ dragging = false;
138
+ velocity = 0;
139
+ if (sheet.hasPointerCapture?.(pointerId)) {
140
+ sheet.releasePointerCapture(pointerId);
141
+ }
142
+ pointerId = -1;
143
+ sheet.style.transform = '';
144
+ sheet.style.transition = '';
145
+ if (backdropRef.current) {
146
+ backdropRef.current.style.opacity = '1';
147
+ }
148
+ };
149
+
132
150
  sheet.addEventListener('pointerdown', onPointerDown);
133
151
  sheet.addEventListener('pointermove', onPointerMove);
134
152
  sheet.addEventListener('pointerup', onPointerUp);
135
- sheet.addEventListener('pointercancel', onPointerUp);
153
+ sheet.addEventListener('pointercancel', onPointerCancel);
136
154
  return () => {
137
155
  if (sheet.hasPointerCapture?.(pointerId)) {
138
156
  sheet.releasePointerCapture(pointerId);
@@ -140,7 +158,7 @@ export function useSheetDrag({
140
158
  sheet.removeEventListener('pointerdown', onPointerDown);
141
159
  sheet.removeEventListener('pointermove', onPointerMove);
142
160
  sheet.removeEventListener('pointerup', onPointerUp);
143
- sheet.removeEventListener('pointercancel', onPointerUp);
161
+ sheet.removeEventListener('pointercancel', onPointerCancel);
144
162
  };
145
163
  }, [sheetRef, backdropRef, enabled]);
146
164
  }