@mpxjs/webpack-plugin 2.9.69-beta.1 → 2.9.69-beta.11
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/config.js +3 -1
- package/lib/platform/template/wx/index.js +3 -1
- package/lib/react/processScript.js +6 -4
- package/lib/runtime/components/react/context.ts +17 -0
- package/lib/runtime/components/react/dist/context.js +2 -0
- package/lib/runtime/components/react/dist/getInnerListeners.js +2 -2
- package/lib/runtime/components/react/dist/locale-provider.jsx +15 -0
- package/lib/runtime/components/react/dist/mpx-button.jsx +16 -44
- package/lib/runtime/components/react/dist/mpx-image.jsx +13 -9
- package/lib/runtime/components/react/dist/mpx-picker/time.jsx +2 -1
- package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +65 -61
- package/lib/runtime/components/react/dist/mpx-portal/portal-consumer.jsx +23 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +93 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +40 -0
- package/lib/runtime/components/react/dist/mpx-portal.jsx +17 -0
- package/lib/runtime/components/react/dist/mpx-provider.jsx +31 -0
- package/lib/runtime/components/react/dist/mpx-root-portal.jsx +5 -3
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +9 -5
- package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +13 -7
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +384 -321
- package/lib/runtime/components/react/dist/mpx-view.jsx +16 -20
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +129 -53
- package/lib/runtime/components/react/dist/pickerFaces.js +2 -7
- package/lib/runtime/components/react/dist/useAnimationHooks.js +30 -13
- package/lib/runtime/components/react/dist/utils.jsx +60 -2
- package/lib/runtime/components/react/getInnerListeners.ts +2 -2
- package/lib/runtime/components/react/locale-provider.tsx +83 -0
- package/lib/runtime/components/react/mpx-button.tsx +20 -57
- package/lib/runtime/components/react/mpx-image.tsx +41 -25
- package/lib/runtime/components/react/mpx-picker/time.tsx +2 -1
- package/lib/runtime/components/react/mpx-picker-view-column-item.tsx +3 -3
- package/lib/runtime/components/react/mpx-picker-view-column.tsx +70 -69
- package/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +32 -0
- package/lib/runtime/components/react/mpx-portal/portal-host.tsx +127 -0
- package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +64 -0
- package/lib/runtime/components/react/mpx-portal.tsx +35 -0
- package/lib/runtime/components/react/mpx-provider.tsx +51 -0
- package/lib/runtime/components/react/mpx-root-portal.tsx +5 -3
- package/lib/runtime/components/react/mpx-scroll-view.tsx +10 -8
- package/lib/runtime/components/react/mpx-swiper-item.tsx +13 -7
- package/lib/runtime/components/react/mpx-swiper.tsx +378 -325
- package/lib/runtime/components/react/mpx-view.tsx +19 -22
- package/lib/runtime/components/react/mpx-web-view.tsx +170 -62
- package/lib/runtime/components/react/pickerFaces.ts +2 -7
- package/lib/runtime/components/react/types/global.d.ts +7 -0
- package/lib/runtime/components/react/useAnimationHooks.ts +34 -14
- package/lib/runtime/components/react/utils.tsx +67 -2
- package/lib/template-compiler/compiler.js +1 -1
- package/lib/wxss/loader.js +15 -2
- package/package.json +1 -1
package/lib/config.js
CHANGED
|
@@ -138,7 +138,9 @@ module.exports = {
|
|
|
138
138
|
}
|
|
139
139
|
},
|
|
140
140
|
getEvent (eventName, prefix = 'on') {
|
|
141
|
-
return
|
|
141
|
+
return prefix + dash2hump(eventName.replace(/^./, (matched) => {
|
|
142
|
+
return matched.toUpperCase()
|
|
143
|
+
}))
|
|
142
144
|
},
|
|
143
145
|
defaultModelProp: 'value',
|
|
144
146
|
defaultModelEvent: 'input',
|
|
@@ -302,7 +302,9 @@ module.exports = function getSpec ({ warn, error }) {
|
|
|
302
302
|
const rPrefix = runRules(spec.event.prefix, prefix, { mode: 'ali' })
|
|
303
303
|
const rEventName = runRules(eventRules, eventName, { mode: 'ali' })
|
|
304
304
|
return {
|
|
305
|
-
name:
|
|
305
|
+
name: rPrefix + dash2hump(rEventName.replace(/^./, (matched) => {
|
|
306
|
+
return matched.toUpperCase()
|
|
307
|
+
})) + modifierStr,
|
|
306
308
|
value
|
|
307
309
|
}
|
|
308
310
|
},
|
|
@@ -14,25 +14,27 @@ module.exports = function (script, {
|
|
|
14
14
|
localPagesMap
|
|
15
15
|
}, callback) {
|
|
16
16
|
let scriptSrcMode = srcMode
|
|
17
|
+
const mode = loaderContext.getMpx().mode
|
|
17
18
|
if (script) {
|
|
18
19
|
scriptSrcMode = script.mode || scriptSrcMode
|
|
19
20
|
} else {
|
|
20
21
|
script = { tag: 'script' }
|
|
21
22
|
}
|
|
22
|
-
|
|
23
23
|
let output = '/* script */\n'
|
|
24
24
|
if (ctorType === 'app') {
|
|
25
25
|
output += `
|
|
26
26
|
import { getComponent } from ${stringifyRequest(loaderContext, optionProcessorPath)}
|
|
27
27
|
import { NavigationContainer, StackActions } from '@react-navigation/native'
|
|
28
|
-
import { createStackNavigator } from '@react-navigation/stack'
|
|
29
|
-
import {
|
|
28
|
+
${mode === 'ios' ? "import { createNativeStackNavigator } from '@react-navigation/native-stack'" : "import { createStackNavigator } from '@react-navigation/stack'" }
|
|
29
|
+
import { useHeaderHeight } from '@react-navigation/elements';
|
|
30
|
+
import Provider from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-provider'
|
|
30
31
|
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
31
32
|
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
|
32
33
|
|
|
33
34
|
global.__navigationHelper = {
|
|
34
35
|
NavigationContainer: NavigationContainer,
|
|
35
|
-
createStackNavigator: createStackNavigator,
|
|
36
|
+
createStackNavigator: ${mode === 'ios' ? 'createNativeStackNavigator' : 'createStackNavigator'},
|
|
37
|
+
useHeaderHeight: useHeaderHeight,
|
|
36
38
|
StackActions: StackActions,
|
|
37
39
|
GestureHandlerRootView: GestureHandlerRootView,
|
|
38
40
|
Provider: Provider,
|
|
@@ -32,6 +32,19 @@ export interface IntersectionObserver {
|
|
|
32
32
|
throttleMeasure: () => void
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
export interface PortalManagerContextValue {
|
|
36
|
+
mount: (key: number, children: React.ReactNode) => void
|
|
37
|
+
update: (key: number, children: React.ReactNode) => void
|
|
38
|
+
unmount: (key: number) => void,
|
|
39
|
+
portals: Array<{key: number, children: React.ReactNode}>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface PortalContextValue {
|
|
43
|
+
mount: (children: React.ReactNode, key?: number, pageId?: number|null) => number| undefined
|
|
44
|
+
update: (key: number, children: React.ReactNode, pageId?: number|null) => void
|
|
45
|
+
unmount: (key: number) => void
|
|
46
|
+
manager?: PortalManagerContextValue
|
|
47
|
+
}
|
|
35
48
|
|
|
36
49
|
export interface ScrollViewContextValue {
|
|
37
50
|
gestureRef: React.RefObject<any> | null
|
|
@@ -60,3 +73,7 @@ export const SwiperContext = createContext({})
|
|
|
60
73
|
export const KeyboardAvoidContext = createContext<KeyboardAvoidContextValue | null>(null)
|
|
61
74
|
|
|
62
75
|
export const ScrollViewContext = createContext<ScrollViewContextValue>({ gestureRef: null })
|
|
76
|
+
|
|
77
|
+
export const PortalContext = createContext<PortalContextValue>(null as any)
|
|
78
|
+
|
|
79
|
+
export const PortalManagerContext = createContext<PortalManagerContextValue| null>(null)
|
|
@@ -11,3 +11,5 @@ export const RouteContext = createContext(null);
|
|
|
11
11
|
export const SwiperContext = createContext({});
|
|
12
12
|
export const KeyboardAvoidContext = createContext(null);
|
|
13
13
|
export const ScrollViewContext = createContext({ gestureRef: null });
|
|
14
|
+
export const PortalContext = createContext(null);
|
|
15
|
+
export const PortalManagerContext = createContext(null);
|
|
@@ -82,8 +82,8 @@ function checkIsNeedPress(e, type, ref) {
|
|
|
82
82
|
const nativeEvent = e.nativeEvent;
|
|
83
83
|
const currentPageX = nativeEvent.changedTouches[0].pageX;
|
|
84
84
|
const currentPageY = nativeEvent.changedTouches[0].pageY;
|
|
85
|
-
if (Math.abs(currentPageX - tapDetailInfo.x) >
|
|
86
|
-
Math.abs(currentPageY - tapDetailInfo.y) >
|
|
85
|
+
if (Math.abs(currentPageX - tapDetailInfo.x) > 3 ||
|
|
86
|
+
Math.abs(currentPageY - tapDetailInfo.y) > 3) {
|
|
87
87
|
ref.current.needPress[type] = false;
|
|
88
88
|
ref.current.startTimer[type] &&
|
|
89
89
|
clearTimeout(ref.current.startTimer[type]);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createContext, useMemo, memo } from 'react';
|
|
2
|
+
import { extendObject } from './utils';
|
|
3
|
+
export const LocaleContext = createContext(undefined);
|
|
4
|
+
const LocaleProvider = (props) => {
|
|
5
|
+
const locale = useMemo(() => {
|
|
6
|
+
return {
|
|
7
|
+
antLocale: extendObject({}, props.locale, { exist: true })
|
|
8
|
+
};
|
|
9
|
+
}, [props.locale]);
|
|
10
|
+
return (<LocaleContext.Provider value={locale}>
|
|
11
|
+
{props.children}
|
|
12
|
+
</LocaleContext.Provider>);
|
|
13
|
+
};
|
|
14
|
+
LocaleProvider.displayName = 'LocaleProvider';
|
|
15
|
+
export default memo(LocaleProvider);
|
|
@@ -34,10 +34,11 @@
|
|
|
34
34
|
* ✘ bindagreeprivacyauthorization
|
|
35
35
|
* ✔ bindtap
|
|
36
36
|
*/
|
|
37
|
-
import { createElement, useEffect, useRef,
|
|
37
|
+
import { createElement, useEffect, useRef, forwardRef, useContext } from 'react';
|
|
38
38
|
import { View, StyleSheet, Animated, Easing } from 'react-native';
|
|
39
39
|
import { warn } from '@mpxjs/utils';
|
|
40
|
-
import {
|
|
40
|
+
import { GestureDetector } from 'react-native-gesture-handler';
|
|
41
|
+
import { getCurrentPage, splitProps, splitStyle, useLayout, useTransformStyle, wrapChildren, extendObject, useHover } from './utils';
|
|
41
42
|
import useInnerProps, { getCustomEvent } from './getInnerListeners';
|
|
42
43
|
import useNodesRef from './useNodesRef';
|
|
43
44
|
import { RouteContext, FormContext } from './context';
|
|
@@ -128,38 +129,34 @@ const Loading = ({ alone = false }) => {
|
|
|
128
129
|
};
|
|
129
130
|
const Button = forwardRef((buttonProps, ref) => {
|
|
130
131
|
const { textProps, innerProps: props = {} } = splitProps(buttonProps);
|
|
131
|
-
const { size = 'default', type = 'default', plain = false, disabled = false, loading = false, 'hover-class': hoverClass, 'hover-style': hoverStyle = {}, 'hover-start-time': hoverStartTime = 20, 'hover-stay-time': hoverStayTime = 70, 'open-type': openType, 'form-type': formType, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, style = {}, children, bindgetuserinfo, bindtap
|
|
132
|
+
const { size = 'default', type = 'default', plain = false, disabled = false, loading = false, 'hover-class': hoverClass, 'hover-style': hoverStyle = {}, 'hover-start-time': hoverStartTime = 20, 'hover-stay-time': hoverStayTime = 70, 'open-type': openType, 'form-type': formType, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, style = {}, children, bindgetuserinfo, bindtap } = props;
|
|
132
133
|
const pageId = useContext(RouteContext);
|
|
133
134
|
const formContext = useContext(FormContext);
|
|
135
|
+
const enableHover = hoverClass !== 'none';
|
|
136
|
+
const { isHover, gesture } = useHover({ enableHover, hoverStartTime, hoverStayTime, disabled });
|
|
134
137
|
let submitFn;
|
|
135
138
|
let resetFn;
|
|
136
139
|
if (formContext) {
|
|
137
140
|
submitFn = formContext.submit;
|
|
138
141
|
resetFn = formContext.reset;
|
|
139
142
|
}
|
|
140
|
-
const refs = useRef({
|
|
141
|
-
hoverStartTimer: undefined,
|
|
142
|
-
hoverStayTimer: undefined
|
|
143
|
-
});
|
|
144
|
-
const [isHover, setIsHover] = useState(false);
|
|
145
143
|
const isMiniSize = size === 'mini';
|
|
146
|
-
const applyHoverEffect = isHover && hoverClass !== 'none';
|
|
147
144
|
const [color, hoverColor, plainColor, disabledColor] = TypeColorMap[type];
|
|
148
|
-
const normalBackgroundColor = disabled ? disabledColor :
|
|
145
|
+
const normalBackgroundColor = disabled ? disabledColor : isHover || loading ? hoverColor : color;
|
|
149
146
|
const plainBorderColor = disabled
|
|
150
147
|
? 'rgba(0, 0, 0, .2)'
|
|
151
|
-
:
|
|
148
|
+
: isHover
|
|
152
149
|
? `rgba(${plainColor},.6)`
|
|
153
150
|
: `rgb(${plainColor})`;
|
|
154
151
|
const normalBorderColor = type === 'default' ? 'rgba(0, 0, 0, .2)' : normalBackgroundColor;
|
|
155
152
|
const plainTextColor = disabled
|
|
156
153
|
? 'rgba(0, 0, 0, .2)'
|
|
157
|
-
:
|
|
154
|
+
: isHover
|
|
158
155
|
? `rgba(${plainColor}, .6)`
|
|
159
156
|
: `rgb(${plainColor})`;
|
|
160
157
|
const normalTextColor = type === 'default'
|
|
161
|
-
? `rgba(0, 0, 0, ${disabled ? 0.3 :
|
|
162
|
-
: `rgba(255 ,255 ,255 , ${disabled ||
|
|
158
|
+
? `rgba(0, 0, 0, ${disabled ? 0.3 : isHover || loading ? 0.6 : 1})`
|
|
159
|
+
: `rgba(255 ,255 ,255 , ${disabled || isHover || loading ? 0.6 : 1})`;
|
|
163
160
|
const viewStyle = {
|
|
164
161
|
borderWidth: 1,
|
|
165
162
|
borderStyle: 'solid',
|
|
@@ -169,7 +166,7 @@ const Button = forwardRef((buttonProps, ref) => {
|
|
|
169
166
|
const defaultViewStyle = extendObject({}, styles.button, isMiniSize ? styles.buttonMini : null, viewStyle);
|
|
170
167
|
const defaultTextStyle = extendObject({}, styles.text, isMiniSize ? styles.textMini : {}, { color: plain ? plainTextColor : normalTextColor });
|
|
171
168
|
const defaultStyle = extendObject({}, defaultViewStyle, defaultTextStyle);
|
|
172
|
-
const styleObj = extendObject({}, defaultStyle, style,
|
|
169
|
+
const styleObj = extendObject({}, defaultStyle, style, isHover ? hoverStyle : {});
|
|
173
170
|
const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
174
171
|
const nodeRef = useRef(null);
|
|
175
172
|
useNodesRef(props, ref, nodeRef, { style: normalStyle });
|
|
@@ -224,32 +221,6 @@ const Button = forwardRef((buttonProps, ref) => {
|
|
|
224
221
|
});
|
|
225
222
|
}
|
|
226
223
|
};
|
|
227
|
-
const setStayTimer = () => {
|
|
228
|
-
clearTimeout(refs.current.hoverStayTimer);
|
|
229
|
-
refs.current.hoverStayTimer = setTimeout(() => {
|
|
230
|
-
setIsHover(false);
|
|
231
|
-
clearTimeout(refs.current.hoverStayTimer);
|
|
232
|
-
}, hoverStayTime);
|
|
233
|
-
};
|
|
234
|
-
const setStartTimer = () => {
|
|
235
|
-
clearTimeout(refs.current.hoverStartTimer);
|
|
236
|
-
refs.current.hoverStartTimer = setTimeout(() => {
|
|
237
|
-
setIsHover(true);
|
|
238
|
-
clearTimeout(refs.current.hoverStartTimer);
|
|
239
|
-
}, hoverStartTime);
|
|
240
|
-
};
|
|
241
|
-
const onTouchStart = (evt) => {
|
|
242
|
-
bindtouchstart && bindtouchstart(evt);
|
|
243
|
-
if (disabled)
|
|
244
|
-
return;
|
|
245
|
-
setStartTimer();
|
|
246
|
-
};
|
|
247
|
-
const onTouchEnd = (evt) => {
|
|
248
|
-
bindtouchend && bindtouchend(evt);
|
|
249
|
-
if (disabled)
|
|
250
|
-
return;
|
|
251
|
-
setStayTimer();
|
|
252
|
-
};
|
|
253
224
|
const handleFormTypeFn = () => {
|
|
254
225
|
if (formType === 'submit') {
|
|
255
226
|
submitFn && submitFn();
|
|
@@ -269,8 +240,6 @@ const Button = forwardRef((buttonProps, ref) => {
|
|
|
269
240
|
ref: nodeRef,
|
|
270
241
|
style: extendObject({}, innerStyle, layoutStyle)
|
|
271
242
|
}, layoutProps, {
|
|
272
|
-
bindtouchstart: (bindtouchstart || !disabled) && onTouchStart,
|
|
273
|
-
bindtouchend: (bindtouchend || !disabled) && onTouchEnd,
|
|
274
243
|
bindtap: !disabled && onTap
|
|
275
244
|
}), [
|
|
276
245
|
'disabled',
|
|
@@ -288,12 +257,15 @@ const Button = forwardRef((buttonProps, ref) => {
|
|
|
288
257
|
layoutRef,
|
|
289
258
|
disableTap: disabled
|
|
290
259
|
});
|
|
291
|
-
|
|
260
|
+
const baseButton = createElement(View, innerProps, loading && createElement(Loading, { alone: !children }), wrapChildren(props, {
|
|
292
261
|
hasVarDec,
|
|
293
262
|
varContext: varContextRef.current,
|
|
294
263
|
textStyle,
|
|
295
264
|
textProps
|
|
296
265
|
}));
|
|
266
|
+
return enableHover
|
|
267
|
+
? createElement(GestureDetector, { gesture: gesture }, baseButton)
|
|
268
|
+
: baseButton;
|
|
297
269
|
});
|
|
298
270
|
Button.displayName = 'MpxButton';
|
|
299
271
|
export default Button;
|
|
@@ -268,14 +268,8 @@ const Image = forwardRef((props, ref) => {
|
|
|
268
268
|
], {
|
|
269
269
|
layoutRef
|
|
270
270
|
});
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
uri: src,
|
|
274
|
-
onLayout: onSvgLoad,
|
|
275
|
-
onError: binderror && onSvgError,
|
|
276
|
-
style: extendObject({ transformOrigin: 'top left' }, modeStyle)
|
|
277
|
-
})
|
|
278
|
-
: loaded && renderImage({
|
|
271
|
+
const createBaseImage = (innerProps = {}) => {
|
|
272
|
+
return renderImage(extendObject({
|
|
279
273
|
source: { uri: src },
|
|
280
274
|
resizeMode: resizeMode,
|
|
281
275
|
onLoad: bindload && onImageLoad,
|
|
@@ -285,7 +279,17 @@ const Image = forwardRef((props, ref) => {
|
|
|
285
279
|
width: isCropMode ? imageWidth : '100%',
|
|
286
280
|
height: isCropMode ? imageHeight : '100%'
|
|
287
281
|
}, isCropMode ? modeStyle : {})
|
|
288
|
-
}, enableFastImage)
|
|
282
|
+
}, innerProps), enableFastImage);
|
|
283
|
+
};
|
|
284
|
+
const SvgImage = createElement(View, innerProps, createElement(SvgCssUri, {
|
|
285
|
+
uri: src,
|
|
286
|
+
onLayout: onSvgLoad,
|
|
287
|
+
onError: binderror && onSvgError,
|
|
288
|
+
style: extendObject({ transformOrigin: 'top left' }, modeStyle)
|
|
289
|
+
}));
|
|
290
|
+
const BaseImage = createBaseImage(innerProps);
|
|
291
|
+
const LayoutImage = createElement(View, innerProps, loaded && createBaseImage());
|
|
292
|
+
return isSvg ? SvgImage : isLayoutMode ? LayoutImage : BaseImage;
|
|
289
293
|
});
|
|
290
294
|
Image.displayName = 'mpx-image';
|
|
291
295
|
export default Image;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { View, Text, Modal, TouchableWithoutFeedback } from 'react-native';
|
|
2
|
-
import
|
|
2
|
+
import Portal from '../mpx-portal';
|
|
3
|
+
import { PickerView } from '@ant-design/react-native';
|
|
3
4
|
import React, { forwardRef, useState, useRef, useEffect } from 'react';
|
|
4
5
|
import useNodesRef from '../useNodesRef'; // 引入辅助函数
|
|
5
6
|
// 可见应用窗口的大小。
|
|
@@ -3,7 +3,7 @@ import Reanimated, { Extrapolation, interpolate, useAnimatedStyle, useSharedValu
|
|
|
3
3
|
import { wrapChildren, extendObject } from './utils';
|
|
4
4
|
import { createFaces } from './pickerFaces';
|
|
5
5
|
import { usePickerViewColumnAnimationContext } from './pickerVIewContext';
|
|
6
|
-
const _PickerViewColumnItem = ({ item, index, itemHeight, itemWidth, textStyleFromParent, textStyle, hasVarDec, varContext, textProps, visibleCount, onItemLayout }) => {
|
|
6
|
+
const _PickerViewColumnItem = ({ item, index, itemHeight, itemWidth = '100%', textStyleFromParent, textStyle, hasVarDec, varContext, textProps, visibleCount, onItemLayout }) => {
|
|
7
7
|
const offsetYShared = usePickerViewColumnAnimationContext();
|
|
8
8
|
const facesShared = useSharedValue(createFaces(itemHeight, visibleCount));
|
|
9
9
|
useEffect(() => {
|
|
@@ -14,8 +14,8 @@ const _PickerViewColumnItem = ({ item, index, itemHeight, itemWidth, textStyleFr
|
|
|
14
14
|
return {
|
|
15
15
|
opacity: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.opacity), Extrapolation.CLAMP),
|
|
16
16
|
transform: [
|
|
17
|
-
{ rotateX: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.deg), Extrapolation.CLAMP) + 'deg' },
|
|
18
17
|
{ translateY: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.offsetY), Extrapolation.EXTEND) },
|
|
18
|
+
{ rotateX: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.deg), Extrapolation.CLAMP) + 'deg' },
|
|
19
19
|
{ scale: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.scale), Extrapolation.EXTEND) }
|
|
20
20
|
]
|
|
21
21
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react';
|
|
2
2
|
import { SafeAreaView, StyleSheet } from 'react-native';
|
|
3
3
|
import Reanimated, { useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated';
|
|
4
|
-
import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid } from './utils';
|
|
4
|
+
import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, useDebounceCallback, useStableCallback } from './utils';
|
|
5
5
|
import useNodesRef from './useNodesRef';
|
|
6
6
|
import PickerOverlay from './pickerViewOverlay';
|
|
7
7
|
import PickerMask from './pickerViewMask';
|
|
@@ -20,14 +20,12 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
20
20
|
style: normalStyle
|
|
21
21
|
});
|
|
22
22
|
const { height: pickerH, itemHeight } = wrapperStyle;
|
|
23
|
-
const [scrollViewWidth, setScrollViewWidth] = useState('100%');
|
|
24
|
-
const [itemRawW, setItemRawW] = useState('100%');
|
|
25
23
|
const [itemRawH, setItemRawH] = useState(itemHeight);
|
|
26
24
|
const maxIndex = useMemo(() => columnData.length - 1, [columnData]);
|
|
27
|
-
const maxScrollViewWidth = useRef(-1);
|
|
28
25
|
const prevScrollingInfo = useRef({ index: initialIndex, y: 0 });
|
|
29
26
|
const touching = useRef(false);
|
|
30
27
|
const scrolling = useRef(false);
|
|
28
|
+
const timerScrollTo = useRef(null);
|
|
31
29
|
const activeIndex = useRef(initialIndex);
|
|
32
30
|
const prevIndex = usePrevious(initialIndex);
|
|
33
31
|
const prevMaxIndex = usePrevious(maxIndex);
|
|
@@ -39,10 +37,6 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
39
37
|
nodeRef: scrollViewRef
|
|
40
38
|
});
|
|
41
39
|
// console.log('[mpx-picker-view-column], render ---> columnIndex=', columnIndex, 'initialIndex=', initialIndex, 'columnData=', columnData.length, 'pickerH=', pickerH, 'itemRawH=', itemRawH, 'itemHeight=', itemHeight)
|
|
42
|
-
// const initialOffset = useMemo(() => ({
|
|
43
|
-
// x: 0,
|
|
44
|
-
// y: itemRawH * initialIndex
|
|
45
|
-
// }), [itemRawH])
|
|
46
40
|
const paddingHeight = useMemo(() => Math.round((pickerH - itemHeight) / 2), [pickerH, itemHeight]);
|
|
47
41
|
const snapToOffsets = useMemo(() => columnData.map((_, i) => i * itemRawH), [columnData, itemRawH]);
|
|
48
42
|
const contentContainerStyle = useMemo(() => {
|
|
@@ -52,8 +46,38 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
52
46
|
const calc = Math.round(y / itemRawH);
|
|
53
47
|
return Math.max(0, Math.min(calc, maxIndex));
|
|
54
48
|
}, [itemRawH, maxIndex]);
|
|
49
|
+
const getYofIndex = useCallback((index) => {
|
|
50
|
+
return index * itemRawH;
|
|
51
|
+
}, [itemRawH]);
|
|
52
|
+
const stableResetScrollPosition = useStableCallback((y) => {
|
|
53
|
+
// console.log('[mpx-picker-view-column], reset --->', 'columnIndex=', columnIndex, 'y=', y, touching.current, scrolling.current, itemRawH, 'snapToOffsets=', snapToOffsets)
|
|
54
|
+
if (touching.current || scrolling.current) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// needReset.current = true
|
|
58
|
+
if (y % itemRawH !== 0) {
|
|
59
|
+
scrolling.current = true;
|
|
60
|
+
const targetIndex = getIndex(y);
|
|
61
|
+
const targetY = getYofIndex(targetIndex);
|
|
62
|
+
scrollViewRef.current?.scrollTo({ x: 0, y: targetY, animated: false });
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
onMomentumScrollEnd({ nativeEvent: { contentOffset: { y } } });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const debounceResetScrollPosition = useDebounceCallback(stableResetScrollPosition, 10);
|
|
69
|
+
const clearTimerScrollTo = () => {
|
|
70
|
+
if (timerScrollTo.current) {
|
|
71
|
+
clearTimeout(timerScrollTo.current);
|
|
72
|
+
timerScrollTo.current = null;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
return () => {
|
|
77
|
+
clearTimerScrollTo();
|
|
78
|
+
};
|
|
79
|
+
}, []);
|
|
55
80
|
useEffect(() => {
|
|
56
|
-
// console.log('[mpx-picker-view-column], useEffect000 --->', 'columnIndex=', columnIndex, 'initialIndex=', initialIndex, 'prevIndex=', prevIndex, 'activeIndex=', activeIndex.current, 'maxIndex=', maxIndex, 'prevMaxIndex=', prevMaxIndex)
|
|
57
81
|
if (!scrollViewRef.current ||
|
|
58
82
|
!itemRawH ||
|
|
59
83
|
touching.current ||
|
|
@@ -64,89 +88,69 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
64
88
|
maxIndex !== prevMaxIndex) {
|
|
65
89
|
return;
|
|
66
90
|
}
|
|
67
|
-
|
|
68
|
-
setTimeout(() => {
|
|
91
|
+
clearTimerScrollTo();
|
|
92
|
+
timerScrollTo.current = setTimeout(() => {
|
|
69
93
|
scrollViewRef.current?.scrollTo({
|
|
70
94
|
x: 0,
|
|
71
|
-
y:
|
|
95
|
+
y: getYofIndex(initialIndex),
|
|
72
96
|
animated: false
|
|
73
97
|
});
|
|
74
98
|
}, isAndroid ? 200 : 0);
|
|
75
99
|
activeIndex.current = initialIndex;
|
|
76
100
|
}, [itemRawH, initialIndex]);
|
|
77
101
|
const onContentSizeChange = (_w, h) => {
|
|
78
|
-
const y =
|
|
79
|
-
// console.log('[mpx-picker-view-column], onContentSizeChange --->', 'columnIndex=', columnIndex, '_w=', _w, 'h=', h, 'y=', y, 'itemRawH=', itemRawH)
|
|
102
|
+
const y = getYofIndex(initialIndex);
|
|
80
103
|
if (y <= h) {
|
|
81
|
-
|
|
104
|
+
clearTimerScrollTo();
|
|
105
|
+
timerScrollTo.current = setTimeout(() => {
|
|
82
106
|
scrollViewRef.current?.scrollTo({ x: 0, y, animated: false });
|
|
83
107
|
}, 0);
|
|
84
108
|
}
|
|
85
109
|
};
|
|
86
|
-
const onScrollViewLayout = (e) => {
|
|
87
|
-
if (isAndroid) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
// RN iOS bug: https://github.com/facebook/react-native/issues/36135
|
|
91
|
-
const { width } = e.nativeEvent.layout;
|
|
92
|
-
const widthInt = Math.ceil(width);
|
|
93
|
-
// console.log('[mpx-picker-view-column], onScrollViewLayout --->', 'columnIndex=', columnIndex, 'width=', width, 'widthInt=', widthInt, 'scrollViewWidth=', scrollViewWidth)
|
|
94
|
-
if (widthInt !== scrollViewWidth) {
|
|
95
|
-
const maxW = maxScrollViewWidth.current;
|
|
96
|
-
if (maxW !== -1 && widthInt > maxW) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
if (maxW === -1) {
|
|
100
|
-
maxScrollViewWidth.current = Math.ceil(widthInt * 1.5);
|
|
101
|
-
}
|
|
102
|
-
setScrollViewWidth(widthInt);
|
|
103
|
-
}
|
|
104
|
-
if (itemRawW === '100%') {
|
|
105
|
-
setItemRawW(widthInt);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
110
|
const onItemLayout = (e) => {
|
|
109
111
|
const { height: rawH } = e.nativeEvent.layout;
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
setItemRawH(
|
|
112
|
+
const roundedH = Math.round(rawH);
|
|
113
|
+
if (roundedH && roundedH !== itemRawH) {
|
|
114
|
+
setItemRawH(roundedH);
|
|
113
115
|
}
|
|
114
116
|
};
|
|
115
|
-
const
|
|
117
|
+
const onScrollBeginDrag = () => {
|
|
118
|
+
isIOS && debounceResetScrollPosition.clear();
|
|
116
119
|
touching.current = true;
|
|
117
120
|
prevScrollingInfo.current = {
|
|
118
121
|
index: activeIndex.current,
|
|
119
|
-
y: activeIndex.current
|
|
122
|
+
y: getYofIndex(activeIndex.current)
|
|
120
123
|
};
|
|
121
124
|
};
|
|
122
|
-
const
|
|
123
|
-
touching.current = false;
|
|
124
|
-
};
|
|
125
|
-
const onTouchCancel = () => {
|
|
125
|
+
const onScrollEndDrag = (e) => {
|
|
126
126
|
touching.current = false;
|
|
127
|
+
const { y } = e.nativeEvent.contentOffset;
|
|
128
|
+
if (isIOS) {
|
|
129
|
+
if (y >= 0 && y <= snapToOffsets[maxIndex]) {
|
|
130
|
+
debounceResetScrollPosition(y);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
127
133
|
};
|
|
128
134
|
const onMomentumScrollBegin = () => {
|
|
135
|
+
isIOS && debounceResetScrollPosition.clear();
|
|
129
136
|
scrolling.current = true;
|
|
130
137
|
};
|
|
131
138
|
const onMomentumScrollEnd = (e) => {
|
|
132
139
|
scrolling.current = false;
|
|
133
|
-
if (!itemRawH) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
140
|
const { y: scrollY } = e.nativeEvent.contentOffset;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
+
// console.log('[mpx-picker-view-column], onMomentumScrollEnd --->', 'columnIndex=', columnIndex, scrollY, itemRawH)
|
|
142
|
+
if (isIOS && scrollY % itemRawH !== 0) {
|
|
143
|
+
return debounceResetScrollPosition(scrollY);
|
|
144
|
+
}
|
|
145
|
+
const calcIndex = getIndex(scrollY);
|
|
146
|
+
if (calcIndex !== activeIndex.current) {
|
|
147
|
+
activeIndex.current = calcIndex;
|
|
141
148
|
onSelectChange(calcIndex);
|
|
142
149
|
}
|
|
143
150
|
};
|
|
144
151
|
const onScroll = (e) => {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
// 全局注册的震动触感 hook
|
|
149
|
-
const pickerVibrate = global.__mpx.config.rnConfig.pickerVibrate;
|
|
152
|
+
// 全局注册的振动触感 hook
|
|
153
|
+
const pickerVibrate = global.__mpx?.config?.rnConfig?.pickerVibrate;
|
|
150
154
|
if (typeof pickerVibrate !== 'function') {
|
|
151
155
|
return;
|
|
152
156
|
}
|
|
@@ -158,7 +162,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
158
162
|
if (currentId !== prevIndex) {
|
|
159
163
|
prevScrollingInfo.current = {
|
|
160
164
|
index: currentId,
|
|
161
|
-
y: currentId
|
|
165
|
+
y: getYofIndex(currentId)
|
|
162
166
|
};
|
|
163
167
|
// vibrateShort({ type: 'selection' })
|
|
164
168
|
pickerVibrate();
|
|
@@ -167,11 +171,11 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
167
171
|
}
|
|
168
172
|
};
|
|
169
173
|
const renderInnerchild = () => columnData.map((item, index) => {
|
|
170
|
-
return (<MpxPickerVIewColumnItem key={index} item={item} index={index} itemHeight={itemHeight}
|
|
174
|
+
return (<MpxPickerVIewColumnItem key={index} item={item} index={index} itemHeight={itemHeight} textStyleFromParent={textStyleFromParent} textStyle={textStyle} hasVarDec={hasVarDec} varContext={varContextRef.current} textProps={textProps} visibleCount={visibleCount} onItemLayout={onItemLayout}/>);
|
|
171
175
|
});
|
|
172
176
|
const renderScollView = () => {
|
|
173
177
|
return (<PickerViewColumnAnimationContext.Provider value={offsetYShared}>
|
|
174
|
-
<Reanimated.ScrollView ref={scrollViewRef} bounces={true} horizontal={false} nestedScrollEnabled={true} removeClippedSubviews={false} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} scrollEventThrottle={16} {...layoutProps} style={[{ width:
|
|
178
|
+
<Reanimated.ScrollView ref={scrollViewRef} bounces={true} horizontal={false} nestedScrollEnabled={true} removeClippedSubviews={false} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} scrollEventThrottle={16} {...layoutProps} style={[{ width: '100%' }]} decelerationRate="fast" snapToOffsets={snapToOffsets} onScroll={onScroll} onScrollBeginDrag={onScrollBeginDrag} onScrollEndDrag={onScrollEndDrag} onMomentumScrollBegin={onMomentumScrollBegin} onMomentumScrollEnd={onMomentumScrollEnd} onContentSizeChange={onContentSizeChange} contentContainerStyle={contentContainerStyle}>
|
|
175
179
|
{renderInnerchild()}
|
|
176
180
|
</Reanimated.ScrollView>
|
|
177
181
|
</PickerViewColumnAnimationContext.Provider>);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { getFocusedNavigation } from '@mpxjs/utils';
|
|
3
|
+
const PortalConsumer = ({ manager, children }) => {
|
|
4
|
+
const keyRef = useRef(null);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const navigation = getFocusedNavigation();
|
|
7
|
+
const curPageId = navigation?.pageId;
|
|
8
|
+
manager.update(keyRef.current, children, curPageId);
|
|
9
|
+
}, [children]);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (!manager) {
|
|
12
|
+
throw new Error('Looks like you forgot to wrap your root component with `Provider` component from `@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal`.\n\n');
|
|
13
|
+
}
|
|
14
|
+
const navigation = getFocusedNavigation();
|
|
15
|
+
const curPageId = navigation?.pageId;
|
|
16
|
+
keyRef.current = manager.mount(children, undefined, curPageId);
|
|
17
|
+
return () => {
|
|
18
|
+
manager.unmount(keyRef.current);
|
|
19
|
+
};
|
|
20
|
+
}, []);
|
|
21
|
+
return null;
|
|
22
|
+
};
|
|
23
|
+
export default PortalConsumer;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { View, DeviceEventEmitter, NativeEventEmitter, StyleSheet } from 'react-native';
|
|
3
|
+
import PortalManager from './portal-manager';
|
|
4
|
+
import { getFocusedNavigation } from '@mpxjs/utils';
|
|
5
|
+
import { PortalContext } from '../context';
|
|
6
|
+
// events
|
|
7
|
+
const addType = 'MPX_RN_ADD_PORTAL';
|
|
8
|
+
const removeType = 'MPX_RN_REMOVE_PORTAL';
|
|
9
|
+
const updateType = 'MPX_RN_UPDATE_PORTAL';
|
|
10
|
+
// fix react native web does not support DeviceEventEmitter
|
|
11
|
+
const TopViewEventEmitter = DeviceEventEmitter || new NativeEventEmitter();
|
|
12
|
+
const styles = StyleSheet.create({
|
|
13
|
+
container: {
|
|
14
|
+
flex: 1
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
class PortalGuard {
|
|
18
|
+
nextKey = 10000;
|
|
19
|
+
add = (e) => {
|
|
20
|
+
const key = this.nextKey++;
|
|
21
|
+
TopViewEventEmitter.emit(addType, e, key);
|
|
22
|
+
return key;
|
|
23
|
+
};
|
|
24
|
+
remove = (key) => {
|
|
25
|
+
TopViewEventEmitter.emit(removeType, key);
|
|
26
|
+
};
|
|
27
|
+
update = (key, e) => {
|
|
28
|
+
TopViewEventEmitter.emit(updateType, key, e);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* portal
|
|
33
|
+
*/
|
|
34
|
+
export const portal = new PortalGuard();
|
|
35
|
+
const PortalHost = ({ children }) => {
|
|
36
|
+
const _nextKey = useRef(0);
|
|
37
|
+
const _queue = useRef([]);
|
|
38
|
+
const _addType = useRef(null);
|
|
39
|
+
const _removeType = useRef(null);
|
|
40
|
+
const _updateType = useRef(null);
|
|
41
|
+
const manager = useRef(null);
|
|
42
|
+
let currentPageId;
|
|
43
|
+
const _mount = (children, _key, curPageId) => {
|
|
44
|
+
const navigation = getFocusedNavigation();
|
|
45
|
+
const pageId = navigation?.pageId;
|
|
46
|
+
if (pageId !== (curPageId ?? currentPageId)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const key = _key || _nextKey.current++;
|
|
50
|
+
if (manager.current) {
|
|
51
|
+
manager.current.mount(key, children);
|
|
52
|
+
}
|
|
53
|
+
return key;
|
|
54
|
+
};
|
|
55
|
+
const _unmount = (key) => {
|
|
56
|
+
if (manager.current) {
|
|
57
|
+
manager.current.unmount(key);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const _update = (key, children, curPageId) => {
|
|
61
|
+
const navigation = getFocusedNavigation();
|
|
62
|
+
const pageId = navigation?.pageId;
|
|
63
|
+
if (pageId !== (curPageId ?? currentPageId)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (manager.current) {
|
|
67
|
+
manager.current.update(key, children);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const navigation = getFocusedNavigation();
|
|
72
|
+
currentPageId = navigation?.pageId;
|
|
73
|
+
_addType.current = TopViewEventEmitter.addListener(addType, _mount);
|
|
74
|
+
_removeType.current = TopViewEventEmitter.addListener(removeType, _unmount);
|
|
75
|
+
_updateType.current = TopViewEventEmitter.addListener(updateType, _update);
|
|
76
|
+
return () => {
|
|
77
|
+
_addType.current?.remove();
|
|
78
|
+
_removeType.current?.remove();
|
|
79
|
+
_updateType.current?.remove();
|
|
80
|
+
};
|
|
81
|
+
}, []);
|
|
82
|
+
return (<PortalContext.Provider value={{
|
|
83
|
+
mount: _mount,
|
|
84
|
+
update: _update,
|
|
85
|
+
unmount: _unmount
|
|
86
|
+
}}>
|
|
87
|
+
<View style={styles.container} collapsable={false}>
|
|
88
|
+
{children}
|
|
89
|
+
</View>
|
|
90
|
+
<PortalManager ref={manager}/>
|
|
91
|
+
</PortalContext.Provider>);
|
|
92
|
+
};
|
|
93
|
+
export default PortalHost;
|