react-native-morph-card 0.1.0 → 0.1.2

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,59 @@ 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 already known.
229
+ * This catches modal screens added to separate ScreenStacks (e.g. transparentModal).
230
+ */
231
+ private fun hideNewScreenContainers(root: ViewGroup, knownScreens: Set<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 (!knownScreens.contains(child) && child.visibility == View.VISIBLE) {
238
+ child.visibility = View.INVISIBLE
239
+ Log.d(TAG, "preDraw: hid new screen ${child.javaClass.simpleName} in ${group.javaClass.simpleName}")
240
+ }
241
+ }
242
+ }
243
+ for (i in 0 until group.childCount) {
244
+ val child = group.getChildAt(i)
245
+ if (child is ViewGroup) walk(child)
246
+ }
247
+ }
248
+ walk(root)
249
+ }
250
+
251
+ /**
252
+ * Collect all current children of ScreenStack/ScreenContainer views.
253
+ */
254
+ private fun collectExistingScreens(root: ViewGroup): Set<View> {
255
+ val screens = mutableSetOf<View>()
256
+ fun walk(group: ViewGroup) {
257
+ val name = group.javaClass.name
258
+ if (name.contains("ScreenStack") || name.contains("ScreenContainer")) {
259
+ for (i in 0 until group.childCount) {
260
+ screens.add(group.getChildAt(i))
261
+ }
262
+ }
263
+ for (i in 0 until group.childCount) {
264
+ val child = group.getChildAt(i)
265
+ if (child is ViewGroup) walk(child)
266
+ }
267
+ }
268
+ walk(root)
269
+ return screens
270
+ }
271
+
225
272
  private fun removeHierarchyListener() {
226
273
  screenStackRef?.get()?.setOnHierarchyChangeListener(null)
227
274
  screenStackRef = null
228
275
  hierarchyListener = null
276
+ preDrawListener?.let { listener ->
277
+ getDecorView()?.viewTreeObserver?.removeOnPreDrawListener(listener)
278
+ preDrawListener = null
279
+ }
229
280
  }
230
281
 
231
282
  // ══════════════════════════════════════════════════════════════
@@ -286,7 +337,7 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
286
337
  override fun onChildViewAdded(parent: View?, child: View?) {
287
338
  if (child != null && child !== sourceScreen) {
288
339
  child.visibility = View.INVISIBLE
289
- Log.d(TAG, "prepareExpand: intercepted new screen, set INVISIBLE")
340
+ Log.d(TAG, "prepareExpand: hierarchy intercepted new screen, set INVISIBLE")
290
341
  }
291
342
  }
292
343
  override fun onChildViewRemoved(parent: View?, child: View?) {}
@@ -296,6 +347,18 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
296
347
  hierarchyListener = listener
297
348
  }
298
349
 
350
+ // Also install a pre-draw listener on the DecorView to catch modal screens
351
+ // that are added to a different ScreenStack (e.g. transparentModal).
352
+ // This fires before every frame draw, so we can hide screens before they render.
353
+ val knownScreens = collectExistingScreens(decorView)
354
+ Log.d(TAG, "prepareExpand: tracking ${knownScreens.size} existing screens")
355
+ val pdListener = ViewTreeObserver.OnPreDrawListener {
356
+ hideNewScreenContainers(decorView, knownScreens)
357
+ true
358
+ }
359
+ decorView.viewTreeObserver.addOnPreDrawListener(pdListener)
360
+ preDrawListener = pdListener
361
+
299
362
  // Capture snapshot
300
363
  val cardImage = captureSnapshot()
301
364
 
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.2",
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",