react-native-morph-card 0.1.0

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.
Files changed (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -0
  3. package/android/build.gradle +59 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/java/com/melivalesca/morphcard/MorphCardModule.kt +120 -0
  6. package/android/src/main/java/com/melivalesca/morphcard/MorphCardPackage.kt +42 -0
  7. package/android/src/main/java/com/melivalesca/morphcard/MorphCardSourceManager.kt +40 -0
  8. package/android/src/main/java/com/melivalesca/morphcard/MorphCardSourceView.kt +755 -0
  9. package/android/src/main/java/com/melivalesca/morphcard/MorphCardTargetManager.kt +48 -0
  10. package/android/src/main/java/com/melivalesca/morphcard/MorphCardTargetView.kt +159 -0
  11. package/android/src/main/java/com/melivalesca/morphcard/MorphCardViewRegistry.kt +24 -0
  12. package/android/src/main/jni/CMakeLists.txt +62 -0
  13. package/common/cpp/react/renderer/components/morphcard/RNCMorphCardState.h +30 -0
  14. package/ios/Fabric/RNCMorphCardSourceComponentView.h +25 -0
  15. package/ios/Fabric/RNCMorphCardSourceComponentView.mm +582 -0
  16. package/ios/Fabric/RNCMorphCardTargetComponentView.h +20 -0
  17. package/ios/Fabric/RNCMorphCardTargetComponentView.mm +99 -0
  18. package/ios/RNCMorphCardModule.h +14 -0
  19. package/ios/RNCMorphCardModule.mm +126 -0
  20. package/ios/RNCMorphCardSource.h +23 -0
  21. package/ios/RNCMorphCardSource.m +144 -0
  22. package/ios/RNCMorphCardSourceManager.h +5 -0
  23. package/ios/RNCMorphCardSourceManager.m +17 -0
  24. package/ios/RNCMorphCardTarget.h +19 -0
  25. package/ios/RNCMorphCardTarget.m +27 -0
  26. package/ios/RNCMorphCardTargetManager.h +5 -0
  27. package/ios/RNCMorphCardTargetManager.m +16 -0
  28. package/ios/RNCMorphCardViewRegistry.h +35 -0
  29. package/ios/RNCMorphCardViewRegistry.m +40 -0
  30. package/lib/commonjs/MorphCard.types.js +6 -0
  31. package/lib/commonjs/MorphCard.types.js.map +1 -0
  32. package/lib/commonjs/MorphCardSource.js +95 -0
  33. package/lib/commonjs/MorphCardSource.js.map +1 -0
  34. package/lib/commonjs/MorphCardTarget.js +83 -0
  35. package/lib/commonjs/MorphCardTarget.js.map +1 -0
  36. package/lib/commonjs/index.js +45 -0
  37. package/lib/commonjs/index.js.map +1 -0
  38. package/lib/commonjs/package.json +1 -0
  39. package/lib/commonjs/specs/NativeMorphCardModule.js +9 -0
  40. package/lib/commonjs/specs/NativeMorphCardModule.js.map +1 -0
  41. package/lib/commonjs/specs/NativeMorphCardSource.js +10 -0
  42. package/lib/commonjs/specs/NativeMorphCardSource.js.map +1 -0
  43. package/lib/commonjs/specs/NativeMorphCardTarget.js +10 -0
  44. package/lib/commonjs/specs/NativeMorphCardTarget.js.map +1 -0
  45. package/lib/commonjs/useMorphTarget.js +28 -0
  46. package/lib/commonjs/useMorphTarget.js.map +1 -0
  47. package/lib/module/MorphCard.types.js +4 -0
  48. package/lib/module/MorphCard.types.js.map +1 -0
  49. package/lib/module/MorphCardSource.js +85 -0
  50. package/lib/module/MorphCardSource.js.map +1 -0
  51. package/lib/module/MorphCardTarget.js +76 -0
  52. package/lib/module/MorphCardTarget.js.map +1 -0
  53. package/lib/module/index.js +6 -0
  54. package/lib/module/index.js.map +1 -0
  55. package/lib/module/package.json +1 -0
  56. package/lib/module/specs/NativeMorphCardModule.js +5 -0
  57. package/lib/module/specs/NativeMorphCardModule.js.map +1 -0
  58. package/lib/module/specs/NativeMorphCardSource.js +5 -0
  59. package/lib/module/specs/NativeMorphCardSource.js.map +1 -0
  60. package/lib/module/specs/NativeMorphCardTarget.js +5 -0
  61. package/lib/module/specs/NativeMorphCardTarget.js.map +1 -0
  62. package/lib/module/useMorphTarget.js +22 -0
  63. package/lib/module/useMorphTarget.js.map +1 -0
  64. package/lib/typescript/src/MorphCard.types.d.ts +29 -0
  65. package/lib/typescript/src/MorphCard.types.d.ts.map +1 -0
  66. package/lib/typescript/src/MorphCardSource.d.ts +35 -0
  67. package/lib/typescript/src/MorphCardSource.d.ts.map +1 -0
  68. package/lib/typescript/src/MorphCardTarget.d.ts +20 -0
  69. package/lib/typescript/src/MorphCardTarget.d.ts.map +1 -0
  70. package/lib/typescript/src/index.d.ts +6 -0
  71. package/lib/typescript/src/index.d.ts.map +1 -0
  72. package/lib/typescript/src/specs/NativeMorphCardModule.d.ts +14 -0
  73. package/lib/typescript/src/specs/NativeMorphCardModule.d.ts.map +1 -0
  74. package/lib/typescript/src/specs/NativeMorphCardSource.d.ts +13 -0
  75. package/lib/typescript/src/specs/NativeMorphCardSource.d.ts.map +1 -0
  76. package/lib/typescript/src/specs/NativeMorphCardTarget.d.ts +25 -0
  77. package/lib/typescript/src/specs/NativeMorphCardTarget.d.ts.map +1 -0
  78. package/lib/typescript/src/useMorphTarget.d.ts +16 -0
  79. package/lib/typescript/src/useMorphTarget.d.ts.map +1 -0
  80. package/package.json +101 -0
  81. package/react-native-morph-card.podspec +41 -0
  82. package/react-native.config.js +13 -0
  83. package/src/MorphCard.types.ts +29 -0
  84. package/src/MorphCardSource.tsx +105 -0
  85. package/src/MorphCardTarget.tsx +127 -0
  86. package/src/index.tsx +10 -0
  87. package/src/specs/NativeMorphCardModule.ts +21 -0
  88. package/src/specs/NativeMorphCardSource.ts +20 -0
  89. package/src/specs/NativeMorphCardTarget.ts +38 -0
  90. package/src/useMorphTarget.ts +21 -0
@@ -0,0 +1,48 @@
1
+ package com.melivalesca.morphcard
2
+
3
+ import com.facebook.react.module.annotations.ReactModule
4
+ import com.facebook.react.uimanager.ThemedReactContext
5
+ import com.facebook.react.uimanager.ViewGroupManager
6
+ import com.facebook.react.uimanager.ViewManagerDelegate
7
+ import com.facebook.react.viewmanagers.RNCMorphCardTargetManagerDelegate
8
+ import com.facebook.react.viewmanagers.RNCMorphCardTargetManagerInterface
9
+
10
+ @ReactModule(name = MorphCardTargetManager.REACT_CLASS)
11
+ class MorphCardTargetManager :
12
+ ViewGroupManager<MorphCardTargetView>(),
13
+ RNCMorphCardTargetManagerInterface<MorphCardTargetView> {
14
+
15
+ private val delegate = RNCMorphCardTargetManagerDelegate(this)
16
+
17
+ override fun getDelegate(): ViewManagerDelegate<MorphCardTargetView> = delegate
18
+
19
+ override fun getName(): String = REACT_CLASS
20
+
21
+ override fun createViewInstance(reactContext: ThemedReactContext): MorphCardTargetView {
22
+ return MorphCardTargetView(reactContext)
23
+ }
24
+
25
+ override fun setDuration(view: MorphCardTargetView, value: Double) {
26
+ // Duration is managed by the source view
27
+ }
28
+
29
+ override fun setSourceTag(view: MorphCardTargetView, value: Int) {
30
+ view.sourceTag = value
31
+ }
32
+
33
+ override fun setTargetWidth(view: MorphCardTargetView, value: Double) {
34
+ view.targetWidth = value.toFloat()
35
+ }
36
+
37
+ override fun setTargetHeight(view: MorphCardTargetView, value: Double) {
38
+ view.targetHeight = value.toFloat()
39
+ }
40
+
41
+ override fun setTargetBorderRadius(view: MorphCardTargetView, value: Double) {
42
+ view.targetBorderRadius = value.toFloat()
43
+ }
44
+
45
+ companion object {
46
+ const val REACT_CLASS = "RNCMorphCardTarget"
47
+ }
48
+ }
@@ -0,0 +1,159 @@
1
+ package com.melivalesca.morphcard
2
+
3
+ import android.content.Context
4
+ import android.graphics.Bitmap
5
+ import android.graphics.Canvas
6
+ import android.graphics.Matrix
7
+ import android.graphics.Outline
8
+ import android.graphics.Paint
9
+ import android.graphics.Path
10
+ import android.graphics.RectF
11
+ import android.util.Log
12
+ import android.view.View
13
+ import android.view.ViewGroup
14
+ import android.view.ViewOutlineProvider
15
+ import android.widget.ImageView
16
+ import com.facebook.react.views.view.ReactViewGroup
17
+
18
+ class MorphCardTargetView(context: Context) : ReactViewGroup(context) {
19
+
20
+ var targetWidth: Float = 0f
21
+ var targetHeight: Float = 0f
22
+ var targetBorderRadius: Float = -1f
23
+ var sourceTag: Int = 0
24
+
25
+ // Snapshot drawn via canvas — Fabric can't remove it
26
+ private var snapshotBitmap: Bitmap? = null
27
+ private var snapshotFrame: RectF? = null
28
+ private var snapshotCornerRadius: Float = 0f
29
+ private var snapshotBgColor: Int? = null
30
+ private val snapshotPaint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
31
+ private val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG)
32
+
33
+ private val density: Float
34
+ get() = resources.displayMetrics.density
35
+
36
+ override fun onAttachedToWindow() {
37
+ super.onAttachedToWindow()
38
+ MorphCardViewRegistry.register(this, id)
39
+ Log.d("MorphCard", "TargetView attached: id=$id sourceTag=$sourceTag")
40
+
41
+ if (sourceTag > 0) {
42
+ val screenContainer = findScreenContainer(this)
43
+ if (screenContainer != null) {
44
+ screenContainer.visibility = View.INVISIBLE
45
+ Log.d("MorphCard", "TargetView: set screen INVISIBLE")
46
+ }
47
+ }
48
+ }
49
+
50
+ override fun onDetachedFromWindow() {
51
+ super.onDetachedFromWindow()
52
+ MorphCardViewRegistry.unregister(id)
53
+ }
54
+
55
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
56
+ super.onLayout(changed, left, top, right, bottom)
57
+ applyBorderRadiusClipping()
58
+ }
59
+
60
+ /**
61
+ * Draw the snapshot bitmap BEFORE children so React components
62
+ * (X button, etc.) render on top of it.
63
+ */
64
+ override fun dispatchDraw(canvas: Canvas) {
65
+ val bmp = snapshotBitmap
66
+ val frame = snapshotFrame
67
+ if (bmp != null && frame != null) {
68
+ val radiusPx = snapshotCornerRadius
69
+
70
+ // Clip to rounded rect if needed
71
+ if (radiusPx > 0) {
72
+ canvas.save()
73
+ val clipPath = Path()
74
+ clipPath.addRoundRect(
75
+ 0f, 0f, width.toFloat(), height.toFloat(),
76
+ radiusPx, radiusPx, Path.Direction.CW
77
+ )
78
+ canvas.clipPath(clipPath)
79
+ }
80
+
81
+ // Draw background
82
+ snapshotBgColor?.let { color ->
83
+ bgPaint.color = color
84
+ canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), bgPaint)
85
+ }
86
+
87
+ // Draw bitmap in the specified frame
88
+ val src = android.graphics.Rect(0, 0, bmp.width, bmp.height)
89
+ val dst = android.graphics.Rect(
90
+ frame.left.toInt(), frame.top.toInt(),
91
+ frame.right.toInt(), frame.bottom.toInt()
92
+ )
93
+ canvas.drawBitmap(bmp, src, dst, snapshotPaint)
94
+
95
+ if (radiusPx > 0) {
96
+ canvas.restore()
97
+ }
98
+ }
99
+
100
+ // Draw React children on top
101
+ super.dispatchDraw(canvas)
102
+ }
103
+
104
+ private fun applyBorderRadiusClipping() {
105
+ val radiusPx = if (targetBorderRadius > 0f) targetBorderRadius * density else 0f
106
+ if (radiusPx > 0f) {
107
+ clipToOutline = true
108
+ outlineProvider = object : ViewOutlineProvider() {
109
+ override fun getOutline(v: View, outline: Outline) {
110
+ outline.setRoundRect(0, 0, v.width, v.height, radiusPx)
111
+ }
112
+ }
113
+ } else {
114
+ clipToOutline = false
115
+ }
116
+ }
117
+
118
+ private fun findScreenContainer(view: View?): View? {
119
+ if (view == null) return null
120
+ var current: View? = view
121
+ while (current != null) {
122
+ val parent = current.parent
123
+ if (parent is ViewGroup) {
124
+ val parentName = parent.javaClass.name
125
+ if (parentName.contains("ScreenStack") || parentName.contains("ScreenContainer")) {
126
+ return current
127
+ }
128
+ }
129
+ current = if (current.parent is View) current.parent as View else null
130
+ }
131
+ return null
132
+ }
133
+
134
+ fun showSnapshot(
135
+ image: Bitmap,
136
+ scaleType: ImageView.ScaleType,
137
+ frame: RectF,
138
+ cornerRadius: Float,
139
+ backgroundColor: Int?
140
+ ) {
141
+ Log.d("MorphCard", "showSnapshot: viewSize=${width}x${height} frame=$frame cornerR=$cornerRadius bg=$backgroundColor")
142
+ snapshotBitmap = image
143
+ snapshotFrame = frame
144
+ snapshotCornerRadius = cornerRadius
145
+ snapshotBgColor = backgroundColor
146
+ invalidate()
147
+ }
148
+
149
+ fun clearSnapshot() {
150
+ if (snapshotBitmap != null) {
151
+ Log.d("MorphCard", "clearSnapshot: clearing bitmap")
152
+ snapshotBitmap = null
153
+ snapshotFrame = null
154
+ snapshotCornerRadius = 0f
155
+ snapshotBgColor = null
156
+ invalidate()
157
+ }
158
+ }
159
+ }
@@ -0,0 +1,24 @@
1
+ package com.melivalesca.morphcard
2
+
3
+ import android.view.View
4
+ import java.lang.ref.WeakReference
5
+
6
+ /**
7
+ * Singleton registry mapping React view tags to native views.
8
+ * Uses weak references so views can be garbage-collected normally.
9
+ */
10
+ object MorphCardViewRegistry {
11
+ private val views = mutableMapOf<Int, WeakReference<View>>()
12
+
13
+ fun register(view: View, tag: Int) {
14
+ views[tag] = WeakReference(view)
15
+ }
16
+
17
+ fun unregister(tag: Int) {
18
+ views.remove(tag)
19
+ }
20
+
21
+ fun getView(tag: Int): View? {
22
+ return views[tag]?.get()
23
+ }
24
+ }
@@ -0,0 +1,62 @@
1
+ cmake_minimum_required(VERSION 3.13)
2
+ set(CMAKE_VERBOSE_MAKEFILE ON)
3
+
4
+ set(LIB_LITERAL morphcard)
5
+ set(LIB_TARGET_NAME react_codegen_${LIB_LITERAL})
6
+
7
+ set(LIB_ANDROID_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
8
+ set(LIB_COMMON_DIR ${LIB_ANDROID_DIR}/../common/cpp)
9
+ set(LIB_COMMON_COMPONENTS_DIR ${LIB_COMMON_DIR}/react/renderer/components/${LIB_LITERAL})
10
+ set(LIB_ANDROID_GENERATED_JNI_DIR ${LIB_ANDROID_DIR}/build/generated/source/codegen/jni)
11
+ set(LIB_ANDROID_GENERATED_COMPONENTS_DIR ${LIB_ANDROID_GENERATED_JNI_DIR}/react/renderer/components/${LIB_LITERAL})
12
+
13
+ add_compile_options(
14
+ -fexceptions
15
+ -frtti
16
+ -std=c++20
17
+ -Wall
18
+ -Wpedantic
19
+ )
20
+
21
+ file(GLOB LIB_CUSTOM_SRCS CONFIGURE_DEPENDS *.cpp ${LIB_COMMON_COMPONENTS_DIR}/*.cpp)
22
+ file(GLOB LIB_CODEGEN_SRCS CONFIGURE_DEPENDS
23
+ ${LIB_ANDROID_GENERATED_JNI_DIR}/*.cpp
24
+ ${LIB_ANDROID_GENERATED_COMPONENTS_DIR}/*.cpp
25
+ )
26
+
27
+ add_library(
28
+ ${LIB_TARGET_NAME}
29
+ SHARED
30
+ ${LIB_CUSTOM_SRCS}
31
+ ${LIB_CODEGEN_SRCS}
32
+ )
33
+
34
+ target_include_directories(
35
+ ${LIB_TARGET_NAME}
36
+ PUBLIC
37
+ .
38
+ ${LIB_COMMON_DIR}
39
+ ${LIB_ANDROID_GENERATED_JNI_DIR}
40
+ ${LIB_ANDROID_GENERATED_COMPONENTS_DIR}
41
+ )
42
+
43
+ target_link_libraries(
44
+ ${LIB_TARGET_NAME}
45
+ ReactAndroid::reactnative
46
+ ReactAndroid::jsi
47
+ fbjni::fbjni
48
+ )
49
+
50
+ if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 80)
51
+ target_compile_reactnative_options(${LIB_TARGET_NAME} PRIVATE)
52
+ else()
53
+ target_compile_options(
54
+ ${LIB_TARGET_NAME}
55
+ PRIVATE
56
+ -DLOG_TAG=\"ReactNative\"
57
+ -fexceptions
58
+ -frtti
59
+ -std=c++20
60
+ -Wall
61
+ )
62
+ endif()
@@ -0,0 +1,30 @@
1
+ #pragma once
2
+
3
+ #ifdef ANDROID
4
+ #include <folly/dynamic.h>
5
+ #include <react/renderer/mapbuffer/MapBuffer.h>
6
+ #include <react/renderer/mapbuffer/MapBufferBuilder.h>
7
+ #endif
8
+
9
+ namespace facebook::react {
10
+
11
+ class RNCMorphCardState final {
12
+ public:
13
+ RNCMorphCardState() = default;
14
+
15
+ #ifdef ANDROID
16
+ RNCMorphCardState(
17
+ const RNCMorphCardState& previousState,
18
+ folly::dynamic data) {}
19
+
20
+ folly::dynamic getDynamic() const {
21
+ return {};
22
+ }
23
+
24
+ MapBuffer getMapBuffer() const {
25
+ return MapBufferBuilder::EMPTY();
26
+ }
27
+ #endif
28
+ };
29
+
30
+ } // namespace facebook::react
@@ -0,0 +1,25 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTViewComponentView.h>
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @interface RNCMorphCardSourceComponentView : RCTViewComponentView
7
+
8
+ @property (nonatomic, assign) CGFloat pendingTargetWidth;
9
+ @property (nonatomic, assign) CGFloat pendingTargetHeight;
10
+ @property (nonatomic, assign) CGFloat pendingTargetBorderRadius;
11
+ @property (nonatomic, assign) CGFloat pendingContentOffsetY;
12
+ @property (nonatomic, assign) BOOL pendingContentCentered;
13
+
14
+ - (void)expandToTarget:(nullable UIView *)targetView
15
+ resolve:(RCTPromiseResolveBlock)resolve;
16
+
17
+ - (void)collapseFromTarget:(nullable UIView *)targetView
18
+ resolve:(RCTPromiseResolveBlock)resolve;
19
+
20
+ /// Collapse using the stored target from the last expand call.
21
+ - (void)collapseWithResolve:(RCTPromiseResolveBlock)resolve;
22
+
23
+ @end
24
+
25
+ NS_ASSUME_NONNULL_END