golia-expo-utils 1.0.1 → 1.0.3
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/android/build.gradle +6 -1
- package/android/src/main/java/expo/modules/goliaexpoutils/segmentedControl/ReactSegmentedControl.kt +63 -26
- package/android/src/main/java/expo/modules/goliaexpoutils/waterView/ReactWaterView.kt +84 -20
- package/build/ReactWaterView.d.ts +1 -1
- package/build/ReactWaterView.d.ts.map +1 -1
- package/build/ReactWaterView.js +1 -1
- package/build/ReactWaterView.js.map +1 -1
- package/package.json +29 -20
- package/src/ReactWaterView.tsx +1 -1
- package/bun.lock +0 -2368
package/android/build.gradle
CHANGED
|
@@ -57,10 +57,15 @@ android {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
dependencies {
|
|
60
|
-
def composeBom = platform('androidx.compose:compose-bom:
|
|
60
|
+
def composeBom = platform('androidx.compose:compose-bom:2026.01.00')
|
|
61
61
|
implementation composeBom
|
|
62
62
|
implementation 'androidx.compose.ui:ui'
|
|
63
63
|
implementation 'androidx.compose.ui:ui-tooling-preview'
|
|
64
64
|
implementation 'androidx.compose.material3:material3'
|
|
65
65
|
implementation 'io.github.kyant0:backdrop:1.0.0'
|
|
66
|
+
implementation "androidx.appcompat:appcompat:1.7.1"
|
|
67
|
+
implementation "androidx.activity:activity-ktx:1.12.2"
|
|
68
|
+
implementation "androidx.savedstate:savedstate-ktx:1.4.0"
|
|
69
|
+
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.10.0"
|
|
70
|
+
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.10.0"
|
|
66
71
|
}
|
package/android/src/main/java/expo/modules/goliaexpoutils/segmentedControl/ReactSegmentedControl.kt
CHANGED
|
@@ -2,12 +2,18 @@ package expo.modules.goliaexpoutils.segmentedControl
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.util.Log
|
|
6
|
+
import android.view.ViewGroup
|
|
5
7
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
6
8
|
import androidx.compose.runtime.mutableIntStateOf
|
|
7
9
|
import androidx.compose.runtime.mutableStateOf
|
|
8
10
|
import androidx.compose.ui.Modifier
|
|
9
11
|
import androidx.compose.ui.platform.ComposeView
|
|
10
12
|
import androidx.compose.ui.platform.ViewCompositionStrategy
|
|
13
|
+
import androidx.lifecycle.findViewTreeLifecycleOwner
|
|
14
|
+
import androidx.lifecycle.setViewTreeLifecycleOwner
|
|
15
|
+
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
|
|
16
|
+
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
|
11
17
|
import expo.modules.goliaexpoutils.Config
|
|
12
18
|
import expo.modules.kotlin.AppContext
|
|
13
19
|
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
@@ -28,42 +34,73 @@ class ReactSegmentedControl(context: Context, appContext: AppContext) :
|
|
|
28
34
|
val hapticEnabledState = mutableStateOf(true)
|
|
29
35
|
val enabledState = mutableStateOf(true)
|
|
30
36
|
|
|
31
|
-
private var isContentSet = false
|
|
32
|
-
|
|
33
37
|
private val composeView = ComposeView(context).apply {
|
|
34
|
-
layoutParams = LayoutParams(
|
|
38
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
39
|
+
LayoutParams.MATCH_PARENT,
|
|
40
|
+
LayoutParams.MATCH_PARENT
|
|
41
|
+
)
|
|
35
42
|
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
init {
|
|
39
|
-
|
|
40
|
-
clipToPadding = false
|
|
41
|
-
addView(composeView)
|
|
46
|
+
// ❌ 不要在这里 addView(composeView)
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
override fun onAttachedToWindow() {
|
|
45
50
|
super.onAttachedToWindow()
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
ctrlBackgroundColor = ctrlBgColorState.value,
|
|
60
|
-
textColor = textColorState.value,
|
|
61
|
-
autoWidth = autoWidthState.value,
|
|
62
|
-
hapticEnabled = hapticEnabledState.value,
|
|
63
|
-
enabled = enabledState.value,
|
|
64
|
-
glassExpansion = Config.Common.Glass.DEFAULT_EXPANSION,
|
|
65
|
-
)
|
|
52
|
+
// 1. 保底注入 Lifecycle
|
|
53
|
+
val activity = appContext.currentActivity
|
|
54
|
+
if (activity != null) {
|
|
55
|
+
try {
|
|
56
|
+
if (composeView.findViewTreeLifecycleOwner() == null && activity is androidx.lifecycle.LifecycleOwner) {
|
|
57
|
+
composeView.setViewTreeLifecycleOwner(activity)
|
|
58
|
+
}
|
|
59
|
+
if (composeView.findViewTreeSavedStateRegistryOwner() == null && activity is androidx.savedstate.SavedStateRegistryOwner) {
|
|
60
|
+
composeView.setViewTreeSavedStateRegistryOwner(activity)
|
|
61
|
+
}
|
|
62
|
+
} catch (e: Exception) {
|
|
63
|
+
Log.e("ReactSegmentedControl", "Lifecycle injection failed", e)
|
|
66
64
|
}
|
|
67
65
|
}
|
|
66
|
+
|
|
67
|
+
// 2. 动态挂载
|
|
68
|
+
if (composeView.parent == null) {
|
|
69
|
+
addView(composeView)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 3. 设置内容
|
|
73
|
+
composeView.setContent {
|
|
74
|
+
SegmentedControl(
|
|
75
|
+
items = itemsState.value,
|
|
76
|
+
selectedIndex = selectedIndexState.intValue,
|
|
77
|
+
onValueChange = { index ->
|
|
78
|
+
selectedIndexState.intValue = index
|
|
79
|
+
val title = itemsState.value.getOrNull(index)?.title ?: ""
|
|
80
|
+
if (appContext.reactContext !== null) {
|
|
81
|
+
try {
|
|
82
|
+
onValueChange(mapOf("value" to title, "index" to index))
|
|
83
|
+
} catch (_: Throwable) {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
modifier = Modifier.fillMaxSize(),
|
|
88
|
+
activeColor = activeColorState.value,
|
|
89
|
+
ctrlBackgroundColor = ctrlBgColorState.value,
|
|
90
|
+
textColor = textColorState.value,
|
|
91
|
+
autoWidth = autoWidthState.value,
|
|
92
|
+
hapticEnabled = hapticEnabledState.value,
|
|
93
|
+
enabled = enabledState.value,
|
|
94
|
+
glassExpansion = Config.Common.Glass.DEFAULT_EXPANSION,
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
override fun onDetachedFromWindow() {
|
|
100
|
+
// 4. 动态卸载
|
|
101
|
+
if (composeView.parent == this) {
|
|
102
|
+
removeView(composeView)
|
|
103
|
+
}
|
|
104
|
+
super.onDetachedFromWindow()
|
|
68
105
|
}
|
|
69
|
-
}
|
|
106
|
+
}
|
|
@@ -2,6 +2,7 @@ package expo.modules.goliaexpoutils.waterView
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.util.Log
|
|
5
6
|
import android.view.View
|
|
6
7
|
import android.view.ViewGroup
|
|
7
8
|
import android.widget.FrameLayout
|
|
@@ -17,6 +18,10 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
|
|
|
17
18
|
import androidx.compose.ui.unit.dp
|
|
18
19
|
import androidx.compose.ui.viewinterop.AndroidView
|
|
19
20
|
import androidx.core.view.isNotEmpty
|
|
21
|
+
import androidx.lifecycle.findViewTreeLifecycleOwner
|
|
22
|
+
import androidx.lifecycle.setViewTreeLifecycleOwner
|
|
23
|
+
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
|
|
24
|
+
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
|
20
25
|
import com.kyant.backdrop.backdrops.layerBackdrop
|
|
21
26
|
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
|
22
27
|
import expo.modules.goliaexpoutils.Config
|
|
@@ -52,59 +57,117 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
|
|
|
52
57
|
private val onPressIn by EventDispatcher()
|
|
53
58
|
private val onPressOut by EventDispatcher()
|
|
54
59
|
private val commandChannel = Channel<WaterCommand>(Channel.BUFFERED)
|
|
60
|
+
private val TAG = "ReactWaterView"
|
|
61
|
+
|
|
62
|
+
// 仅仅作为容器,初始化时不添加 ComposeView
|
|
55
63
|
private val childContainer = FrameLayout(context).apply {
|
|
56
64
|
layoutParams = ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
57
65
|
}
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
// ComposeView 懒加载,并且只在 Attached 状态下存在于 ViewTree 中
|
|
61
68
|
private val composeView = ComposeView(context).apply {
|
|
62
69
|
layoutParams = ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
70
|
+
// 关键策略:Detach 时立即销毁 Composition,防止内存泄漏和重绘崩溃
|
|
63
71
|
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
init {
|
|
67
75
|
clipChildren = false
|
|
68
76
|
clipToPadding = false
|
|
69
|
-
addView(composeView)
|
|
77
|
+
// ❌ 以前这里有 addView(composeView),现在删掉!
|
|
78
|
+
// 保持 View 树干净,Fabric 测量时不会触发 Compose 逻辑
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
override fun onAttachedToWindow() {
|
|
73
82
|
super.onAttachedToWindow()
|
|
74
83
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
// 1. 再次尝试获取 Activity (为了保底)
|
|
85
|
+
val activity = appContext.currentActivity
|
|
86
|
+
if (activity != null) {
|
|
87
|
+
// 2. 注入 Lifecycle (如果 ReactActivity 还没给它的话)
|
|
88
|
+
try {
|
|
89
|
+
if (composeView.findViewTreeLifecycleOwner() == null && activity is androidx.lifecycle.LifecycleOwner) {
|
|
90
|
+
composeView.setViewTreeLifecycleOwner(activity)
|
|
91
|
+
}
|
|
92
|
+
if (composeView.findViewTreeSavedStateRegistryOwner() == null && activity is androidx.savedstate.SavedStateRegistryOwner) {
|
|
93
|
+
composeView.setViewTreeSavedStateRegistryOwner(activity)
|
|
94
|
+
}
|
|
95
|
+
} catch (e: Exception) {
|
|
96
|
+
Log.e(TAG, "Lifecycle injection failed", e)
|
|
85
97
|
}
|
|
86
98
|
}
|
|
99
|
+
|
|
100
|
+
// 3. ✅ 核心修复:只有在真正 Attach 到窗口时,才把 ComposeView 加进去
|
|
101
|
+
// 这样 Fabric 在后台测量这个 View 时,它只是一个空的 ViewGroup,不会触发 Compose
|
|
102
|
+
if (composeView.parent == null) {
|
|
103
|
+
addView(composeView)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 4. 设置内容 (Compose 会自己判断是否需要重新 Composition)
|
|
107
|
+
composeView.setContent {
|
|
108
|
+
WaterViewEntry(
|
|
109
|
+
childContainer = childContainer,
|
|
110
|
+
props = props,
|
|
111
|
+
commandFlow = commandChannel.receiveAsFlow(),
|
|
112
|
+
onMoveDispatch = { x, y ->
|
|
113
|
+
if (appContext.reactContext !== null) {
|
|
114
|
+
try {
|
|
115
|
+
onMove(mapOf("x" to x, "y" to y))
|
|
116
|
+
} catch (_: Throwable) {
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
onPressInDispatch = { onPressIn(mapOf()) },
|
|
121
|
+
onPressOutDispatch = { onPressOut(mapOf()) }
|
|
122
|
+
)
|
|
123
|
+
}
|
|
87
124
|
}
|
|
88
125
|
|
|
126
|
+
override fun onDetachedFromWindow() {
|
|
127
|
+
// 5. ✅ 核心修复:Detach 时立即移除 ComposeView
|
|
128
|
+
// 确保 View 被回收或离屏测量时,ComposeView 不在 ViewTree 里
|
|
129
|
+
if (composeView.parent == this) {
|
|
130
|
+
removeView(composeView)
|
|
131
|
+
}
|
|
132
|
+
super.onDetachedFromWindow()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 下面处理 RN 子 View 的挂载 (Keep standard logic)
|
|
89
136
|
override fun addView(child: View?, index: Int) {
|
|
90
|
-
if (child == composeView)
|
|
91
|
-
|
|
137
|
+
if (child == composeView) {
|
|
138
|
+
super.addView(child, index)
|
|
139
|
+
} else {
|
|
140
|
+
// RN 的子 View (比如 Text/Image) 放到 childContainer 里
|
|
141
|
+
// 注意:childContainer 需要被 NativeChildrenContainer (AndroidView) 渲染
|
|
142
|
+
// 这一步逻辑通过 WaterViewEntry -> NativeChildrenContainer 实现
|
|
143
|
+
if (child != null) {
|
|
144
|
+
// 如果 child 已经有父节点,先移除 (Fabric 有时会重用 View)
|
|
145
|
+
(child.parent as? ViewGroup)?.removeView(child)
|
|
146
|
+
childContainer.addView(child)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
92
149
|
}
|
|
93
150
|
|
|
94
151
|
override fun removeView(view: View?) {
|
|
95
|
-
if (view == composeView)
|
|
96
|
-
|
|
152
|
+
if (view == composeView) {
|
|
153
|
+
super.removeView(view)
|
|
154
|
+
} else {
|
|
155
|
+
childContainer.removeView(view)
|
|
156
|
+
}
|
|
97
157
|
}
|
|
98
158
|
|
|
99
159
|
override fun removeViewAt(index: Int) {
|
|
160
|
+
// 这是一个 tricky 的 override,因为 RN 有时候通过 index 删除
|
|
161
|
+
// 我们要确保删的是 childContainer 里的东西,而不是把 composeView 删了
|
|
100
162
|
if (childContainer.isNotEmpty()) {
|
|
101
163
|
try {
|
|
102
|
-
childContainer.removeViewAt(0)
|
|
164
|
+
childContainer.removeViewAt(0) // 通常 RN 只是顺序删除
|
|
103
165
|
} catch (_: Exception) {
|
|
104
|
-
|
|
166
|
+
// 忽略
|
|
105
167
|
}
|
|
106
168
|
} else {
|
|
107
|
-
|
|
169
|
+
// 如果 childContainer 空了,那可能是在操作 ComposeView,虽然 RN 不应该直接操作它
|
|
170
|
+
// 保持 safe
|
|
108
171
|
}
|
|
109
172
|
}
|
|
110
173
|
|
|
@@ -113,6 +176,7 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
|
|
|
113
176
|
}
|
|
114
177
|
}
|
|
115
178
|
|
|
179
|
+
// Composable 部分保持不变 (WaterViewEntry, NativeChildrenContainer)
|
|
116
180
|
@Composable
|
|
117
181
|
private fun WaterViewEntry(
|
|
118
182
|
childContainer: FrameLayout,
|
|
@@ -53,7 +53,7 @@ declare const WaterViewEffect: React.ForwardRefExoticComponent<ViewProps & {
|
|
|
53
53
|
onPressIn?: (event: NativeSyntheticEvent<WaterViewEventData>) => void;
|
|
54
54
|
onPressOut?: (event: NativeSyntheticEvent<WaterViewEventData>) => void;
|
|
55
55
|
} & React.RefAttributes<WaterViewRef>>;
|
|
56
|
-
export declare const
|
|
56
|
+
export declare const ReactWaterView: React.FC<WaterViewProps> & {
|
|
57
57
|
Effect: typeof WaterViewEffect;
|
|
58
58
|
};
|
|
59
59
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactWaterView.d.ts","sourceRoot":"","sources":["../src/ReactWaterView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAmD,MAAM,OAAO,CAAA;AACvE,OAAO,EAAE,oBAAoB,EAAgC,SAAS,EAAE,MAAM,cAAc,CAAA;AAM5F,MAAM,MAAM,kBAAkB,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AACzD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAA;AAE5C,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAClE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACrE,CAAA;AAGD,KAAK,WAAW,GAAG,SAAS,GAAG;IAC7B,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACxC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAA;IAClE,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAA;IACrE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAA;CACvE,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG,WAAW,CAAA;AAE9C,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,CAAA;AAOvE,QAAA,MAAM,eAAe;WA1BZ;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;cAC9B,MAAM;uBACG,OAAO;mBACX,YAAY;aAClB,OAAO;aACP,OAAO;WACT,MAAM;WACN,MAAM;WACN,MAAM;WACN,MAAM;qBACI,MAAM;qBACN,MAAM;aACd,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI;gBACtD,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI;iBACxD,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI;sCA6BvE,CAAA;AAqDD,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"ReactWaterView.d.ts","sourceRoot":"","sources":["../src/ReactWaterView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAmD,MAAM,OAAO,CAAA;AACvE,OAAO,EAAE,oBAAoB,EAAgC,SAAS,EAAE,MAAM,cAAc,CAAA;AAM5F,MAAM,MAAM,kBAAkB,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AACzD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAA;AAE5C,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAClE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACrE,CAAA;AAGD,KAAK,WAAW,GAAG,SAAS,GAAG;IAC7B,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACxC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAA;IAClE,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAA;IACrE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAA;CACvE,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG,WAAW,CAAA;AAE9C,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,CAAA;AAOvE,QAAA,MAAM,eAAe;WA1BZ;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;cAC9B,MAAM;uBACG,OAAO;mBACX,YAAY;aAClB,OAAO;aACP,OAAO;WACT,MAAM;WACN,MAAM;WACN,MAAM;WACN,MAAM;qBACI,MAAM;qBACN,MAAM;aACd,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI;gBACtD,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI;iBACxD,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI;sCA6BvE,CAAA;AAqDD,eAAO,MAAM,cAAc,EAAoB,KAAK,CAAC,EAAE,CAAC,cAAc,CAAC,GAAG;IACxE,MAAM,EAAE,OAAO,eAAe,CAAA;CAC/B,CAAA"}
|
package/build/ReactWaterView.js
CHANGED
|
@@ -47,5 +47,5 @@ const WaterViewRoot = ({ children, style, ...props }) => {
|
|
|
47
47
|
WaterViewRoot.displayName = 'WaterView';
|
|
48
48
|
WaterViewRoot.Effect = WaterViewEffect;
|
|
49
49
|
// 导出
|
|
50
|
-
export const
|
|
50
|
+
export const ReactWaterView = WaterViewRoot;
|
|
51
51
|
//# sourceMappingURL=ReactWaterView.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactWaterView.js","sourceRoot":"","sources":["../src/ReactWaterView.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ReactWaterView.js","sourceRoot":"","sources":["../src/ReactWaterView.tsx"],"names":[],"mappings":"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","sourcesContent":["import { requireNativeViewManager } from 'expo-modules-core'\nimport React, { isValidElement, cloneElement, forwardRef } from 'react'\nimport { NativeSyntheticEvent, Platform, processColor, View, ViewProps } from 'react-native'\n\n// =============================================================================\n// 1. 类型定义\n// =============================================================================\n\nexport type WaterViewEventData = { x: number; y: number }\nexport type RestrictMode = 'edge' | 'center'\n\nexport type WaterViewRef = {\n moveTo: (x: number, y: number, duration?: number) => Promise<void>\n moveBy: (dx: number, dy: number, duration?: number) => Promise<void>\n}\n\n// Native Props\ntype NativeProps = ViewProps & {\n size?: { width: number; height: number }\n bgColor?: string\n shouldSpringBack?: boolean\n restrictMode?: RestrictMode\n allowX?: boolean\n allowY?: boolean\n minX?: number\n maxX?: number\n minY?: number\n maxY?: number\n initialOffsetX?: number\n initialOffsetY?: number\n onMove?: (event: NativeSyntheticEvent<WaterViewEventData>) => void\n onPressIn?: (event: NativeSyntheticEvent<WaterViewEventData>) => void\n onPressOut?: (event: NativeSyntheticEvent<WaterViewEventData>) => void\n}\n\nexport type WaterViewEffectProps = NativeProps\n\nexport type WaterViewProps = ViewProps & { children?: React.ReactNode }\n\nconst NativeComponent = requireNativeViewManager('WaterViewModule', 'ReactWaterView')\n\n// =============================================================================\n// 2. 子组件: WaterView.Effect\n// =============================================================================\nconst WaterViewEffect = forwardRef<WaterViewRef, WaterViewEffectProps>(\n ({ bgColor, children, ...props }, ref) => {\n const nativeRef = React.useRef<any>(null)\n\n React.useImperativeHandle(ref, () => ({\n moveBy: async (dx, dy, duration) => nativeRef.current?.moveBy(dx, dy, duration),\n moveTo: async (x, y, duration) => nativeRef.current?.moveTo(x, y, duration),\n }))\n\n const nativeProps = { ...props, bgColor: processColor(bgColor), collapsable: false }\n\n return (\n <NativeComponent ref={nativeRef} {...nativeProps} collapsable={false}>\n {children}\n </NativeComponent>\n )\n }\n)\nWaterViewEffect.displayName = 'WaterView.Effect'\n\n// =============================================================================\n// 3. 父组件: WaterView (容器)\n// =============================================================================\n\nconst WaterViewRoot = ({ children, style, ...props }: WaterViewProps) => {\n // [修复]: 使用 React.Children.toArray + find,而不是 forEach 回调\n // 这样 TypeScript 可以正确推断 effectNode 不为 null/never\n const childrenArray = React.Children.toArray(children)\n\n const effectNode = childrenArray.find(\n (child): child is React.ReactElement<WaterViewEffectProps> =>\n isValidElement(child) && child.type === WaterViewEffect\n )\n\n const contentChildren = childrenArray.filter((child) => child !== effectNode)\n\n if (!effectNode) {\n console.warn('<WaterView> requires a <WaterView.Effect> child to work correctly.')\n return (\n <View style={style} {...props} collapsable={false}>\n {contentChildren}\n </View>\n )\n }\n\n // Android 渲染逻辑\n if (Platform.OS === 'android') {\n return cloneElement(\n effectNode,\n { ...effectNode.props },\n <View style={style} {...props} collapsable={false}>\n {contentChildren}\n </View>\n )\n }\n\n // iOS 渲染逻辑\n return (\n <View style={style} {...props} collapsable={false}>\n {contentChildren}\n {cloneElement(effectNode, { style: effectNode.props.style })}\n </View>\n )\n}\n\n// 挂载静态属性\n;(WaterViewRoot as any).displayName = 'WaterView'\n;(WaterViewRoot as any).Effect = WaterViewEffect\n\n// 导出\nexport const ReactWaterView = WaterViewRoot as React.FC<WaterViewProps> & {\n Effect: typeof WaterViewEffect\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "golia-expo-utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "expo utils provided by golia.jp",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"homepage": "https://expo.golia.jp/golia-expo-utils",
|
|
@@ -19,23 +19,32 @@
|
|
|
19
19
|
},
|
|
20
20
|
"keywords": ["react-native", "expo", "golia-expo-utils", "GoliaExpoUtils"],
|
|
21
21
|
"repository": "https://github.com/goliajp/golia-expo-utils",
|
|
22
|
-
"bugs": {
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/goliajp/golia-expo-utils/issues"
|
|
24
|
+
},
|
|
25
|
+
"author": "doracawl <lihao@golia.jp> (doracawl)",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"fs-extra": "^11.3.3",
|
|
29
|
+
"svg2vectordrawable": "^2.9.1"
|
|
30
|
+
},
|
|
31
|
+
"app.plugin": "./plugin/build/index.js",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/fs-extra": "^11.0.4",
|
|
34
|
+
"@types/node": "^25.0.3",
|
|
35
|
+
"@types/react": "~19.1.0",
|
|
36
|
+
"eslint-config-expo": "^10.0.0",
|
|
37
|
+
"eslint-plugin-perfectionist": "^5.3.1",
|
|
38
|
+
"eslint-plugin-react": "^7.37.5",
|
|
39
|
+
"eslint-plugin-unused-imports": "^4.3.0",
|
|
40
|
+
"expo": "^54.0.31",
|
|
41
|
+
"expo-module-scripts": "^5.0.8",
|
|
42
|
+
"prettier": "^3.7.4",
|
|
43
|
+
"react-native": "0.81.5"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"expo": "*",
|
|
47
|
+
"react": "*",
|
|
48
|
+
"react-native": "*"
|
|
49
|
+
}
|
|
41
50
|
}
|
package/src/ReactWaterView.tsx
CHANGED
|
@@ -112,6 +112,6 @@ const WaterViewRoot = ({ children, style, ...props }: WaterViewProps) => {
|
|
|
112
112
|
;(WaterViewRoot as any).Effect = WaterViewEffect
|
|
113
113
|
|
|
114
114
|
// 导出
|
|
115
|
-
export const
|
|
115
|
+
export const ReactWaterView = WaterViewRoot as React.FC<WaterViewProps> & {
|
|
116
116
|
Effect: typeof WaterViewEffect
|
|
117
117
|
}
|