golia-expo-utils 1.0.0 → 1.0.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.
@@ -57,10 +57,15 @@ android {
57
57
  }
58
58
 
59
59
  dependencies {
60
- def composeBom = platform('androidx.compose:compose-bom:2025.12.01')
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
  }
@@ -2,11 +2,18 @@ package expo.modules.goliaexpoutils.segmentedControl
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
+ import android.content.ContextWrapper
6
+ import androidx.appcompat.app.AppCompatActivity
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
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
10
17
  import expo.modules.goliaexpoutils.Config
11
18
  import expo.modules.kotlin.AppContext
12
19
  import expo.modules.kotlin.viewevent.EventDispatcher
@@ -27,30 +34,12 @@ class ReactSegmentedControl(context: Context, appContext: AppContext) :
27
34
  val hapticEnabledState = mutableStateOf(true)
28
35
  val enabledState = mutableStateOf(true)
29
36
 
37
+ private var isContentSet = false
38
+
30
39
  private val composeView = ComposeView(context).apply {
31
40
  layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
32
- clipChildren = false
33
- clipToPadding = false
34
-
35
- setContent {
36
- SegmentedControl(
37
- items = itemsState.value,
38
- selectedIndex = selectedIndexState.intValue,
39
- onValueChange = { index ->
40
- selectedIndexState.intValue = index
41
- val title = itemsState.value.getOrNull(index)?.title ?: ""
42
- onValueChange(mapOf("value" to title, "index" to index))
43
- },
44
- modifier = Modifier.fillMaxSize(),
45
- activeColor = activeColorState.value,
46
- ctrlBackgroundColor = ctrlBgColorState.value,
47
- textColor = textColorState.value,
48
- autoWidth = autoWidthState.value,
49
- hapticEnabled = hapticEnabledState.value,
50
- enabled = enabledState.value,
51
- glassExpansion = Config.Common.Glass.DEFAULT_EXPANSION,
52
- )
53
- }
41
+ // 确保 View 卸载时清理 Compose 资源
42
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
54
43
  }
55
44
 
56
45
  init {
@@ -58,4 +47,55 @@ class ReactSegmentedControl(context: Context, appContext: AppContext) :
58
47
  clipToPadding = false
59
48
  addView(composeView)
60
49
  }
50
+
51
+ override fun onAttachedToWindow() {
52
+ super.onAttachedToWindow()
53
+
54
+ // ✅ 核心修复:查找 Activity 并强行注入 Lifecycle
55
+ val activity = context.lookupActivity()
56
+ if (activity != null) {
57
+ if (composeView.findViewTreeLifecycleOwner() == null) {
58
+ composeView.setViewTreeLifecycleOwner(activity)
59
+ }
60
+ if (composeView.findViewTreeSavedStateRegistryOwner() == null) {
61
+ composeView.setViewTreeSavedStateRegistryOwner(activity)
62
+ }
63
+ }
64
+
65
+ if (!isContentSet) {
66
+ composeView.setContent {
67
+ SegmentedControl(
68
+ items = itemsState.value,
69
+ selectedIndex = selectedIndexState.intValue,
70
+ onValueChange = { index ->
71
+ selectedIndexState.intValue = index
72
+ val title = itemsState.value.getOrNull(index)?.title ?: ""
73
+ onValueChange(mapOf("value" to title, "index" to index))
74
+ },
75
+ modifier = Modifier.fillMaxSize(),
76
+ activeColor = activeColorState.value,
77
+ ctrlBackgroundColor = ctrlBgColorState.value,
78
+ textColor = textColorState.value,
79
+ autoWidth = autoWidthState.value,
80
+ hapticEnabled = hapticEnabledState.value,
81
+ enabled = enabledState.value,
82
+ glassExpansion = Config.Common.Glass.DEFAULT_EXPANSION,
83
+ )
84
+ }
85
+ isContentSet = true
86
+ }
87
+ }
88
+ }
89
+
90
+ // ✅ 辅助扩展函数:从 ContextWrapper 中剥离出 Activity
91
+ // (虽然代码重复了,但这样解耦,避免跨包依赖问题)
92
+ private fun Context.lookupActivity(): AppCompatActivity? {
93
+ var ctx = this
94
+ while (ctx is ContextWrapper) {
95
+ if (ctx is AppCompatActivity) {
96
+ return ctx
97
+ }
98
+ ctx = ctx.baseContext
99
+ }
100
+ return null
61
101
  }
@@ -2,9 +2,11 @@ package expo.modules.goliaexpoutils.waterView
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
+ import android.content.ContextWrapper
5
6
  import android.view.View
6
7
  import android.view.ViewGroup
7
8
  import android.widget.FrameLayout
9
+ import androidx.appcompat.app.AppCompatActivity
8
10
  import androidx.compose.foundation.layout.Box
9
11
  import androidx.compose.foundation.layout.fillMaxSize
10
12
  import androidx.compose.runtime.Composable
@@ -17,6 +19,10 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
17
19
  import androidx.compose.ui.unit.dp
18
20
  import androidx.compose.ui.viewinterop.AndroidView
19
21
  import androidx.core.view.isNotEmpty
22
+ import androidx.lifecycle.findViewTreeLifecycleOwner
23
+ import androidx.lifecycle.setViewTreeLifecycleOwner
24
+ import androidx.savedstate.findViewTreeSavedStateRegistryOwner
25
+ import androidx.savedstate.setViewTreeSavedStateRegistryOwner
20
26
  import com.kyant.backdrop.backdrops.layerBackdrop
21
27
  import com.kyant.backdrop.backdrops.rememberLayerBackdrop
22
28
  import expo.modules.goliaexpoutils.Config
@@ -53,29 +59,14 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
53
59
  private val onPressOut by EventDispatcher()
54
60
  private val commandChannel = Channel<WaterCommand>(Channel.BUFFERED)
55
61
  private val childContainer = FrameLayout(context).apply {
56
- layoutParams = ViewGroup.LayoutParams(
57
- LayoutParams.MATCH_PARENT,
58
- LayoutParams.MATCH_PARENT
59
- )
62
+ layoutParams = ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
60
63
  }
64
+
65
+ private var isContentSet = false
66
+
61
67
  private val composeView = ComposeView(context).apply {
62
- layoutParams = ViewGroup.LayoutParams(
63
- LayoutParams.MATCH_PARENT,
64
- LayoutParams.MATCH_PARENT
65
- )
66
- clipChildren = false
67
- clipToPadding = false
68
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindowOrReleasedFromPool)
69
- setContent {
70
- WaterViewEntry(
71
- childContainer = childContainer,
72
- props = props,
73
- commandFlow = commandChannel.receiveAsFlow(),
74
- onMoveDispatch = { x, y -> onMove(mapOf("x" to x, "y" to y)) },
75
- onPressInDispatch = { onPressIn(mapOf()) },
76
- onPressOutDispatch = { onPressOut(mapOf()) }
77
- )
78
- }
68
+ layoutParams = ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
69
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
79
70
  }
80
71
 
81
72
  init {
@@ -84,6 +75,34 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
84
75
  addView(composeView)
85
76
  }
86
77
 
78
+ override fun onAttachedToWindow() {
79
+ super.onAttachedToWindow()
80
+
81
+ val activity = context.lookupActivity()
82
+ if (activity != null) {
83
+ if (composeView.findViewTreeLifecycleOwner() == null) {
84
+ composeView.setViewTreeLifecycleOwner(activity)
85
+ }
86
+ if (composeView.findViewTreeSavedStateRegistryOwner() == null) {
87
+ composeView.setViewTreeSavedStateRegistryOwner(activity)
88
+ }
89
+ }
90
+
91
+ if (!isContentSet) {
92
+ composeView.setContent {
93
+ WaterViewEntry(
94
+ childContainer = childContainer,
95
+ props = props,
96
+ commandFlow = commandChannel.receiveAsFlow(),
97
+ onMoveDispatch = { x, y -> onMove(mapOf("x" to x, "y" to y)) },
98
+ onPressInDispatch = { onPressIn(mapOf()) },
99
+ onPressOutDispatch = { onPressOut(mapOf()) }
100
+ )
101
+ }
102
+ isContentSet = true
103
+ }
104
+ }
105
+
87
106
  override fun addView(child: View?, index: Int) {
88
107
  if (child == composeView) super.addView(child, index)
89
108
  else childContainer.addView(child)
@@ -111,6 +130,18 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
111
130
  }
112
131
  }
113
132
 
133
+ // ✅ 辅助扩展函数:从 ContextWrapper 中剥离出 Activity
134
+ private fun Context.lookupActivity(): AppCompatActivity? {
135
+ var ctx = this
136
+ while (ctx is ContextWrapper) {
137
+ if (ctx is AppCompatActivity) {
138
+ return ctx
139
+ }
140
+ ctx = ctx.baseContext
141
+ }
142
+ return null
143
+ }
144
+
114
145
  @Composable
115
146
  private fun WaterViewEntry(
116
147
  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 WaterView: React.FC<WaterViewProps> & {
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,SAAS,EAAoB,KAAK,CAAC,EAAE,CAAC,cAAc,CAAC,GAAG;IACnE,MAAM,EAAE,OAAO,eAAe,CAAA;CAC/B,CAAA"}
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"}
@@ -47,5 +47,5 @@ const WaterViewRoot = ({ children, style, ...props }) => {
47
47
  WaterViewRoot.displayName = 'WaterView';
48
48
  WaterViewRoot.Effect = WaterViewEffect;
49
49
  // 导出
50
- export const WaterView = WaterViewRoot;
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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","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 WaterView = WaterViewRoot as React.FC<WaterViewProps> & {\n Effect: typeof WaterViewEffect\n}\n"]}
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.0",
3
+ "version": "1.0.2",
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": { "url": "https://github.com/goliajp/golia-expo-utils/issues" },
23
- "author": "doracawl <lihao@golia.jp> (doracawl)",
24
- "license": "MIT",
25
- "dependencies": { "fs-extra": "^11.3.3", "svg2vectordrawable": "^2.9.1" },
26
- "app.plugin": "./plugin/build/index.js",
27
- "devDependencies": {
28
- "@types/fs-extra": "^11.0.4",
29
- "@types/node": "^25.0.3",
30
- "@types/react": "~19.1.0",
31
- "eslint-config-expo": "^10.0.0",
32
- "eslint-plugin-perfectionist": "^5.3.1",
33
- "eslint-plugin-react": "^7.37.5",
34
- "eslint-plugin-unused-imports": "^4.3.0",
35
- "expo": "^54.0.31",
36
- "expo-module-scripts": "^5.0.8",
37
- "prettier": "^3.7.4",
38
- "react-native": "0.81.5"
39
- },
40
- "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" }
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
  }
@@ -112,6 +112,6 @@ const WaterViewRoot = ({ children, style, ...props }: WaterViewProps) => {
112
112
  ;(WaterViewRoot as any).Effect = WaterViewEffect
113
113
 
114
114
  // 导出
115
- export const WaterView = WaterViewRoot as React.FC<WaterViewProps> & {
115
+ export const ReactWaterView = WaterViewRoot as React.FC<WaterViewProps> & {
116
116
  Effect: typeof WaterViewEffect
117
117
  }