@mpxjs/core 2.10.4-beta.2 → 2.10.4-beta.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/package.json
CHANGED
|
@@ -7,7 +7,7 @@ import Mpx from '../index'
|
|
|
7
7
|
import { createElement, memo, useRef, useEffect } from 'react'
|
|
8
8
|
import * as ReactNative from 'react-native'
|
|
9
9
|
import { initAppProvides } from './export/inject'
|
|
10
|
-
import { NavigationContainer,
|
|
10
|
+
import { NavigationContainer, createNativeStackNavigator, SafeAreaProvider } from './env/navigationHelper'
|
|
11
11
|
|
|
12
12
|
const appHooksMap = makeMap(mergeLifecycle(LIFECYCLE).app)
|
|
13
13
|
|
|
@@ -52,25 +52,25 @@ export default function createApp (options) {
|
|
|
52
52
|
|
|
53
53
|
const pages = currentInject.getPages() || {}
|
|
54
54
|
const firstPage = currentInject.firstPage
|
|
55
|
-
const Stack =
|
|
55
|
+
const Stack = createNativeStackNavigator()
|
|
56
56
|
const getPageScreens = (initialRouteName, initialParams) => {
|
|
57
57
|
return Object.entries(pages).map(([key, item]) => {
|
|
58
|
-
const options = {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
58
|
+
// const options = {
|
|
59
|
+
// // __mpxPageStatusMap 为编译注入的全局变量
|
|
60
|
+
// headerShown: !(Object.assign({}, global.__mpxPageConfig, global.__mpxPageConfigsMap[key]).navigationStyle === 'custom')
|
|
61
|
+
// }
|
|
62
62
|
if (key === initialRouteName) {
|
|
63
63
|
return createElement(Stack.Screen, {
|
|
64
64
|
name: key,
|
|
65
65
|
component: item,
|
|
66
|
-
initialParams
|
|
67
|
-
options
|
|
66
|
+
initialParams
|
|
67
|
+
// options
|
|
68
68
|
})
|
|
69
69
|
}
|
|
70
70
|
return createElement(Stack.Screen, {
|
|
71
71
|
name: key,
|
|
72
|
-
component: item
|
|
73
|
-
options
|
|
72
|
+
component: item
|
|
73
|
+
// options
|
|
74
74
|
})
|
|
75
75
|
})
|
|
76
76
|
}
|
|
@@ -195,32 +195,10 @@ export default function createApp (options) {
|
|
|
195
195
|
|
|
196
196
|
const { initialRouteName, initialParams } = initialRouteRef.current
|
|
197
197
|
const navScreenOpts = {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
// 整体切换native-stack时进行修改如下
|
|
203
|
-
// statusBarTranslucent: true,
|
|
204
|
-
// statusBarBackgroundColor: 'transparent'
|
|
205
|
-
}
|
|
206
|
-
if (__mpx_mode__ === 'ios') {
|
|
207
|
-
// ios使用native-stack
|
|
208
|
-
const headerBackImageSource = Mpx.config.rnConfig.headerBackImageSource || null
|
|
209
|
-
if (headerBackImageSource) {
|
|
210
|
-
navScreenOpts.headerBackImageSource = headerBackImageSource
|
|
211
|
-
}
|
|
212
|
-
} else {
|
|
213
|
-
// 安卓上会出现导航条闪现的问题所以默认加headerShown false(stack版本, native-stack版本可以干掉)
|
|
214
|
-
// iOS加上默认headerShown false的话会因为iOS根高度是screenHeight - useHeaderHeight()会导致出现渲染两次情况,因此iOS不加此默认值
|
|
215
|
-
navScreenOpts.headerShown = false
|
|
216
|
-
// 安卓和鸿蒙先用stack
|
|
217
|
-
const headerBackImageProps = Mpx.config.rnConfig.headerBackImageProps || null
|
|
218
|
-
if (headerBackImageProps) {
|
|
219
|
-
navScreenOpts.headerBackImage = () => {
|
|
220
|
-
return createElement(ReactNative.Image, headerBackImageProps)
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
198
|
+
headerShown: false,
|
|
199
|
+
statusBarTranslucent: true,
|
|
200
|
+
statusBarBackgroundColor: 'transparent'
|
|
201
|
+
}
|
|
224
202
|
|
|
225
203
|
return createElement(SafeAreaProvider,
|
|
226
204
|
null,
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { createElement, useState, useMemo } from 'react'
|
|
2
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
3
|
+
import * as ReactNative from 'react-native'
|
|
4
|
+
import Mpx from '../../index'
|
|
5
|
+
|
|
6
|
+
export function useInnerHeaderHeight (pageconfig) {
|
|
7
|
+
if (pageconfig.navigationStyle === 'custom') {
|
|
8
|
+
return 0
|
|
9
|
+
} else {
|
|
10
|
+
const safeAreaTop = useSafeAreaInsets()?.top || 0
|
|
11
|
+
const headerHeight = safeAreaTop + getTitleHeight()
|
|
12
|
+
return headerHeight
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 固定写死高度
|
|
17
|
+
function getTitleHeight () {
|
|
18
|
+
return 44
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 计算颜色亮度
|
|
22
|
+
const getColorBrightness = (color) => {
|
|
23
|
+
const processedColor = ReactNative.processColor(color)
|
|
24
|
+
if (typeof processedColor === 'number') {
|
|
25
|
+
const r = (processedColor >> 16) & 255
|
|
26
|
+
const g = (processedColor >> 8) & 255
|
|
27
|
+
const b = processedColor & 255
|
|
28
|
+
return (r * 299 + g * 587 + b * 114) / 1000
|
|
29
|
+
}
|
|
30
|
+
return 0
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const styles = ReactNative.StyleSheet.create({
|
|
34
|
+
header: {
|
|
35
|
+
elevation: 3
|
|
36
|
+
},
|
|
37
|
+
headerContent: {
|
|
38
|
+
flexDirection: 'row',
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
justifyContent: 'center'
|
|
41
|
+
},
|
|
42
|
+
backButton: {
|
|
43
|
+
position: 'absolute',
|
|
44
|
+
height: '100%',
|
|
45
|
+
width: 40,
|
|
46
|
+
left: 0,
|
|
47
|
+
top: 0,
|
|
48
|
+
alignItems: 'center',
|
|
49
|
+
justifyContent: 'center'
|
|
50
|
+
},
|
|
51
|
+
backButtonImage: {
|
|
52
|
+
width: 22,
|
|
53
|
+
height: 22
|
|
54
|
+
},
|
|
55
|
+
title: {
|
|
56
|
+
fontSize: 17,
|
|
57
|
+
fontWeight: 600
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
export function innerNav ({ props, navigation }) {
|
|
62
|
+
const { pageConfig } = props
|
|
63
|
+
const [innerPageConfig, setPageConfig] = useState(pageConfig || {})
|
|
64
|
+
navigation.setPageConfig = (config) => {
|
|
65
|
+
const newConfig = Object.assign({}, innerPageConfig, config)
|
|
66
|
+
setPageConfig(newConfig)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const isCustom = innerPageConfig.navigationStyle === 'custom'
|
|
70
|
+
if (isCustom) return null
|
|
71
|
+
const safeAreaTop = useSafeAreaInsets()?.top || 0
|
|
72
|
+
|
|
73
|
+
// 回退按钮的颜色,根据背景色的亮度来进行调节
|
|
74
|
+
const backButtonColor = useMemo(() => {
|
|
75
|
+
return getColorBrightness(innerPageConfig.navigationBarBackgroundColor) > 128 ? '#000000' : '#ffffff'
|
|
76
|
+
}, [innerPageConfig.navigationBarBackgroundColor])
|
|
77
|
+
|
|
78
|
+
// 假设是栈导航,获取栈的长度
|
|
79
|
+
const stackLength = navigation.getState()?.routes?.length
|
|
80
|
+
// 用于外部注册打开RN容器之前的栈长度
|
|
81
|
+
const beforeStackLength = Mpx.config?.rnConfig?.beforeStackLength || 0
|
|
82
|
+
|
|
83
|
+
// 回退按钮与图标
|
|
84
|
+
const backElement = stackLength + beforeStackLength > 1
|
|
85
|
+
? createElement(ReactNative.TouchableOpacity, {
|
|
86
|
+
style: [styles.backButton],
|
|
87
|
+
onPress: () => { navigation.goBack() }
|
|
88
|
+
}, createElement(ReactNative.Image, {
|
|
89
|
+
source: { uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAABICAYAAACqT5alAAAA2UlEQVR4nO3bMQrCUBRE0Yla6AYEN2nnBrTL+izcitW3MRDkEUWSvPzJvfCqgMwhZbAppWhNbbIHzB1g9wATERFRVyvpkj1irlpJ5X326D7WHh1hbdFD2CLpLmmftm7kfsEe09aNHFiBrT+wAlt/YAW2/sAKbP2BFdj6Ayuwy+ufz6XPL893krZ//O6iu2n4LT8kndLWTRTo4EC7BDo40C6BDg60S6CDA+0S6OBAuwQ6uNWiD2nrJmoIfU7cNWkR2hbb1UfbY7uuWhGWiIg+a/iHuHmA3QPs3gu4JW9Gan+OJAAAAABJRU5ErkJggg==' },
|
|
90
|
+
style: [styles.backButtonImage, { tintColor: backButtonColor }]
|
|
91
|
+
}))
|
|
92
|
+
: null
|
|
93
|
+
|
|
94
|
+
return createElement(ReactNative.View, {
|
|
95
|
+
style: [styles.header, {
|
|
96
|
+
paddingTop: safeAreaTop,
|
|
97
|
+
backgroundColor: innerPageConfig.navigationBarBackgroundColor || '#000000'
|
|
98
|
+
}]
|
|
99
|
+
},
|
|
100
|
+
createElement(ReactNative.View, {
|
|
101
|
+
style: styles.headerContent,
|
|
102
|
+
height: getTitleHeight()
|
|
103
|
+
}, backElement,
|
|
104
|
+
createElement(ReactNative.Text, {
|
|
105
|
+
style: [styles.title, { color: innerPageConfig.navigationBarTextStyle || 'white' }]
|
|
106
|
+
}, innerPageConfig.navigationBarTitleText?.trim() || ''))
|
|
107
|
+
)
|
|
108
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
|
2
2
|
import { NavigationContainer, StackActions } from '@react-navigation/native'
|
|
3
3
|
import PortalHost from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/portal-host'
|
|
4
4
|
import { useHeaderHeight } from '@react-navigation/elements'
|
|
@@ -6,7 +6,7 @@ import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-cont
|
|
|
6
6
|
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
|
7
7
|
|
|
8
8
|
export {
|
|
9
|
-
|
|
9
|
+
createNativeStackNavigator,
|
|
10
10
|
NavigationContainer,
|
|
11
11
|
useHeaderHeight,
|
|
12
12
|
StackActions,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createNativeStackNavigator
|
|
1
|
+
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
|
2
2
|
import { NavigationContainer, StackActions } from '@react-navigation/native'
|
|
3
3
|
import PortalHost from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/portal-host'
|
|
4
4
|
import { useHeaderHeight } from '@react-navigation/elements'
|
|
@@ -6,7 +6,7 @@ import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-cont
|
|
|
6
6
|
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
|
7
7
|
|
|
8
8
|
export {
|
|
9
|
-
|
|
9
|
+
createNativeStackNavigator,
|
|
10
10
|
NavigationContainer,
|
|
11
11
|
useHeaderHeight,
|
|
12
12
|
StackActions,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect,
|
|
1
|
+
import { useEffect, useSyncExternalStore, useRef, useMemo, createElement, memo, forwardRef, useImperativeHandle, useContext, Fragment, cloneElement, createContext } from 'react'
|
|
2
2
|
import * as ReactNative from 'react-native'
|
|
3
3
|
import { ReactiveEffect } from '../../observer/effect'
|
|
4
4
|
import { watch } from '../../observer/watch'
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
KeyboardAvoidContext,
|
|
16
16
|
RouteContext
|
|
17
17
|
} from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/context'
|
|
18
|
-
import { PortalHost, useSafeAreaInsets, GestureHandlerRootView
|
|
18
|
+
import { PortalHost, useSafeAreaInsets, GestureHandlerRootView } from '../env/navigationHelper'
|
|
19
|
+
import { innerNav, useInnerHeaderHeight } from '../env/nav'
|
|
19
20
|
|
|
20
21
|
const ProviderContext = createContext(null)
|
|
21
22
|
function getSystemInfo () {
|
|
@@ -445,9 +446,28 @@ const checkRelation = (options) => {
|
|
|
445
446
|
hasAncestorRelation
|
|
446
447
|
}
|
|
447
448
|
}
|
|
449
|
+
function getLayoutData (headerHeight) {
|
|
450
|
+
const screenDimensions = ReactNative.Dimensions.get('screen')
|
|
451
|
+
const windowDimensions = ReactNative.Dimensions.get('window')
|
|
452
|
+
// 在横屏状态下 screen.height = window.height + bottomVirtualHeight
|
|
453
|
+
// 在正常状态 screen.height = window.height + bottomVirtualHeight + statusBarHeight
|
|
454
|
+
const isLandscape = screenDimensions.height < screenDimensions.width
|
|
455
|
+
const bottomVirtualHeight = isLandscape ? screenDimensions.height - windowDimensions.height : ((screenDimensions.height - windowDimensions.height - ReactNative.StatusBar.currentHeight) || 0)
|
|
456
|
+
return {
|
|
457
|
+
x: 0,
|
|
458
|
+
y: headerHeight,
|
|
459
|
+
left: 0,
|
|
460
|
+
top: headerHeight,
|
|
461
|
+
// 此处必须为windowDimensions.width,在横屏状态下windowDimensions.width才符合预期
|
|
462
|
+
width: windowDimensions.width,
|
|
463
|
+
height: screenDimensions.height - headerHeight - bottomVirtualHeight,
|
|
464
|
+
// ios为0 android为实际statusbar高度
|
|
465
|
+
statusBarHeight: ReactNative.StatusBar.currentHeight || 0,
|
|
466
|
+
bottomVirtualHeight: bottomVirtualHeight,
|
|
467
|
+
isLandscape: isLandscape
|
|
468
|
+
}
|
|
469
|
+
}
|
|
448
470
|
|
|
449
|
-
// 临时用来存储安卓底部(iOS没有这个)的高度(虚拟按键等高度)根据第一次进入推算
|
|
450
|
-
let bottomVirtualHeight = null
|
|
451
471
|
export function PageWrapperHOC (WrappedComponent) {
|
|
452
472
|
return function PageWrapperCom ({ navigation, route, pageConfig = {}, ...props }) {
|
|
453
473
|
const rootRef = useRef(null)
|
|
@@ -464,59 +484,15 @@ export function PageWrapperHOC (WrappedComponent) {
|
|
|
464
484
|
error('Using pageWrapper requires passing navigation and route')
|
|
465
485
|
return null
|
|
466
486
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
navigation.setOptions({
|
|
470
|
-
title: currentPageConfig.navigationBarTitleText?.trim() || '',
|
|
471
|
-
headerStyle: {
|
|
472
|
-
backgroundColor: currentPageConfig.navigationBarBackgroundColor || '#000000'
|
|
473
|
-
},
|
|
474
|
-
headerTintColor: currentPageConfig.navigationBarTextStyle || 'white'
|
|
475
|
-
})
|
|
476
|
-
|
|
477
|
-
// TODO 此部分内容在native-stack可删除,用setOptions设置
|
|
478
|
-
if (__mpx_mode__ !== 'ios') {
|
|
479
|
-
ReactNative.StatusBar.setBarStyle(currentPageConfig.barStyle || 'dark-content')
|
|
480
|
-
ReactNative.StatusBar.setTranslucent(true) // 控制statusbar是否占位
|
|
481
|
-
ReactNative.StatusBar.setBackgroundColor('transparent')
|
|
482
|
-
}
|
|
483
|
-
}, [])
|
|
484
|
-
|
|
485
|
-
const headerHeight = useHeaderHeight()
|
|
487
|
+
const headerHeight = useInnerHeaderHeight(currentPageConfig)
|
|
488
|
+
navigation.layout = getLayoutData(headerHeight)
|
|
486
489
|
const onLayout = () => {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
navigation.layout = {
|
|
490
|
-
x: 0,
|
|
491
|
-
y: headerHeight,
|
|
492
|
-
width: screenDimensions.width,
|
|
493
|
-
height: screenDimensions.height - headerHeight
|
|
494
|
-
}
|
|
495
|
-
} else {
|
|
496
|
-
if (bottomVirtualHeight === null) {
|
|
497
|
-
rootRef.current?.measureInWindow((x, y, width, height) => {
|
|
498
|
-
// 沉浸模式的计算方式
|
|
499
|
-
bottomVirtualHeight = screenDimensions.height - height - headerHeight
|
|
500
|
-
// 非沉浸模式(translucent=true)计算方式, 现在默认是全用沉浸模式,所以先不算这个
|
|
501
|
-
// bottomVirtualHeight = windowDimensions.height - height - headerHeight
|
|
502
|
-
navigation.layout = {
|
|
503
|
-
x: 0,
|
|
504
|
-
y: headerHeight,
|
|
505
|
-
width: screenDimensions.width,
|
|
506
|
-
height: height
|
|
507
|
-
}
|
|
508
|
-
})
|
|
509
|
-
} else {
|
|
510
|
-
navigation.layout = {
|
|
511
|
-
x: 0,
|
|
512
|
-
y: headerHeight, // 这个y值
|
|
513
|
-
width: screenDimensions.width,
|
|
514
|
-
// 后续页面的layout是通过第一次路由进入时候推算出来的底部区域来推算出来的
|
|
515
|
-
height: screenDimensions.height - bottomVirtualHeight - headerHeight
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
490
|
+
// 当用户处于横屏或者竖屏状态的时候,需要进行layout修正
|
|
491
|
+
navigation.layout = getLayoutData(headerHeight)
|
|
519
492
|
}
|
|
493
|
+
|
|
494
|
+
usePageStatus(navigation, currentPageId)
|
|
495
|
+
|
|
520
496
|
const withKeyboardAvoidingView = (element) => {
|
|
521
497
|
return createElement(KeyboardAvoidContext.Provider,
|
|
522
498
|
{
|
|
@@ -535,26 +511,26 @@ export function PageWrapperHOC (WrappedComponent) {
|
|
|
535
511
|
)
|
|
536
512
|
)
|
|
537
513
|
}
|
|
538
|
-
|
|
514
|
+
// android存在第一次打开insets都返回为0情况,后续会触发第二次渲染后正确
|
|
539
515
|
navigation.insets = useSafeAreaInsets()
|
|
540
|
-
|
|
541
516
|
return createElement(GestureHandlerRootView,
|
|
542
517
|
{
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
height: ReactNative.Dimensions.get('screen').height - useHeaderHeight()
|
|
547
|
-
}
|
|
548
|
-
: {
|
|
549
|
-
flex: 1
|
|
550
|
-
}
|
|
518
|
+
style: {
|
|
519
|
+
flex: 1
|
|
520
|
+
}
|
|
551
521
|
},
|
|
522
|
+
createElement(innerNav, {
|
|
523
|
+
props: { pageConfig: currentPageConfig },
|
|
524
|
+
navigation
|
|
525
|
+
}),
|
|
552
526
|
withKeyboardAvoidingView(
|
|
553
527
|
createElement(ReactNative.View,
|
|
554
528
|
{
|
|
555
529
|
style: {
|
|
556
530
|
flex: 1,
|
|
557
|
-
backgroundColor: currentPageConfig?.backgroundColor || '#fff'
|
|
531
|
+
backgroundColor: currentPageConfig?.backgroundColor || '#fff',
|
|
532
|
+
// 解决页面内有元素定位relative left为负值的时候,回退的时候还能看到对应元素问题
|
|
533
|
+
overflow: 'hidden'
|
|
558
534
|
},
|
|
559
535
|
ref: rootRef,
|
|
560
536
|
onLayout
|
|
@@ -582,7 +558,6 @@ export function PageWrapperHOC (WrappedComponent) {
|
|
|
582
558
|
))
|
|
583
559
|
}
|
|
584
560
|
}
|
|
585
|
-
|
|
586
561
|
export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
|
|
587
562
|
rawOptions = mergeOptions(rawOptions, type, false)
|
|
588
563
|
const components = Object.assign({}, rawOptions.components, currentInject.getComponents())
|