react-native-morph-card 0.1.0 → 0.1.1

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.
@@ -14,6 +14,7 @@ import android.util.Log
14
14
  import android.view.View
15
15
  import android.view.ViewGroup
16
16
  import android.view.ViewOutlineProvider
17
+ import android.view.ViewTreeObserver
17
18
  import android.view.animation.PathInterpolator
18
19
  import android.widget.FrameLayout
19
20
  import android.widget.ImageView
@@ -54,6 +55,7 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
54
55
  private var targetScreenContainerRef: WeakReference<View>? = null
55
56
  private var screenStackRef: WeakReference<ViewGroup>? = null
56
57
  private var hierarchyListener: ViewGroup.OnHierarchyChangeListener? = null
58
+ private var preDrawListener: ViewTreeObserver.OnPreDrawListener? = null
57
59
 
58
60
  // Spring-like interpolator (approximates iOS dampingRatio:0.85)
59
61
  private val springInterpolator = PathInterpolator(0.25f, 1.0f, 0.5f, 1.0f)
@@ -222,10 +224,53 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
222
224
  }
223
225
  }
224
226
 
227
+ /**
228
+ * Walk the view tree and hide any screen container that isn't the source screen.
229
+ * This catches modal screens added to separate ScreenStacks.
230
+ */
231
+ private fun hideNewScreenContainers(root: ViewGroup, sourceScreen: View?) {
232
+ fun walk(group: ViewGroup) {
233
+ val name = group.javaClass.name
234
+ if (name.contains("ScreenStack") || name.contains("ScreenContainer")) {
235
+ for (i in 0 until group.childCount) {
236
+ val child = group.getChildAt(i)
237
+ if (child !== sourceScreen && child.visibility == View.VISIBLE) {
238
+ // Check if this is a screen container we haven't seen before
239
+ val childScreen = child
240
+ val isSourceAncestor = isAncestorOf(childScreen, sourceScreen)
241
+ if (!isSourceAncestor) {
242
+ childScreen.visibility = View.INVISIBLE
243
+ Log.d(TAG, "preDraw: hid screen container ${childScreen.javaClass.simpleName}")
244
+ }
245
+ }
246
+ }
247
+ }
248
+ for (i in 0 until group.childCount) {
249
+ val child = group.getChildAt(i)
250
+ if (child is ViewGroup) walk(child)
251
+ }
252
+ }
253
+ walk(root)
254
+ }
255
+
256
+ private fun isAncestorOf(potentialAncestor: View, target: View?): Boolean {
257
+ if (target == null) return false
258
+ var current: View? = target
259
+ while (current != null) {
260
+ if (current === potentialAncestor) return true
261
+ current = if (current.parent is View) current.parent as View else null
262
+ }
263
+ return false
264
+ }
265
+
225
266
  private fun removeHierarchyListener() {
226
267
  screenStackRef?.get()?.setOnHierarchyChangeListener(null)
227
268
  screenStackRef = null
228
269
  hierarchyListener = null
270
+ preDrawListener?.let { listener ->
271
+ getDecorView()?.viewTreeObserver?.removeOnPreDrawListener(listener)
272
+ preDrawListener = null
273
+ }
229
274
  }
230
275
 
231
276
  // ══════════════════════════════════════════════════════════════
@@ -286,7 +331,7 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
286
331
  override fun onChildViewAdded(parent: View?, child: View?) {
287
332
  if (child != null && child !== sourceScreen) {
288
333
  child.visibility = View.INVISIBLE
289
- Log.d(TAG, "prepareExpand: intercepted new screen, set INVISIBLE")
334
+ Log.d(TAG, "prepareExpand: hierarchy intercepted new screen, set INVISIBLE")
290
335
  }
291
336
  }
292
337
  override fun onChildViewRemoved(parent: View?, child: View?) {}
@@ -296,6 +341,17 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
296
341
  hierarchyListener = listener
297
342
  }
298
343
 
344
+ // Also install a pre-draw listener on the DecorView to catch modal screens
345
+ // that are added to a different ScreenStack (e.g. transparentModal).
346
+ // This fires before every frame draw, so we can hide screens before they render.
347
+ val savedSourceScreen = sourceScreen
348
+ val pdListener = ViewTreeObserver.OnPreDrawListener {
349
+ hideNewScreenContainers(decorView, savedSourceScreen)
350
+ true
351
+ }
352
+ decorView.viewTreeObserver.addOnPreDrawListener(pdListener)
353
+ preDrawListener = pdListener
354
+
299
355
  // Capture snapshot
300
356
  val cardImage = captureSnapshot()
301
357
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-morph-card",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Native card-to-modal morph transition for React Native. iOS App Store-style expand animation.",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",