@mpxjs/webpack-plugin 2.10.6-beta.8 → 2.10.6-beta.9
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/platform/template/wx/component-config/index.js +3 -1
- package/lib/platform/template/wx/component-config/nav-container.js +27 -0
- package/lib/runtime/components/ali/mpx-nav-container.mpx +3 -0
- package/lib/runtime/components/react/context.ts +10 -4
- package/lib/runtime/components/react/dist/context.d.ts +5 -1
- package/lib/runtime/components/react/dist/context.js +1 -0
- package/lib/runtime/components/react/dist/mpx-input.jsx +2 -3
- package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +13 -40
- 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/nav.jsx +18 -25
- 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/mpx-input.tsx +3 -3
- package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +13 -45
- package/lib/runtime/components/react/mpx-nav-container.tsx +33 -0
- package/lib/runtime/components/react/nav.tsx +29 -34
- 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
|
@@ -44,6 +44,7 @@ const fixComponentName = require('./fix-component-name')
|
|
|
44
44
|
const rootPortal = require('./root-portal')
|
|
45
45
|
const stickyHeader = require('./sticky-header')
|
|
46
46
|
const stickySection = require('./sticky-section')
|
|
47
|
+
const navContainer = require('./nav-container')
|
|
47
48
|
|
|
48
49
|
module.exports = function getComponentConfigs ({ warn, error }) {
|
|
49
50
|
/**
|
|
@@ -129,6 +130,7 @@ module.exports = function getComponentConfigs ({ warn, error }) {
|
|
|
129
130
|
component(),
|
|
130
131
|
rootPortal({ print }),
|
|
131
132
|
stickyHeader({ print }),
|
|
132
|
-
stickySection({ print })
|
|
133
|
+
stickySection({ print }),
|
|
134
|
+
navContainer({ print })
|
|
133
135
|
]
|
|
134
136
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const TAG_NAME = 'nav-container'
|
|
2
|
+
|
|
3
|
+
module.exports = function ({ print }) {
|
|
4
|
+
return {
|
|
5
|
+
test: TAG_NAME,
|
|
6
|
+
web(tag, { el }) {
|
|
7
|
+
el.isBuiltIn = true
|
|
8
|
+
return 'mpx-nav-container'
|
|
9
|
+
},
|
|
10
|
+
ios(tag, { el }) {
|
|
11
|
+
el.isBuiltIn = true
|
|
12
|
+
return 'mpx-nav-container'
|
|
13
|
+
},
|
|
14
|
+
android(tag, { el }) {
|
|
15
|
+
el.isBuiltIn = true
|
|
16
|
+
return 'mpx-nav-container'
|
|
17
|
+
},
|
|
18
|
+
harmony(tag, { el }) {
|
|
19
|
+
el.isBuiltIn = true
|
|
20
|
+
return 'mpx-nav-container'
|
|
21
|
+
},
|
|
22
|
+
wx(tag, { el }) {
|
|
23
|
+
el.isBuiltIn = true
|
|
24
|
+
return 'mpx-nav-container'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -12,7 +12,6 @@ export type KeyboardAvoidContextValue = MutableRefObject<{
|
|
|
12
12
|
adjustPosition: boolean
|
|
13
13
|
keyboardHeight?: number
|
|
14
14
|
onKeyboardShow?: () => void
|
|
15
|
-
blurCallbacks: (() => void)[]
|
|
16
15
|
} | null>
|
|
17
16
|
|
|
18
17
|
export interface GroupValue {
|
|
@@ -42,13 +41,13 @@ export interface IntersectionObserver {
|
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
export interface PortalContextValue {
|
|
45
|
-
mount: (children: React.ReactNode, key?: number | null, id?: number| null) => number| undefined
|
|
44
|
+
mount: (children: React.ReactNode, key?: number | null, id?: number | null) => number | undefined
|
|
46
45
|
update: (key: number, children: React.ReactNode) => void
|
|
47
46
|
unmount: (key: number) => void
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
export interface ScrollViewContextValue {
|
|
51
|
-
gestureRef: React.RefObject<any> | null
|
|
50
|
+
gestureRef: React.RefObject<any> | null
|
|
52
51
|
scrollOffset: Animated.Value
|
|
53
52
|
}
|
|
54
53
|
|
|
@@ -58,10 +57,15 @@ export interface RouteContextValue {
|
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
export interface StickyContextValue {
|
|
61
|
-
registerStickyHeader: Function
|
|
60
|
+
registerStickyHeader: Function
|
|
62
61
|
unregisterStickyHeader: Function
|
|
63
62
|
}
|
|
64
63
|
|
|
64
|
+
export interface NavSharedValue {
|
|
65
|
+
customNav?: React.ReactNode
|
|
66
|
+
setCustomNav: (value: React.ReactNode) => void
|
|
67
|
+
}
|
|
68
|
+
|
|
65
69
|
export const MovableAreaContext = createContext({ width: 0, height: 0 })
|
|
66
70
|
|
|
67
71
|
export const FormContext = createContext<FormContextValue | null>(null)
|
|
@@ -89,3 +93,5 @@ export const ScrollViewContext = createContext<ScrollViewContextValue>({ gesture
|
|
|
89
93
|
export const PortalContext = createContext<PortalContextValue>(null as any)
|
|
90
94
|
|
|
91
95
|
export const StickyContext = createContext<StickyContextValue>({ registerStickyHeader: noop, unregisterStickyHeader: noop })
|
|
96
|
+
|
|
97
|
+
export const NavSharedContext = createContext<NavSharedValue>(null as any)
|
|
@@ -9,7 +9,6 @@ export type KeyboardAvoidContextValue = MutableRefObject<{
|
|
|
9
9
|
adjustPosition: boolean;
|
|
10
10
|
keyboardHeight?: number;
|
|
11
11
|
onKeyboardShow?: () => void;
|
|
12
|
-
blurCallbacks: (() => void)[];
|
|
13
12
|
} | null>;
|
|
14
13
|
export interface GroupValue {
|
|
15
14
|
[key: string]: {
|
|
@@ -55,6 +54,10 @@ export interface StickyContextValue {
|
|
|
55
54
|
registerStickyHeader: Function;
|
|
56
55
|
unregisterStickyHeader: Function;
|
|
57
56
|
}
|
|
57
|
+
export interface NavSharedValue {
|
|
58
|
+
customNav?: React.ReactNode;
|
|
59
|
+
setCustomNav: (value: React.ReactNode) => void;
|
|
60
|
+
}
|
|
58
61
|
export declare const MovableAreaContext: import("react").Context<{
|
|
59
62
|
width: number;
|
|
60
63
|
height: number;
|
|
@@ -72,3 +75,4 @@ export declare const KeyboardAvoidContext: import("react").Context<KeyboardAvoid
|
|
|
72
75
|
export declare const ScrollViewContext: import("react").Context<ScrollViewContextValue>;
|
|
73
76
|
export declare const PortalContext: import("react").Context<PortalContextValue>;
|
|
74
77
|
export declare const StickyContext: import("react").Context<StickyContextValue>;
|
|
78
|
+
export declare const NavSharedContext: import("react").Context<NavSharedValue>;
|
|
@@ -15,3 +15,4 @@ export const KeyboardAvoidContext = createContext(null);
|
|
|
15
15
|
export const ScrollViewContext = createContext({ gestureRef: null, scrollOffset: new Animated.Value(0) });
|
|
16
16
|
export const PortalContext = createContext(null);
|
|
17
17
|
export const StickyContext = createContext({ registerStickyHeader: noop, unregisterStickyHeader: noop });
|
|
18
|
+
export const NavSharedContext = createContext(null);
|
|
@@ -145,7 +145,7 @@ const Input = forwardRef((props, ref) => {
|
|
|
145
145
|
};
|
|
146
146
|
const setKeyboardAvoidContext = () => {
|
|
147
147
|
if (keyboardAvoid) {
|
|
148
|
-
keyboardAvoid.current = { cursorSpacing, ref: nodeRef, adjustPosition
|
|
148
|
+
keyboardAvoid.current = { cursorSpacing, ref: nodeRef, adjustPosition };
|
|
149
149
|
}
|
|
150
150
|
};
|
|
151
151
|
const onTouchStart = () => {
|
|
@@ -164,7 +164,7 @@ const Input = forwardRef((props, ref) => {
|
|
|
164
164
|
bindfocus(getCustomEvent('focus', evt, {
|
|
165
165
|
detail: {
|
|
166
166
|
value: tmpValue.current || '',
|
|
167
|
-
height: keyboardAvoid.current?.keyboardHeight
|
|
167
|
+
height: keyboardAvoid.current?.keyboardHeight,
|
|
168
168
|
},
|
|
169
169
|
layoutRef
|
|
170
170
|
}, props));
|
|
@@ -184,7 +184,6 @@ const Input = forwardRef((props, ref) => {
|
|
|
184
184
|
}
|
|
185
185
|
};
|
|
186
186
|
const onBlur = (evt) => {
|
|
187
|
-
keyboardAvoid?.current?.blurCallbacks.forEach(fn => fn());
|
|
188
187
|
bindblur && bindblur(getCustomEvent('blur', evt, {
|
|
189
188
|
detail: {
|
|
190
189
|
value: tmpValue.current || '',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useContext, useEffect } from 'react';
|
|
2
2
|
import { Keyboard, View } from 'react-native';
|
|
3
3
|
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
|
|
4
|
-
import { getWindowInfo } from '@mpxjs/api-proxy';
|
|
5
4
|
import { KeyboardAvoidContext } from './context';
|
|
6
5
|
import { isIOS } from './utils';
|
|
7
6
|
const KeyboardAvoidingView = ({ children, style, contentContainerStyle, navigation }) => {
|
|
@@ -11,7 +10,8 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle, navigati
|
|
|
11
10
|
const basic = useSharedValue('auto');
|
|
12
11
|
const keyboardAvoid = useContext(KeyboardAvoidContext);
|
|
13
12
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
14
|
-
|
|
13
|
+
// translate/position top可能会导致地步渲染区域缺失
|
|
14
|
+
marginTop: -offset.value,
|
|
15
15
|
flexBasis: basic.value
|
|
16
16
|
}));
|
|
17
17
|
const resetKeyboard = () => {
|
|
@@ -22,11 +22,6 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle, navigati
|
|
|
22
22
|
inputRef.blur();
|
|
23
23
|
}
|
|
24
24
|
keyboardAvoid.current = null;
|
|
25
|
-
navigation.setPageConfig({
|
|
26
|
-
animatedNavStyle: {
|
|
27
|
-
top: withTiming(0, { duration: 100, easing: Easing.in(Easing.bezierFn(0.51, 1.18, 0.97, 0.94)) })
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
25
|
}
|
|
31
26
|
offset.value = withTiming(0, { duration, easing });
|
|
32
27
|
basic.value = 'auto';
|
|
@@ -54,7 +49,7 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle, navigati
|
|
|
54
49
|
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
|
|
55
50
|
const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing);
|
|
56
51
|
const value = aboveOffset > 0 ? belowValue : aboveValue;
|
|
57
|
-
offset.value = withTiming(value, { duration, easing },
|
|
52
|
+
offset.value = withTiming(value, { duration, easing }, finished => {
|
|
58
53
|
if (finished) {
|
|
59
54
|
// Set flexBasic after animation to trigger re-layout and reset layout information
|
|
60
55
|
basic.value = '99.99%';
|
|
@@ -74,39 +69,22 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle, navigati
|
|
|
74
69
|
return;
|
|
75
70
|
const { endCoordinates } = evt;
|
|
76
71
|
const { ref, cursorSpacing = 0, adjustPosition, onKeyboardShow } = keyboardAvoid.current;
|
|
77
|
-
// android 上键盘消失只能使用 keyboardDidHide 事件,对于需要和键盘一起改变位置的 nav 来说
|
|
78
|
-
// keyboardDidHide 是比较晚的,从动画上看也并不同步,因此采用比较早的blur
|
|
79
|
-
keyboardAvoid.current.blurCallbacks.push(resetKeyboard);
|
|
80
72
|
keyboardAvoid.current.keyboardHeight = endCoordinates.height;
|
|
81
73
|
onKeyboardShow?.();
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
|
|
90
|
-
const belowValue = Math.min(belowOffset, cursorSpacing);
|
|
91
|
-
const value = aboveOffset > 0 ? belowValue : aboveValue;
|
|
92
|
-
navigation.setPageConfig({
|
|
93
|
-
animatedNavStyle: {
|
|
94
|
-
// android 手机本身支持将页面整体上移(包含 nav 和 body)
|
|
95
|
-
// mpx-keyboard-avoiding-view 和 nav 使用 transform 时互不影响,因此这里只需要计算 android 键盘出现导致上移的高度即可
|
|
96
|
-
top: withTiming(navAboveOffset, {
|
|
97
|
-
duration: 100,
|
|
98
|
-
easing
|
|
99
|
-
})
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
offset.value = withTiming(value, { duration, easing }, (finished) => {
|
|
74
|
+
ref?.current?.measure((x, y, width, height, pageX, pageY) => {
|
|
75
|
+
const aboveOffset = offset.value + pageY + height - endCoordinates.screenY;
|
|
76
|
+
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
|
|
77
|
+
const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing);
|
|
78
|
+
const value = aboveOffset > 0 ? belowValue : aboveValue;
|
|
79
|
+
if (adjustPosition) {
|
|
80
|
+
offset.value = withTiming(value, { duration, easing }, finished => {
|
|
103
81
|
if (finished) {
|
|
104
82
|
// Set flexBasic after animation to trigger re-layout and reset layout information
|
|
105
83
|
basic.value = '99.99%';
|
|
106
84
|
}
|
|
107
85
|
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
110
88
|
}),
|
|
111
89
|
Keyboard.addListener('keyboardDidHide', resetKeyboard)
|
|
112
90
|
];
|
|
@@ -116,12 +94,7 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle, navigati
|
|
|
116
94
|
};
|
|
117
95
|
}, [keyboardAvoid]);
|
|
118
96
|
return (<View style={style} onTouchEnd={onTouchEnd} onTouchMove={onTouchEnd}>
|
|
119
|
-
<Animated.View style={[
|
|
120
|
-
contentContainerStyle,
|
|
121
|
-
animatedStyle
|
|
122
|
-
]}>
|
|
123
|
-
{children}
|
|
124
|
-
</Animated.View>
|
|
97
|
+
<Animated.View style={[contentContainerStyle, animatedStyle]}>{children}</Animated.View>
|
|
125
98
|
</View>);
|
|
126
99
|
};
|
|
127
100
|
KeyboardAvoidingView.displayName = 'MpxKeyboardAvoidingView';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
interface MpxNavContainerProps {
|
|
3
|
+
children?: React.ReactNode;
|
|
4
|
+
}
|
|
5
|
+
export default function MpxNavContainer(props: MpxNavContainerProps): import("react").ReactNode;
|
|
6
|
+
export declare function NavSharedProvider({ children }: {
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useNavShared } from './useNavShared';
|
|
2
|
+
import { NavSharedContext } from './context';
|
|
3
|
+
import { useLayoutEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { isAndroid } from './utils';
|
|
5
|
+
export default function MpxNavContainer(props) {
|
|
6
|
+
const [, setCustomNav] = useNavShared();
|
|
7
|
+
useLayoutEffect(() => {
|
|
8
|
+
if (!isAndroid)
|
|
9
|
+
return;
|
|
10
|
+
if (props.children) {
|
|
11
|
+
setCustomNav(props.children);
|
|
12
|
+
}
|
|
13
|
+
return () => {
|
|
14
|
+
setCustomNav(undefined);
|
|
15
|
+
};
|
|
16
|
+
}, [props.children]);
|
|
17
|
+
return isAndroid ? null : props.children;
|
|
18
|
+
}
|
|
19
|
+
export function NavSharedProvider({ children }) {
|
|
20
|
+
const [customNav, setCustomNav] = useState();
|
|
21
|
+
const value = useMemo(() => ({ customNav, setCustomNav }), [customNav]);
|
|
22
|
+
return <NavSharedContext.Provider value={value}>{children}</NavSharedContext.Provider>;
|
|
23
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createElement, useState, useMemo, memo } from 'react';
|
|
3
3
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
4
4
|
import { StatusBar, processColor, TouchableWithoutFeedback, Image, View, StyleSheet, Text } from 'react-native';
|
|
5
|
-
import
|
|
5
|
+
import { useNavShared } from './useNavShared';
|
|
6
6
|
function convertToHex(color) {
|
|
7
7
|
try {
|
|
8
8
|
const intColor = processColor(color);
|
|
@@ -83,16 +83,10 @@ function createMpxNav(options) {
|
|
|
83
83
|
const { Mpx } = options;
|
|
84
84
|
const innerNav = memo(({ pageConfig, navigation }) => {
|
|
85
85
|
const [innerPageConfig, setPageConfig] = useState(pageConfig || {});
|
|
86
|
-
const
|
|
86
|
+
const [customNav] = useNavShared();
|
|
87
87
|
const safeAreaTop = useSafeAreaInsets()?.top || 0;
|
|
88
|
-
const animatedStyle = useAnimatedStyle(() => ({
|
|
89
|
-
transform: [{ translateY: translateY.value }]
|
|
90
|
-
}));
|
|
91
88
|
navigation.setPageConfig = (config) => {
|
|
92
|
-
|
|
93
|
-
translateY.value =
|
|
94
|
-
newConfig?.animatedNavStyle?.top ?? withTiming(0, { duration: 100, easing: Easing.in(Easing.bezierFn(0.51, 1.18, 0.97, 0.94)) });
|
|
95
|
-
setPageConfig(newConfig);
|
|
89
|
+
setPageConfig(Object.assign({}, innerPageConfig, config));
|
|
96
90
|
};
|
|
97
91
|
const isCustom = innerPageConfig.navigationStyle === 'custom';
|
|
98
92
|
const navigationBarTextStyle = useMemo(() => validBarTextStyle(innerPageConfig.navigationBarTextStyle), [innerPageConfig.navigationBarTextStyle]);
|
|
@@ -104,7 +98,10 @@ function createMpxNav(options) {
|
|
|
104
98
|
barStyle: navigationBarTextStyle === NavColor.White ? 'light-content' : 'dark-content' // 'default'/'light-content'/'dark-content'
|
|
105
99
|
});
|
|
106
100
|
if (isCustom)
|
|
107
|
-
return
|
|
101
|
+
return (<>
|
|
102
|
+
{statusBarElement}
|
|
103
|
+
{customNav}
|
|
104
|
+
</>);
|
|
108
105
|
// 假设是栈导航,获取栈的长度
|
|
109
106
|
const stackLength = navigation.getState()?.routes?.length;
|
|
110
107
|
const onStackTopBack = Mpx.config?.rnConfig?.onStackTopBack;
|
|
@@ -124,28 +121,24 @@ function createMpxNav(options) {
|
|
|
124
121
|
</View>
|
|
125
122
|
</TouchableWithoutFeedback>)
|
|
126
123
|
: null;
|
|
127
|
-
return (
|
|
128
|
-
// 不设置 zIndex transform 无法生效
|
|
129
|
-
<Animated.View style={[{ position: 'relative', zIndex: 10000 }, animatedStyle]}>
|
|
130
|
-
<View style={[
|
|
124
|
+
return (<View style={[
|
|
131
125
|
styles.header,
|
|
132
126
|
{
|
|
133
127
|
paddingTop: safeAreaTop,
|
|
134
128
|
backgroundColor: innerPageConfig.navigationBarBackgroundColor || '#000000'
|
|
135
129
|
}
|
|
136
130
|
]}>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
</View>
|
|
131
|
+
{statusBarElement}
|
|
132
|
+
{/* TODO: 确定 height 的有效性 */}
|
|
133
|
+
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
|
|
134
|
+
{/* @ts-expect-error */}
|
|
135
|
+
<View style={styles.headerContent} height={titleHeight}>
|
|
136
|
+
{backElement}
|
|
137
|
+
<Text style={[styles.title, { color: navigationBarTextStyle }]} numberOfLines={1}>
|
|
138
|
+
{innerPageConfig.navigationBarTitleText?.trim() || ''}
|
|
139
|
+
</Text>
|
|
147
140
|
</View>
|
|
148
|
-
</
|
|
141
|
+
</View>);
|
|
149
142
|
// return createElement(
|
|
150
143
|
// Animated.View,
|
|
151
144
|
// {
|
|
@@ -281,7 +281,7 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
|
|
|
281
281
|
|
|
282
282
|
const setKeyboardAvoidContext = () => {
|
|
283
283
|
if (keyboardAvoid) {
|
|
284
|
-
keyboardAvoid.current = { cursorSpacing, ref: nodeRef, adjustPosition
|
|
284
|
+
keyboardAvoid.current = { cursorSpacing, ref: nodeRef, adjustPosition }
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
|
|
@@ -298,6 +298,7 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
|
|
|
298
298
|
if (!keyboardAvoid?.current) {
|
|
299
299
|
setKeyboardAvoidContext()
|
|
300
300
|
}
|
|
301
|
+
|
|
301
302
|
if (bindfocus && keyboardAvoid?.current) {
|
|
302
303
|
const focusAction = () => {
|
|
303
304
|
bindfocus(
|
|
@@ -307,7 +308,7 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
|
|
|
307
308
|
{
|
|
308
309
|
detail: {
|
|
309
310
|
value: tmpValue.current || '',
|
|
310
|
-
height: keyboardAvoid.current?.keyboardHeight
|
|
311
|
+
height: keyboardAvoid.current?.keyboardHeight,
|
|
311
312
|
},
|
|
312
313
|
layoutRef
|
|
313
314
|
},
|
|
@@ -330,7 +331,6 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
|
|
|
330
331
|
}
|
|
331
332
|
|
|
332
333
|
const onBlur = (evt: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
333
|
-
keyboardAvoid?.current?.blurCallbacks.forEach(fn => fn())
|
|
334
334
|
bindblur && bindblur(
|
|
335
335
|
getCustomEvent(
|
|
336
336
|
'blur',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { ReactNode, useContext, useEffect } from 'react'
|
|
2
2
|
import { DimensionValue, EmitterSubscription, Keyboard, View, ViewStyle, NativeSyntheticEvent, NativeTouchEvent } from 'react-native'
|
|
3
3
|
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated'
|
|
4
|
-
import { getWindowInfo } from '@mpxjs/api-proxy'
|
|
5
4
|
import { KeyboardAvoidContext } from './context'
|
|
6
5
|
import { isIOS } from './utils'
|
|
7
6
|
|
|
@@ -21,7 +20,8 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle, navigati
|
|
|
21
20
|
const keyboardAvoid = useContext(KeyboardAvoidContext)
|
|
22
21
|
|
|
23
22
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
24
|
-
|
|
23
|
+
// translate/position top可能会导致地步渲染区域缺失
|
|
24
|
+
marginTop: -offset.value,
|
|
25
25
|
flexBasis: basic.value as DimensionValue
|
|
26
26
|
}))
|
|
27
27
|
|
|
@@ -33,11 +33,6 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle, navigati
|
|
|
33
33
|
inputRef.blur()
|
|
34
34
|
}
|
|
35
35
|
keyboardAvoid.current = null
|
|
36
|
-
navigation.setPageConfig({
|
|
37
|
-
animatedNavStyle: {
|
|
38
|
-
top: withTiming(0, { duration: 100, easing: Easing.in(Easing.bezierFn(0.51, 1.18, 0.97, 0.94)) })
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
36
|
}
|
|
42
37
|
offset.value = withTiming(0, { duration, easing })
|
|
43
38
|
basic.value = 'auto'
|
|
@@ -67,7 +62,7 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle, navigati
|
|
|
67
62
|
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
|
|
68
63
|
const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing)
|
|
69
64
|
const value = aboveOffset > 0 ? belowValue : aboveValue
|
|
70
|
-
offset.value = withTiming(value, { duration, easing },
|
|
65
|
+
offset.value = withTiming(value, { duration, easing }, finished => {
|
|
71
66
|
if (finished) {
|
|
72
67
|
// Set flexBasic after animation to trigger re-layout and reset layout information
|
|
73
68
|
basic.value = '99.99%'
|
|
@@ -85,43 +80,23 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle, navigati
|
|
|
85
80
|
if (!keyboardAvoid?.current) return
|
|
86
81
|
const { endCoordinates } = evt
|
|
87
82
|
const { ref, cursorSpacing = 0, adjustPosition, onKeyboardShow } = keyboardAvoid.current
|
|
88
|
-
// android 上键盘消失只能使用 keyboardDidHide 事件,对于需要和键盘一起改变位置的 nav 来说
|
|
89
|
-
// keyboardDidHide 是比较晚的,从动画上看也并不同步,因此采用比较早的blur
|
|
90
|
-
keyboardAvoid.current.blurCallbacks.push(resetKeyboard)
|
|
91
83
|
keyboardAvoid.current.keyboardHeight = endCoordinates.height
|
|
92
84
|
onKeyboardShow?.()
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const navAboveOffset = pageY + height - Math.floor(endCoordinates.screenY * screenHeightRatio)
|
|
99
|
-
|
|
100
|
-
const aboveOffset = pageY + height - endCoordinates.screenY
|
|
101
|
-
const belowOffset = endCoordinates.height - aboveOffset
|
|
102
|
-
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
|
|
103
|
-
const belowValue = Math.min(belowOffset, cursorSpacing)
|
|
104
|
-
const value = aboveOffset > 0 ? belowValue : aboveValue
|
|
85
|
+
ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
|
|
86
|
+
const aboveOffset = offset.value + pageY + height - endCoordinates.screenY
|
|
87
|
+
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
|
|
88
|
+
const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing)
|
|
89
|
+
const value = aboveOffset > 0 ? belowValue : aboveValue
|
|
105
90
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// android 手机本身支持将页面整体上移(包含 nav 和 body)
|
|
109
|
-
// mpx-keyboard-avoiding-view 和 nav 使用 transform 时互不影响,因此这里只需要计算 android 键盘出现导致上移的高度即可
|
|
110
|
-
top: withTiming(navAboveOffset, {
|
|
111
|
-
duration: 100,
|
|
112
|
-
easing
|
|
113
|
-
})
|
|
114
|
-
}
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
offset.value = withTiming(value, { duration, easing }, (finished) => {
|
|
91
|
+
if (adjustPosition) {
|
|
92
|
+
offset.value = withTiming(value, { duration, easing }, finished => {
|
|
118
93
|
if (finished) {
|
|
119
94
|
// Set flexBasic after animation to trigger re-layout and reset layout information
|
|
120
95
|
basic.value = '99.99%'
|
|
121
96
|
}
|
|
122
97
|
})
|
|
123
|
-
}
|
|
124
|
-
}
|
|
98
|
+
}
|
|
99
|
+
})
|
|
125
100
|
}),
|
|
126
101
|
Keyboard.addListener('keyboardDidHide', resetKeyboard)
|
|
127
102
|
]
|
|
@@ -134,14 +109,7 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle, navigati
|
|
|
134
109
|
|
|
135
110
|
return (
|
|
136
111
|
<View style={style} onTouchEnd={onTouchEnd} onTouchMove={onTouchEnd}>
|
|
137
|
-
<Animated.View
|
|
138
|
-
style={[
|
|
139
|
-
contentContainerStyle,
|
|
140
|
-
animatedStyle
|
|
141
|
-
]}
|
|
142
|
-
>
|
|
143
|
-
{children}
|
|
144
|
-
</Animated.View>
|
|
112
|
+
<Animated.View style={[contentContainerStyle, animatedStyle]}>{children}</Animated.View>
|
|
145
113
|
</View>
|
|
146
114
|
)
|
|
147
115
|
}
|
|
@@ -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
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* eslint-disable space-before-function-paren */
|
|
2
|
-
import { createElement, useState, useMemo, memo } from 'react'
|
|
2
|
+
import { createElement, useState, useMemo, memo, useContext, useLayoutEffect } from 'react'
|
|
3
3
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
4
4
|
import { StatusBar, processColor, TouchableWithoutFeedback, Image, View, StyleSheet, Text } from 'react-native'
|
|
5
|
-
import
|
|
5
|
+
import { useNavShared } from './useNavShared'
|
|
6
6
|
|
|
7
7
|
function convertToHex(color?: string) {
|
|
8
8
|
try {
|
|
@@ -95,19 +95,11 @@ function createMpxNav(options: MpxNavFactorOptions) {
|
|
|
95
95
|
const { Mpx } = options
|
|
96
96
|
const innerNav = memo(({ pageConfig, navigation }: MpxNavProps) => {
|
|
97
97
|
const [innerPageConfig, setPageConfig] = useState<PageConfig>(pageConfig || {})
|
|
98
|
-
|
|
99
|
-
const translateY = useSharedValue(0)
|
|
98
|
+
const [customNav] = useNavShared()
|
|
100
99
|
const safeAreaTop = useSafeAreaInsets()?.top || 0
|
|
101
100
|
|
|
102
|
-
const animatedStyle = useAnimatedStyle(() => ({
|
|
103
|
-
transform: [{ translateY: translateY.value }]
|
|
104
|
-
}))
|
|
105
|
-
|
|
106
101
|
navigation.setPageConfig = (config: PageConfig) => {
|
|
107
|
-
|
|
108
|
-
translateY.value =
|
|
109
|
-
newConfig?.animatedNavStyle?.top ?? withTiming(0, { duration: 100, easing: Easing.in(Easing.bezierFn(0.51, 1.18, 0.97, 0.94)) })
|
|
110
|
-
setPageConfig(newConfig)
|
|
102
|
+
setPageConfig(Object.assign({}, innerPageConfig, config))
|
|
111
103
|
}
|
|
112
104
|
const isCustom = innerPageConfig.navigationStyle === 'custom'
|
|
113
105
|
const navigationBarTextStyle = useMemo(() => validBarTextStyle(innerPageConfig.navigationBarTextStyle), [innerPageConfig.navigationBarTextStyle])
|
|
@@ -124,7 +116,13 @@ function createMpxNav(options: MpxNavFactorOptions) {
|
|
|
124
116
|
barStyle: navigationBarTextStyle === NavColor.White ? 'light-content' : 'dark-content' // 'default'/'light-content'/'dark-content'
|
|
125
117
|
})
|
|
126
118
|
|
|
127
|
-
if (isCustom)
|
|
119
|
+
if (isCustom)
|
|
120
|
+
return (
|
|
121
|
+
<>
|
|
122
|
+
{statusBarElement}
|
|
123
|
+
{customNav}
|
|
124
|
+
</>
|
|
125
|
+
)
|
|
128
126
|
// 假设是栈导航,获取栈的长度
|
|
129
127
|
const stackLength = navigation.getState()?.routes?.length
|
|
130
128
|
const onStackTopBack = Mpx.config?.rnConfig?.onStackTopBack
|
|
@@ -150,28 +148,25 @@ function createMpxNav(options: MpxNavFactorOptions) {
|
|
|
150
148
|
: null
|
|
151
149
|
|
|
152
150
|
return (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
{
|
|
167
|
-
<
|
|
168
|
-
{
|
|
169
|
-
|
|
170
|
-
{innerPageConfig.navigationBarTitleText?.trim() || ''}
|
|
171
|
-
</Text>
|
|
172
|
-
</View>
|
|
151
|
+
<View
|
|
152
|
+
style={[
|
|
153
|
+
styles.header,
|
|
154
|
+
{
|
|
155
|
+
paddingTop: safeAreaTop,
|
|
156
|
+
backgroundColor: innerPageConfig.navigationBarBackgroundColor || '#000000'
|
|
157
|
+
}
|
|
158
|
+
]}>
|
|
159
|
+
{statusBarElement}
|
|
160
|
+
{/* TODO: 确定 height 的有效性 */}
|
|
161
|
+
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
|
|
162
|
+
{/* @ts-expect-error */}
|
|
163
|
+
<View style={styles.headerContent} height={titleHeight}>
|
|
164
|
+
{backElement}
|
|
165
|
+
<Text style={[styles.title, { color: navigationBarTextStyle }]} numberOfLines={1}>
|
|
166
|
+
{innerPageConfig.navigationBarTitleText?.trim() || ''}
|
|
167
|
+
</Text>
|
|
173
168
|
</View>
|
|
174
|
-
</
|
|
169
|
+
</View>
|
|
175
170
|
)
|
|
176
171
|
|
|
177
172
|
// return createElement(
|
|
@@ -78,7 +78,7 @@ const isBuildInWebTag = makeMap(
|
|
|
78
78
|
'mpx-swiper,mpx-view,mpx-checkbox-group,mpx-movable-area,mpx-radio-group,' +
|
|
79
79
|
'mpx-switch,mpx-web-view,mpx-checkbox,mpx-movable-view,mpx-radio,' +
|
|
80
80
|
'mpx-tab-bar-container,mpx-form,mpx-navigator,mpx-rich-text,mpx-tab-bar,' +
|
|
81
|
-
'mpx-icon,mpx-picker-view-column,mpx-scroll-view,mpx-text'
|
|
81
|
+
'mpx-icon,mpx-picker-view-column,mpx-scroll-view,mpx-text,mpx-nav-container'
|
|
82
82
|
)
|
|
83
83
|
|
|
84
84
|
/**
|
|
@@ -91,7 +91,7 @@ const isBuildInReactTag = makeMap(
|
|
|
91
91
|
'mpx-movable-area,mpx-label,mpx-keyboard-avoiding-view,mpx-input,mpx-inline-text,' +
|
|
92
92
|
'mpx-image,mpx-form,mpx-checkbox,mpx-checkbox-group,mpx-button,' +
|
|
93
93
|
'mpx-rich-text,mpx-portal,mpx-popup,mpx-picker-view-column,mpx-picker-view,mpx-picker,' +
|
|
94
|
-
'mpx-icon,mpx-canvas'
|
|
94
|
+
'mpx-icon,mpx-canvas,mpx-nav-container'
|
|
95
95
|
)
|
|
96
96
|
|
|
97
97
|
const isSpace = makeMap('ensp,emsp,nbsp')
|