@mpxjs/webpack-plugin 2.10.14-beta.2 → 2.10.14-beta.21
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/lib/dependencies/ImportDependency.js +102 -0
- package/lib/index.js +18 -11
- package/lib/platform/style/wx/index.js +7 -1
- package/lib/platform/template/wx/component-config/button.js +13 -4
- package/lib/platform/template/wx/component-config/index.js +3 -1
- package/lib/platform/template/wx/component-config/nav-container.js +27 -0
- package/lib/react/LoadAsyncChunkModule.js +2 -5
- package/lib/runtime/components/ali/mpx-nav-container.mpx +3 -0
- package/lib/runtime/components/react/context.ts +18 -6
- package/lib/runtime/components/react/dist/context.d.ts +79 -0
- package/lib/runtime/components/react/dist/context.js +1 -0
- package/lib/runtime/components/react/dist/event.config.d.ts +7 -0
- package/lib/runtime/components/react/dist/getInnerListeners.d.ts +7 -0
- package/lib/runtime/components/react/dist/mpx-async-suspense.d.ts +12 -0
- package/lib/runtime/components/react/dist/mpx-button.d.ts +68 -0
- package/lib/runtime/components/react/dist/mpx-canvas/Bus.d.ts +23 -0
- package/lib/runtime/components/react/dist/mpx-canvas/CanvasGradient.d.ts +7 -0
- package/lib/runtime/components/react/dist/mpx-canvas/CanvasRenderingContext2D.d.ts +6 -0
- package/lib/runtime/components/react/dist/mpx-canvas/Image.d.ts +20 -0
- package/lib/runtime/components/react/dist/mpx-canvas/ImageData.d.ts +8 -0
- package/lib/runtime/components/react/dist/mpx-canvas/constructorsRegistry.d.ts +10 -0
- package/lib/runtime/components/react/dist/mpx-canvas/html.d.ts +2 -0
- package/lib/runtime/components/react/dist/mpx-canvas/index.d.ts +32 -0
- package/lib/runtime/components/react/dist/mpx-canvas/utils.d.ts +52 -0
- package/lib/runtime/components/react/dist/mpx-checkbox-group.d.ts +20 -0
- package/lib/runtime/components/react/dist/mpx-checkbox.d.ts +32 -0
- package/lib/runtime/components/react/dist/mpx-form.d.ts +27 -0
- package/lib/runtime/components/react/dist/mpx-icon/index.d.ts +18 -0
- package/lib/runtime/components/react/dist/mpx-image.d.ts +21 -0
- package/lib/runtime/components/react/dist/mpx-inline-text.d.ts +7 -0
- package/lib/runtime/components/react/dist/mpx-input.d.ts +50 -0
- package/lib/runtime/components/react/dist/mpx-input.jsx +38 -19
- package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.d.ts +12 -0
- package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +81 -48
- package/lib/runtime/components/react/dist/mpx-label.d.ts +20 -0
- package/lib/runtime/components/react/dist/mpx-movable-area.d.ts +20 -0
- package/lib/runtime/components/react/dist/mpx-movable-view.d.ts +63 -0
- package/lib/runtime/components/react/dist/mpx-nav-container.d.ts +9 -0
- package/lib/runtime/components/react/dist/mpx-nav-container.jsx +23 -0
- package/lib/runtime/components/react/dist/mpx-navigator.d.ts +9 -0
- package/lib/runtime/components/react/dist/mpx-picker/date.d.ts +6 -0
- package/lib/runtime/components/react/dist/mpx-picker/dateData.d.ts +7 -0
- package/lib/runtime/components/react/dist/mpx-picker/index.d.ts +6 -0
- package/lib/runtime/components/react/dist/mpx-picker/multiSelector.d.ts +6 -0
- package/lib/runtime/components/react/dist/mpx-picker/region.d.ts +6 -0
- package/lib/runtime/components/react/dist/mpx-picker/regionData.d.ts +2 -0
- package/lib/runtime/components/react/dist/mpx-picker/selector.d.ts +6 -0
- package/lib/runtime/components/react/dist/mpx-picker/time.d.ts +6 -0
- package/lib/runtime/components/react/dist/mpx-picker/type.d.ts +106 -0
- package/lib/runtime/components/react/dist/mpx-picker-view/index.d.ts +31 -0
- package/lib/runtime/components/react/dist/mpx-picker-view/pickerVIewContext.d.ts +8 -0
- package/lib/runtime/components/react/dist/mpx-picker-view-column/index.d.ts +22 -0
- package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItem.d.ts +14 -0
- package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewFaces.d.ts +16 -0
- package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewIndicator.d.ts +12 -0
- package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewMask.d.ts +11 -0
- package/lib/runtime/components/react/dist/mpx-popup/index.d.ts +22 -0
- package/lib/runtime/components/react/dist/mpx-popup/popupBase.d.ts +16 -0
- package/lib/runtime/components/react/dist/mpx-portal/index.d.ts +15 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-host.d.ts +29 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-manager.d.ts +9 -0
- package/lib/runtime/components/react/dist/mpx-radio-group.d.ts +20 -0
- package/lib/runtime/components/react/dist/mpx-radio.d.ts +26 -0
- package/lib/runtime/components/react/dist/mpx-rich-text/html.d.ts +1 -0
- package/lib/runtime/components/react/dist/mpx-rich-text/index.d.ts +24 -0
- package/lib/runtime/components/react/dist/mpx-root-portal.d.ts +14 -0
- package/lib/runtime/components/react/dist/mpx-scroll-view.d.ts +54 -0
- package/lib/runtime/components/react/dist/mpx-simple-text.d.ts +7 -0
- package/lib/runtime/components/react/dist/mpx-simple-view.d.ts +7 -0
- package/lib/runtime/components/react/dist/mpx-sticky-header.d.ts +17 -0
- package/lib/runtime/components/react/dist/mpx-sticky-section.d.ts +15 -0
- package/lib/runtime/components/react/dist/mpx-swiper-item.d.ts +18 -0
- package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-swiper.d.ts +54 -0
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +49 -5
- package/lib/runtime/components/react/dist/mpx-switch.d.ts +26 -0
- package/lib/runtime/components/react/dist/mpx-text.d.ts +21 -0
- package/lib/runtime/components/react/dist/mpx-textarea.d.ts +7 -0
- package/lib/runtime/components/react/dist/mpx-textarea.jsx +1 -0
- package/lib/runtime/components/react/dist/mpx-video.d.ts +101 -0
- package/lib/runtime/components/react/dist/mpx-view.d.ts +34 -0
- package/lib/runtime/components/react/dist/mpx-view.jsx +3 -2
- package/lib/runtime/components/react/dist/mpx-web-view.d.ts +22 -0
- package/lib/runtime/components/react/dist/nav.d.ts +8 -0
- package/lib/runtime/components/react/dist/nav.jsx +138 -0
- package/lib/runtime/components/react/dist/parser.d.ts +39 -0
- package/lib/runtime/components/react/dist/useAnimationHooks.d.ts +32 -0
- package/lib/runtime/components/react/dist/useNavShared.d.ts +2 -0
- package/lib/runtime/components/react/dist/useNavShared.js +6 -0
- package/lib/runtime/components/react/dist/useNodesRef.d.ts +11 -0
- package/lib/runtime/components/react/dist/utils.d.ts +122 -0
- package/lib/runtime/components/react/mpx-input.tsx +48 -26
- package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +91 -47
- package/lib/runtime/components/react/mpx-nav-container.tsx +33 -0
- package/lib/runtime/components/react/mpx-swiper-item.tsx +1 -1
- package/lib/runtime/components/react/mpx-swiper.tsx +60 -7
- package/lib/runtime/components/react/mpx-textarea.tsx +1 -0
- package/lib/runtime/components/react/mpx-view.tsx +3 -2
- package/lib/runtime/components/react/nav.tsx +164 -0
- package/lib/runtime/components/react/types/common.d.ts +19 -0
- package/lib/runtime/components/react/useNavShared.ts +8 -0
- package/lib/runtime/components/web/mpx-nav-container.vue +13 -0
- package/lib/runtime/components/wx/mpx-nav-container.mpx +9 -0
- package/lib/utils/dom-tag-config.js +2 -2
- package/package.json +1 -1
- package/lib/dependencies/ImportDependencyTemplate.js +0 -50
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable space-before-function-paren */
|
|
2
|
+
import React, { ReactNode, useContext, useEffect, useRef } from 'react'
|
|
2
3
|
import { DimensionValue, EmitterSubscription, Keyboard, View, ViewStyle, NativeSyntheticEvent, NativeTouchEvent } from 'react-native'
|
|
3
|
-
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated'
|
|
4
|
+
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing, cancelAnimation } from 'react-native-reanimated'
|
|
4
5
|
import { KeyboardAvoidContext } from './context'
|
|
5
|
-
import { isIOS } from './utils'
|
|
6
|
+
import { isAndroid, isIOS } from './utils'
|
|
6
7
|
|
|
7
8
|
type KeyboardAvoidViewProps = {
|
|
8
9
|
children?: ReactNode
|
|
@@ -18,15 +19,40 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
|
|
|
18
19
|
const basic = useSharedValue('auto')
|
|
19
20
|
const keyboardAvoid = useContext(KeyboardAvoidContext)
|
|
20
21
|
|
|
22
|
+
// fix: 某些特殊机型下隐藏键盘可能会先触发一次 keyboardWillShow,
|
|
23
|
+
// 比如机型 iPhone 11 Pro,可能会导致显隐动画冲突
|
|
24
|
+
// 因此增加状态标记 + clearTimeout + cancelAnimation 来优化
|
|
25
|
+
const isShow = useRef<boolean>(false)
|
|
26
|
+
const timerRef = useRef<NodeJS.Timeout | null>(null)
|
|
27
|
+
const keybaordHandleTimerRef = useRef<NodeJS.Timeout | null>(null)
|
|
28
|
+
|
|
21
29
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
30
|
+
// translate/position top可能会导致底部渲染区域缺失(需要 android 配置聚焦时禁用高度缩小),margin-top 会导致 portal 的定位失效,无法顶起 portal
|
|
22
31
|
transform: [{ translateY: -offset.value }],
|
|
23
32
|
flexBasis: basic.value as DimensionValue
|
|
24
33
|
}))
|
|
25
34
|
|
|
26
35
|
const resetKeyboard = () => {
|
|
36
|
+
if (!isShow.current) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
isShow.current = false
|
|
41
|
+
timerRef.current && clearTimeout(timerRef.current)
|
|
42
|
+
|
|
27
43
|
if (keyboardAvoid?.current) {
|
|
28
|
-
keyboardAvoid.current
|
|
44
|
+
const inputRef = keyboardAvoid.current.ref?.current
|
|
45
|
+
if (inputRef && inputRef.isFocused() && !keyboardAvoid.current.readyToShow) {
|
|
46
|
+
// 修复 Android 点击键盘收起按钮时当前 input 没触发失焦的问题
|
|
47
|
+
// keyboardAvoid.current.readyToShow = true 表示聚焦到了新的输入框,不需要手动触发失焦
|
|
48
|
+
inputRef.blur()
|
|
49
|
+
}
|
|
50
|
+
if (!keyboardAvoid.current.onKeyboardShow) {
|
|
51
|
+
keyboardAvoid.current = null
|
|
52
|
+
}
|
|
29
53
|
}
|
|
54
|
+
|
|
55
|
+
cancelAnimation(offset)
|
|
30
56
|
offset.value = withTiming(0, { duration, easing })
|
|
31
57
|
basic.value = 'auto'
|
|
32
58
|
}
|
|
@@ -40,68 +66,86 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
|
|
|
40
66
|
useEffect(() => {
|
|
41
67
|
let subscriptions: EmitterSubscription[] = []
|
|
42
68
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
69
|
+
// iphone 16 pro & iphone 其它 pro 上长按可能会触发两次 keyboard show 事件,第一次键盘高度错误,第二次正确,因此不能用节流、仅首次生效等操作控制 show 事件
|
|
70
|
+
function keybaordAvoding(evt: any, ios = false) {
|
|
71
|
+
if (keyboardAvoid?.current?.readyToShow) {
|
|
72
|
+
// 重置标记位
|
|
73
|
+
keyboardAvoid.current.readyToShow = false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!keyboardAvoid?.current) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
isShow.current = true
|
|
81
|
+
|
|
82
|
+
if (ios) {
|
|
83
|
+
timerRef.current && clearTimeout(timerRef.current)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const { endCoordinates } = evt
|
|
87
|
+
const { ref, cursorSpacing = 0, adjustPosition, onKeyboardShow } = keyboardAvoid.current
|
|
88
|
+
keyboardAvoid.current.keyboardHeight = endCoordinates.height
|
|
89
|
+
onKeyboardShow?.()
|
|
90
|
+
if (adjustPosition) {
|
|
91
|
+
// 默认沿用旧版本逻辑,在 android 原生关闭键盘避让的情况下应该将该配置设置为 false,走 mpx 的键盘避让逻辑,否则bundle内的所有input都会无法避让键盘
|
|
92
|
+
const enableNativeKeyboardAvoiding = mpxGlobal?.__mpx?.config?.rnConfig?.enableNativeKeyboardAvoiding ?? true
|
|
93
|
+
const callback = () => {
|
|
94
|
+
ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
|
|
95
|
+
function calculateOffset() {
|
|
96
|
+
// enableNativeKeyboardAvoding 默认开启
|
|
97
|
+
if (enableNativeKeyboardAvoiding && isAndroid) {
|
|
98
|
+
const aboveOffset = pageY + height - endCoordinates.screenY
|
|
99
|
+
const belowOffset = endCoordinates.height - aboveOffset
|
|
100
|
+
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
|
|
101
|
+
const belowValue = Math.min(belowOffset, cursorSpacing)
|
|
102
|
+
return aboveOffset > 0 ? belowValue : aboveValue
|
|
103
|
+
}
|
|
104
|
+
|
|
51
105
|
const aboveOffset = offset.value + pageY + height - endCoordinates.screenY
|
|
52
106
|
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
|
|
53
107
|
const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
-
})
|
|
63
|
-
}),
|
|
64
|
-
Keyboard.addListener('keyboardWillHide', resetKeyboard)
|
|
65
|
-
]
|
|
66
|
-
} else {
|
|
67
|
-
subscriptions = [
|
|
68
|
-
Keyboard.addListener('keyboardDidShow', (evt: any) => {
|
|
69
|
-
if (!keyboardAvoid?.current) return
|
|
70
|
-
const { endCoordinates } = evt
|
|
71
|
-
const { ref, cursorSpacing = 0 } = keyboardAvoid.current
|
|
72
|
-
ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
|
|
73
|
-
const aboveOffset = pageY + height - endCoordinates.screenY
|
|
74
|
-
const belowOffset = endCoordinates.height - aboveOffset
|
|
75
|
-
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
|
|
76
|
-
const belowValue = Math.min(belowOffset, cursorSpacing)
|
|
77
|
-
const value = aboveOffset > 0 ? belowValue : aboveValue
|
|
78
|
-
offset.value = withTiming(value, { duration, easing }, (finished) => {
|
|
108
|
+
return aboveOffset > 0 ? belowValue : aboveValue
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
cancelAnimation(offset)
|
|
112
|
+
offset.value = withTiming(calculateOffset(), { duration, easing }, finished => {
|
|
79
113
|
if (finished) {
|
|
80
114
|
// Set flexBasic after animation to trigger re-layout and reset layout information
|
|
81
115
|
basic.value = '99.99%'
|
|
82
116
|
}
|
|
83
117
|
})
|
|
84
118
|
})
|
|
119
|
+
}
|
|
120
|
+
;(isIOS ? () => (timerRef.current = setTimeout(callback)) : callback)()
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (isIOS) {
|
|
125
|
+
subscriptions = [
|
|
126
|
+
Keyboard.addListener('keyboardWillShow', (evt: any) => {
|
|
127
|
+
if (keybaordHandleTimerRef.current) {
|
|
128
|
+
clearTimeout(keybaordHandleTimerRef.current)
|
|
129
|
+
}
|
|
130
|
+
// iphone 在input聚焦时长按滑动后会导致 show 事件先于 focus 事件发生,因此等一下,等 focus 先触发拿到 input,避免键盘出现但input没顶上去
|
|
131
|
+
keybaordHandleTimerRef.current = setTimeout(() => keybaordAvoding(evt, true), 32)
|
|
85
132
|
}),
|
|
86
|
-
Keyboard.addListener('
|
|
133
|
+
Keyboard.addListener('keyboardWillHide', resetKeyboard)
|
|
87
134
|
]
|
|
135
|
+
} else {
|
|
136
|
+
subscriptions = [Keyboard.addListener('keyboardDidShow', keybaordAvoding), Keyboard.addListener('keyboardDidHide', resetKeyboard)]
|
|
88
137
|
}
|
|
89
138
|
|
|
90
139
|
return () => {
|
|
91
140
|
subscriptions.forEach(subscription => subscription.remove())
|
|
141
|
+
timerRef.current && clearTimeout(timerRef.current)
|
|
142
|
+
keybaordHandleTimerRef.current && clearTimeout(keybaordHandleTimerRef.current)
|
|
92
143
|
}
|
|
93
144
|
}, [keyboardAvoid])
|
|
94
145
|
|
|
95
146
|
return (
|
|
96
|
-
<View style={style} onTouchEnd={onTouchEnd}>
|
|
97
|
-
<Animated.View
|
|
98
|
-
style={[
|
|
99
|
-
contentContainerStyle,
|
|
100
|
-
animatedStyle
|
|
101
|
-
]}
|
|
102
|
-
>
|
|
103
|
-
{children}
|
|
104
|
-
</Animated.View>
|
|
147
|
+
<View style={style} onTouchEnd={onTouchEnd} onTouchMove={onTouchEnd}>
|
|
148
|
+
<Animated.View style={[contentContainerStyle, animatedStyle]}>{children}</Animated.View>
|
|
105
149
|
</View>
|
|
106
150
|
)
|
|
107
151
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AnimatedStyle } from 'react-native-reanimated'
|
|
2
|
+
import { useNavShared } from './useNavShared'
|
|
3
|
+
import { NavSharedContext, NavSharedValue } from './context'
|
|
4
|
+
import { useLayoutEffect, useMemo, useState } from 'react'
|
|
5
|
+
import { StyleProp } from 'react-native'
|
|
6
|
+
import { isAndroid } from './utils'
|
|
7
|
+
|
|
8
|
+
interface MpxNavContainerProps {
|
|
9
|
+
children?: React.ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function MpxNavContainer (props: MpxNavContainerProps) {
|
|
13
|
+
const [, setCustomNav] = useNavShared()
|
|
14
|
+
|
|
15
|
+
useLayoutEffect(() => {
|
|
16
|
+
if (!isAndroid) return
|
|
17
|
+
if (props.children) {
|
|
18
|
+
setCustomNav(props.children)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return () => {
|
|
22
|
+
setCustomNav(undefined)
|
|
23
|
+
}
|
|
24
|
+
}, [props.children])
|
|
25
|
+
|
|
26
|
+
return isAndroid ? null : props.children
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function NavSharedProvider ({ children }: { children?: React.ReactNode }) {
|
|
30
|
+
const [customNav, setCustomNav] = useState()
|
|
31
|
+
const value = useMemo(() => ({ customNav, setCustomNav } as NavSharedValue), [customNav])
|
|
32
|
+
return <NavSharedContext.Provider value={value}>{children}</NavSharedContext.Provider>
|
|
33
|
+
}
|
|
@@ -84,7 +84,7 @@ const _SwiperItem = forwardRef<HandlerRef<View, SwiperItemProps>, SwiperItemProp
|
|
|
84
84
|
const inputRange = [step.value, 0]
|
|
85
85
|
const outputRange = [0.7, 1]
|
|
86
86
|
// 实现元素的宽度跟随step从0到真实宽度,且不能触发重新渲染整个组件,通过AnimatedStyle的方式实现
|
|
87
|
-
const outerLayoutStyle = dir === 'x' ? { width: step.value, height: '100%' } : { width: '100%', height: step.value }
|
|
87
|
+
const outerLayoutStyle = dir === 'x' ? { width: step.value || '100%', height: '100%' } : { width: '100%', height: step.value || '100%' }
|
|
88
88
|
const transformStyle = []
|
|
89
89
|
if (scale) {
|
|
90
90
|
transformStyle.push({
|
|
@@ -5,7 +5,7 @@ import Animated, { useAnimatedStyle, useSharedValue, withTiming, Easing, runOnJS
|
|
|
5
5
|
import React, { JSX, forwardRef, useRef, useEffect, ReactNode, ReactElement, useMemo, createElement } from 'react'
|
|
6
6
|
import useInnerProps, { getCustomEvent } from './getInnerListeners'
|
|
7
7
|
import useNodesRef, { HandlerRef } from './useNodesRef' // 引入辅助函数
|
|
8
|
-
import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren, extendObject, GestureHandler, flatGesture, useRunOnJSCallback } from './utils'
|
|
8
|
+
import { useTransformStyle, splitStyle, splitProps, useLayout, wrapChildren, extendObject, GestureHandler, flatGesture, useRunOnJSCallback, isHarmony } from './utils'
|
|
9
9
|
import { SwiperContext } from './context'
|
|
10
10
|
import Portal from './mpx-portal'
|
|
11
11
|
/**
|
|
@@ -33,6 +33,11 @@ type EventDataType = {
|
|
|
33
33
|
// onUpdate时根据上一个判断方向,onFinalize根据transformStart判断
|
|
34
34
|
transdir: number
|
|
35
35
|
}
|
|
36
|
+
// bindtransition回调的参数
|
|
37
|
+
type EventTransition = {
|
|
38
|
+
dx: number
|
|
39
|
+
dy: number
|
|
40
|
+
}
|
|
36
41
|
|
|
37
42
|
interface SwiperProps {
|
|
38
43
|
children?: ReactNode
|
|
@@ -64,6 +69,8 @@ interface SwiperProps {
|
|
|
64
69
|
'simultaneous-handlers'?: Array<GestureHandler>
|
|
65
70
|
disableGesture?: boolean
|
|
66
71
|
bindchange?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void
|
|
72
|
+
bindtransition?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void
|
|
73
|
+
bindanimationfinish?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
/**
|
|
@@ -149,7 +156,9 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
149
156
|
circular = false,
|
|
150
157
|
disableGesture = false,
|
|
151
158
|
current: propCurrent = 0,
|
|
152
|
-
bindchange
|
|
159
|
+
bindchange,
|
|
160
|
+
bindtransition,
|
|
161
|
+
bindanimationfinish
|
|
153
162
|
} = props
|
|
154
163
|
const easeingFunc = props['easing-function'] || 'default'
|
|
155
164
|
const easeDuration = props.duration || 500
|
|
@@ -378,6 +387,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
378
387
|
}, () => {
|
|
379
388
|
currentIndex.value = nextIndex
|
|
380
389
|
runOnJS(runOnJSCallback)('loop')
|
|
390
|
+
bindanimationfinish && runOnJS(runOnJSCallback)('handleAnimationfinish', nextIndex)
|
|
381
391
|
})
|
|
382
392
|
} else {
|
|
383
393
|
// 默认向右, 向下
|
|
@@ -393,6 +403,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
393
403
|
offset.value = initOffset
|
|
394
404
|
currentIndex.value = nextIndex
|
|
395
405
|
runOnJS(runOnJSCallback)('loop')
|
|
406
|
+
bindanimationfinish && runOnJS(runOnJSCallback)('handleAnimationfinish', nextIndex)
|
|
396
407
|
})
|
|
397
408
|
} else {
|
|
398
409
|
nextIndex = currentIndex.value + 1
|
|
@@ -404,6 +415,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
404
415
|
}, () => {
|
|
405
416
|
currentIndex.value = nextIndex
|
|
406
417
|
runOnJS(runOnJSCallback)('loop')
|
|
418
|
+
runOnJS(runOnJSCallback)('handleAnimationfinish', nextIndex)
|
|
407
419
|
})
|
|
408
420
|
}
|
|
409
421
|
}
|
|
@@ -436,11 +448,23 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
436
448
|
bindchange && bindchange(eventData)
|
|
437
449
|
}
|
|
438
450
|
|
|
451
|
+
function handleTransition (transData: EventTransition) {
|
|
452
|
+
const eventData = getCustomEvent('change', {}, { detail: transData, layoutRef: layoutRef })
|
|
453
|
+
bindtransition && bindtransition(eventData)
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function handleAnimationfinish (current: number) {
|
|
457
|
+
const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef })
|
|
458
|
+
bindanimationfinish && bindanimationfinish(eventData)
|
|
459
|
+
}
|
|
460
|
+
|
|
439
461
|
const runOnJSCallbackRef = useRef({
|
|
440
462
|
loop,
|
|
441
463
|
pauseLoop,
|
|
442
464
|
resumeLoop,
|
|
443
|
-
handleSwiperChange
|
|
465
|
+
handleSwiperChange,
|
|
466
|
+
handleTransition,
|
|
467
|
+
handleAnimationfinish
|
|
444
468
|
})
|
|
445
469
|
const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
|
|
446
470
|
|
|
@@ -466,6 +490,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
466
490
|
easing: easeMap[easeingFunc]
|
|
467
491
|
}, () => {
|
|
468
492
|
currentIndex.value = propCurrent
|
|
493
|
+
bindanimationfinish && runOnJS(runOnJSCallback)('handleAnimationfinish', propCurrent)
|
|
469
494
|
})
|
|
470
495
|
} else {
|
|
471
496
|
offset.value = targetOffset
|
|
@@ -486,7 +511,27 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
486
511
|
runOnJS(runOnJSCallback)('handleSwiperChange', newIndex, propCurrent)
|
|
487
512
|
}
|
|
488
513
|
})
|
|
489
|
-
|
|
514
|
+
useAnimatedReaction(() => offset.value, (curOffset, preOffset) => {
|
|
515
|
+
const curAbsOffset = Math.abs(curOffset)
|
|
516
|
+
const preAbsOffset = Math.abs(preOffset || 0)
|
|
517
|
+
const computeOffset = step.value * (currentIndex.value + patchElmNumShared.value)
|
|
518
|
+
// 有小数点的情况
|
|
519
|
+
const isEqual = Math.abs(Math.floor(computeOffset) - Math.floor(Math.abs(offset.value))) <= 2
|
|
520
|
+
if (curAbsOffset !== preAbsOffset && curAbsOffset && !isEqual) {
|
|
521
|
+
// 移动的距离,手向左滑动正数( curAbsOffset >= preAbsOffset),右右滑动是负数( curAbsOffset < preAbsOffset)
|
|
522
|
+
let trans = 0
|
|
523
|
+
if (curAbsOffset >= preAbsOffset) {
|
|
524
|
+
trans = curAbsOffset % step.value
|
|
525
|
+
} else {
|
|
526
|
+
trans = -(step.value - curAbsOffset % step.value)
|
|
527
|
+
}
|
|
528
|
+
const transData = {
|
|
529
|
+
dx: dir === 'x' ? trans : 0,
|
|
530
|
+
dy: dir === 'y' ? trans : 0
|
|
531
|
+
}
|
|
532
|
+
bindtransition && runOnJS(runOnJSCallback)('handleTransition', transData)
|
|
533
|
+
}
|
|
534
|
+
})
|
|
490
535
|
useEffect(() => {
|
|
491
536
|
let patchStep = 0
|
|
492
537
|
if (preMargin !== preMarginShared.value) {
|
|
@@ -613,6 +658,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
613
658
|
currentIndex.value = selectedIndex
|
|
614
659
|
offset.value = resetOffset
|
|
615
660
|
runOnJS(runOnJSCallback)('resumeLoop')
|
|
661
|
+
bindanimationfinish && runOnJS(runOnJSCallback)('handleAnimationfinish', selectedIndex)
|
|
616
662
|
}
|
|
617
663
|
})
|
|
618
664
|
} else {
|
|
@@ -623,6 +669,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
623
669
|
if (touchfinish.value !== false) {
|
|
624
670
|
currentIndex.value = selectedIndex
|
|
625
671
|
runOnJS(runOnJSCallback)('resumeLoop')
|
|
672
|
+
bindanimationfinish && runOnJS(runOnJSCallback)('handleAnimationfinish', selectedIndex)
|
|
626
673
|
}
|
|
627
674
|
})
|
|
628
675
|
}
|
|
@@ -645,6 +692,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
645
692
|
if (touchfinish.value !== false) {
|
|
646
693
|
currentIndex.value = moveToIndex
|
|
647
694
|
runOnJS(runOnJSCallback)('resumeLoop')
|
|
695
|
+
bindanimationfinish && runOnJS(runOnJSCallback)('handleAnimationfinish', moveToIndex)
|
|
648
696
|
}
|
|
649
697
|
})
|
|
650
698
|
}
|
|
@@ -791,6 +839,10 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
791
839
|
}
|
|
792
840
|
preAbsolutePos.value = e[strAbso]
|
|
793
841
|
})
|
|
842
|
+
.onEnd((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
|
|
843
|
+
// 修复某些安卓机型小米 onFinalize拿到的absolute值不正确的问题, onUpdate并不是最终的值
|
|
844
|
+
preAbsolutePos.value = e[strAbso]
|
|
845
|
+
})
|
|
794
846
|
.onFinalize((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
|
|
795
847
|
'worklet'
|
|
796
848
|
if (touchfinish.value) return
|
|
@@ -855,10 +907,11 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
855
907
|
}, [gestureSwitch.current])
|
|
856
908
|
|
|
857
909
|
const animatedStyles = useAnimatedStyle(() => {
|
|
910
|
+
const opacity = isHarmony ? 1 : (step.value > 0 ? 1 : 0)
|
|
858
911
|
if (dir === 'x') {
|
|
859
|
-
return { transform: [{ translateX: offset.value }], opacity
|
|
912
|
+
return { transform: [{ translateX: offset.value }], opacity }
|
|
860
913
|
} else {
|
|
861
|
-
return { transform: [{ translateY: offset.value }], opacity
|
|
914
|
+
return { transform: [{ translateY: offset.value }], opacity }
|
|
862
915
|
}
|
|
863
916
|
})
|
|
864
917
|
|
|
@@ -891,4 +944,4 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
891
944
|
})
|
|
892
945
|
SwiperWrapper.displayName = 'MpxSwiperWrapper'
|
|
893
946
|
|
|
894
|
-
export default SwiperWrapper
|
|
947
|
+
export default SwiperWrapper
|
|
@@ -287,7 +287,7 @@ function backgroundSize (imageProps: ImageProps, preImageInfo: PreImageInfo, ima
|
|
|
287
287
|
} else { // 数值类型 ImageStyle
|
|
288
288
|
// 数值类型设置为 stretch
|
|
289
289
|
imageProps.resizeMode = 'stretch'
|
|
290
|
-
if (type === 'linear' && (!layoutWidth || !layoutHeight)) {
|
|
290
|
+
if (type === 'linear' && (!layoutWidth || !layoutHeight) && isNeedLayout(preImageInfo)) {
|
|
291
291
|
// ios 上 linear 组件只要重新触发渲染,在渲染过程中外层容器 width 或者 height 被设置为 0,通过设置 % 的方式会渲染不出来,即使后面再更新为正常宽高也渲染不出来
|
|
292
292
|
// 所以 hack 手动先将 linear 宽高也设置为 0,后面再更新为正确的数值或 %。
|
|
293
293
|
dimensions = {
|
|
@@ -295,6 +295,7 @@ function backgroundSize (imageProps: ImageProps, preImageInfo: PreImageInfo, ima
|
|
|
295
295
|
height: 0
|
|
296
296
|
} as { width: NumberVal, height: NumberVal }
|
|
297
297
|
} else {
|
|
298
|
+
// background-size 手动设置具体值,不会触发 onLayout,需要走这里的逻辑
|
|
298
299
|
dimensions = {
|
|
299
300
|
width: isPercent(width) ? width : +width,
|
|
300
301
|
height: isPercent(height) ? height : +height
|
|
@@ -501,7 +502,7 @@ function parseBgImage (text: string): {
|
|
|
501
502
|
type?: 'image' | 'linear'
|
|
502
503
|
src?: string
|
|
503
504
|
} {
|
|
504
|
-
if (!text) return {}
|
|
505
|
+
if (!text || text === 'none') return {}
|
|
505
506
|
|
|
506
507
|
const src = parseUrl(text)
|
|
507
508
|
if (src) return { src, type: 'image' }
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/* eslint-disable space-before-function-paren */
|
|
2
|
+
import { createElement, useState, useMemo, memo, useContext, useLayoutEffect } from 'react'
|
|
3
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
4
|
+
import { StatusBar, processColor, TouchableWithoutFeedback, Image, View, StyleSheet, Text } from 'react-native'
|
|
5
|
+
import { useNavShared } from './useNavShared'
|
|
6
|
+
|
|
7
|
+
function convertToHex(color?: string) {
|
|
8
|
+
try {
|
|
9
|
+
const intColor = processColor(color) as number | null | undefined
|
|
10
|
+
if (intColor === null || intColor === undefined) {
|
|
11
|
+
return null
|
|
12
|
+
}
|
|
13
|
+
// 将32位整数颜色值转换为RGBA
|
|
14
|
+
const r = (intColor >> 16) & 255
|
|
15
|
+
const g = (intColor >> 8) & 255
|
|
16
|
+
const b = intColor & 255
|
|
17
|
+
// 转换为十六进制
|
|
18
|
+
const hexR = r.toString(16).padStart(2, '0')
|
|
19
|
+
const hexG = g.toString(16).padStart(2, '0')
|
|
20
|
+
const hexB = b.toString(16).padStart(2, '0')
|
|
21
|
+
return `#${hexR}${hexG}${hexB}`
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const titleHeight = 44
|
|
28
|
+
export function useInnerHeaderHeight(pageConfig: PageConfig) {
|
|
29
|
+
const safeArea = useSafeAreaInsets()
|
|
30
|
+
if (pageConfig.navigationStyle === 'custom') {
|
|
31
|
+
return 0
|
|
32
|
+
} else {
|
|
33
|
+
const safeAreaTop = safeArea?.top || 0
|
|
34
|
+
const headerHeight = safeAreaTop + titleHeight
|
|
35
|
+
return headerHeight
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const styles = StyleSheet.create({
|
|
40
|
+
header: {
|
|
41
|
+
elevation: 3
|
|
42
|
+
},
|
|
43
|
+
headerContent: {
|
|
44
|
+
flexDirection: 'row',
|
|
45
|
+
alignItems: 'center',
|
|
46
|
+
justifyContent: 'center'
|
|
47
|
+
},
|
|
48
|
+
backButton: {
|
|
49
|
+
position: 'absolute',
|
|
50
|
+
height: '100%',
|
|
51
|
+
width: 40,
|
|
52
|
+
left: 0,
|
|
53
|
+
top: 0,
|
|
54
|
+
alignItems: 'center',
|
|
55
|
+
justifyContent: 'center'
|
|
56
|
+
},
|
|
57
|
+
backButtonImage: {
|
|
58
|
+
width: 22,
|
|
59
|
+
height: 22
|
|
60
|
+
},
|
|
61
|
+
title: {
|
|
62
|
+
fontSize: 17,
|
|
63
|
+
fontWeight: 600,
|
|
64
|
+
width: '60%',
|
|
65
|
+
textAlign: 'center'
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
const NavColor = {
|
|
69
|
+
White: '#ffffff',
|
|
70
|
+
Black: '#000000'
|
|
71
|
+
}
|
|
72
|
+
// navigationBarTextStyle 只支持黑白 'white'/'black
|
|
73
|
+
const validBarTextStyle = (textStyle?: string) => {
|
|
74
|
+
const textStyleColor = convertToHex(textStyle)
|
|
75
|
+
if (textStyle && textStyleColor && [NavColor.White, NavColor.Black].includes(textStyleColor)) {
|
|
76
|
+
return textStyleColor
|
|
77
|
+
} else {
|
|
78
|
+
return NavColor.White
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface MpxNavProps {
|
|
83
|
+
pageConfig: PageConfig
|
|
84
|
+
navigation: any
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const BACK_ICON =
|
|
88
|
+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAABICAYAAACqT5alAAAA2UlEQVR4nO3bMQrCUBRE0Yla6AYEN2nnBrTL+izcitW3MRDkEUWSvPzJvfCqgMwhZbAppWhNbbIHzB1g9wATERFRVyvpkj1irlpJ5X326D7WHh1hbdFD2CLpLmmftm7kfsEe09aNHFiBrT+wAlt/YAW2/sAKbP2BFdj6Ayuwy+ufz6XPL893krZ//O6iu2n4LT8kndLWTRTo4EC7BDo40C6BDg60S6CDA+0S6OBAuwQ6uNWiD2nrJmoIfU7cNWkR2hbb1UfbY7uuWhGWiIg+a/iHuHmA3QPs3gu4JW9Gan+OJAAAAABJRU5ErkJggg=='
|
|
89
|
+
|
|
90
|
+
const MpxNav = memo(({ pageConfig, navigation }: MpxNavProps) => {
|
|
91
|
+
const [innerPageConfig, setPageConfig] = useState<PageConfig>(pageConfig || {})
|
|
92
|
+
const [customNav] = useNavShared()
|
|
93
|
+
const safeAreaTop = useSafeAreaInsets()?.top || 0
|
|
94
|
+
|
|
95
|
+
navigation.setPageConfig = (config: PageConfig) => {
|
|
96
|
+
setPageConfig(Object.assign({}, innerPageConfig, config))
|
|
97
|
+
}
|
|
98
|
+
const isCustom = innerPageConfig.navigationStyle === 'custom'
|
|
99
|
+
const navigationBarTextStyle = useMemo(() => validBarTextStyle(innerPageConfig.navigationBarTextStyle), [innerPageConfig.navigationBarTextStyle])
|
|
100
|
+
// 状态栏的颜色
|
|
101
|
+
const statusBarElement = (
|
|
102
|
+
<StatusBar
|
|
103
|
+
translucent
|
|
104
|
+
backgroundColor='transparent'
|
|
105
|
+
barStyle={navigationBarTextStyle === NavColor.White ? 'light-content' : 'dark-content'}></StatusBar>
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if (isCustom) {
|
|
109
|
+
return (
|
|
110
|
+
<>
|
|
111
|
+
{statusBarElement}
|
|
112
|
+
{customNav}
|
|
113
|
+
</>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
// 假设是栈导航,获取栈的长度
|
|
117
|
+
const stackLength = navigation.getState()?.routes?.length
|
|
118
|
+
const onStackTopBack = mpxGlobal?.__mpx?.config?.rnConfig?.onStackTopBack
|
|
119
|
+
const isHandleStackTopBack = typeof onStackTopBack === 'function'
|
|
120
|
+
|
|
121
|
+
// 回退按钮与图标
|
|
122
|
+
// prettier-ignore
|
|
123
|
+
const backElement = stackLength > 1 || isHandleStackTopBack
|
|
124
|
+
? (
|
|
125
|
+
<TouchableWithoutFeedback
|
|
126
|
+
onPress={() => {
|
|
127
|
+
if (stackLength <= 1 && isHandleStackTopBack) {
|
|
128
|
+
onStackTopBack()
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
navigation.goBack()
|
|
132
|
+
}}>
|
|
133
|
+
<View style={[styles.backButton]}>
|
|
134
|
+
<Image style={[styles.backButtonImage, { tintColor: navigationBarTextStyle }]} source={{ uri: BACK_ICON }}></Image>
|
|
135
|
+
</View>
|
|
136
|
+
</TouchableWithoutFeedback>
|
|
137
|
+
)
|
|
138
|
+
: null
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<View
|
|
142
|
+
style={[
|
|
143
|
+
styles.header,
|
|
144
|
+
{
|
|
145
|
+
paddingTop: safeAreaTop,
|
|
146
|
+
backgroundColor: innerPageConfig.navigationBarBackgroundColor || '#000000'
|
|
147
|
+
}
|
|
148
|
+
]}>
|
|
149
|
+
{statusBarElement}
|
|
150
|
+
{/* TODO: 确定 height 的有效性 */}
|
|
151
|
+
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
|
|
152
|
+
{/* @ts-expect-error */}
|
|
153
|
+
<View style={styles.headerContent} height={titleHeight}>
|
|
154
|
+
{backElement}
|
|
155
|
+
<Text style={[styles.title, { color: navigationBarTextStyle }]} numberOfLines={1}>
|
|
156
|
+
{innerPageConfig.navigationBarTitleText?.trim() || ''}
|
|
157
|
+
</Text>
|
|
158
|
+
</View>
|
|
159
|
+
</View>
|
|
160
|
+
)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
MpxNav.displayName = 'MpxNav'
|
|
164
|
+
export default MpxNav
|
|
@@ -18,3 +18,22 @@ export type ExtendedFunctionComponent = FunctionComponent & {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export type AnyFunc = (...args: ReadonlyArray<any>) => any
|
|
21
|
+
|
|
22
|
+
declare global {
|
|
23
|
+
interface PageConfig {
|
|
24
|
+
/**
|
|
25
|
+
* 是否自定义导航栏
|
|
26
|
+
*/
|
|
27
|
+
navigationStyle?: 'custom'
|
|
28
|
+
/**
|
|
29
|
+
* 标题栏样式
|
|
30
|
+
*/
|
|
31
|
+
navigationBarTextStyle?: 'white' | 'black' | '#ffffff' | '#000000'
|
|
32
|
+
/**
|
|
33
|
+
* 页面标题
|
|
34
|
+
*/
|
|
35
|
+
navigationBarTitleText?: string
|
|
36
|
+
|
|
37
|
+
[key: string]: any
|
|
38
|
+
}
|
|
39
|
+
}
|