@mpxjs/webpack-plugin 2.9.70-alpha.0 → 2.9.70-alpha.1
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/index.js +1 -1
- package/lib/loader.js +0 -1
- package/lib/runtime/components/react/dist/context.js +4 -0
- package/lib/runtime/components/react/dist/getInnerListeners.js +5 -6
- package/lib/runtime/components/react/dist/locale-provider.jsx +15 -0
- package/lib/runtime/components/react/dist/mpx-button.jsx +9 -37
- package/lib/runtime/components/react/dist/mpx-image.jsx +13 -9
- package/lib/runtime/components/react/dist/mpx-input.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-picker/time.jsx +2 -1
- package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +56 -55
- package/lib/runtime/components/react/dist/mpx-picker-view.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-portal/portal-consumer.jsx +23 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +124 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +40 -0
- package/lib/runtime/components/react/dist/mpx-portal.jsx +12 -0
- package/lib/runtime/components/react/dist/mpx-provider.jsx +31 -0
- package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +10 -11
- package/lib/runtime/components/react/dist/mpx-root-portal.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +18 -9
- package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +25 -8
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +157 -150
- package/lib/runtime/components/react/dist/mpx-view.jsx +21 -58
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +118 -22
- package/lib/runtime/components/react/dist/pickerFaces.js +2 -2
- package/lib/runtime/components/react/dist/useAnimationHooks.js +15 -3
- package/lib/runtime/components/react/dist/utils.jsx +63 -4
- package/lib/runtime/optionProcessor.tenon.js +3 -3
- package/lib/tenon/index.js +2 -7
- package/lib/tenon/processScript.js +0 -5
- package/lib/tenon/processTemplate.js +1 -2
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1880,7 +1880,7 @@ try {
|
|
|
1880
1880
|
if (tenonStyleLoaderIndex > -1 && !isPitcherRequest) {
|
|
1881
1881
|
loaders.splice(tenonStyleLoaderIndex + 1, 0, {
|
|
1882
1882
|
loader: normalize.lib('style-compiler/index.js'),
|
|
1883
|
-
options: (mpxStyleOptions && JSON.parse(mpxStyleOptions)) || {}
|
|
1883
|
+
options: (queryObj.mpxStyleOptions && JSON.parse(queryObj.mpxStyleOptions)) || {}
|
|
1884
1884
|
})
|
|
1885
1885
|
}
|
|
1886
1886
|
} else if (mpxStyleLoaderIndex === -1) {
|
package/lib/loader.js
CHANGED
|
@@ -8,4 +8,8 @@ export const PickerContext = createContext(null);
|
|
|
8
8
|
export const VarContext = createContext({});
|
|
9
9
|
export const IntersectionObserverContext = createContext(null);
|
|
10
10
|
export const RouteContext = createContext(null);
|
|
11
|
+
export const SwiperContext = createContext({});
|
|
11
12
|
export const KeyboardAvoidContext = createContext(null);
|
|
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]);
|
|
@@ -213,7 +213,6 @@ const useInnerProps = (props = {}, additionalProps = {}, userRemoveProps = [], r
|
|
|
213
213
|
const eventConfig = {};
|
|
214
214
|
const config = rawConfig || {
|
|
215
215
|
layoutRef: { current: {} },
|
|
216
|
-
disableTouch: false,
|
|
217
216
|
disableTap: false
|
|
218
217
|
};
|
|
219
218
|
const removeProps = [
|
|
@@ -237,10 +236,10 @@ const useInnerProps = (props = {}, additionalProps = {}, userRemoveProps = [], r
|
|
|
237
236
|
rawEventKeys.push(key);
|
|
238
237
|
}
|
|
239
238
|
}
|
|
240
|
-
if (!rawEventKeys.length || config.disableTouch) {
|
|
241
|
-
return omit(propsRef.current, removeProps);
|
|
242
|
-
}
|
|
243
239
|
const events = useMemo(() => {
|
|
240
|
+
if (!rawEventKeys.length) {
|
|
241
|
+
return {};
|
|
242
|
+
}
|
|
244
243
|
const transformedEventKeys = rawEventKeys.reduce((acc, key) => {
|
|
245
244
|
if (propsRef.current[key]) {
|
|
246
245
|
return acc.concat(eventConfig[key]);
|
|
@@ -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, useHoverStyle } from './utils';
|
|
41
42
|
import useInnerProps, { getCustomEvent } from './getInnerListeners';
|
|
42
43
|
import useNodesRef from './useNodesRef';
|
|
43
44
|
import { RouteContext, FormContext } from './context';
|
|
@@ -128,20 +129,16 @@ 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 { isHover, enableHoverStyle, gesture } = useHoverStyle({ hoverStyle, hoverStartTime, hoverStayTime, disabled });
|
|
134
136
|
let submitFn;
|
|
135
137
|
let resetFn;
|
|
136
138
|
if (formContext) {
|
|
137
139
|
submitFn = formContext.submit;
|
|
138
140
|
resetFn = formContext.reset;
|
|
139
141
|
}
|
|
140
|
-
const refs = useRef({
|
|
141
|
-
hoverStartTimer: undefined,
|
|
142
|
-
hoverStayTimer: undefined
|
|
143
|
-
});
|
|
144
|
-
const [isHover, setIsHover] = useState(false);
|
|
145
142
|
const isMiniSize = size === 'mini';
|
|
146
143
|
const applyHoverEffect = isHover && hoverClass !== 'none';
|
|
147
144
|
const [color, hoverColor, plainColor, disabledColor] = TypeColorMap[type];
|
|
@@ -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 enableHoverStyle
|
|
267
|
+
? createElement(GestureDetector, { 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;
|
|
@@ -54,7 +54,7 @@ const keyboardTypeMap = {
|
|
|
54
54
|
}) || ''
|
|
55
55
|
};
|
|
56
56
|
const Input = forwardRef((props, ref) => {
|
|
57
|
-
const { style = {}, allowFontScaling = false, type = 'text', value, password, 'placeholder-style': placeholderStyle, disabled, maxlength = 140, 'auto-focus': autoFocus, focus, 'confirm-type': confirmType = 'done', 'confirm-hold': confirmHold = false, cursor, 'cursor-color': cursorColor, 'selection-start': selectionStart = -1, 'selection-end': selectionEnd = -1, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'adjust-position': adjustPosition =
|
|
57
|
+
const { style = {}, allowFontScaling = false, type = 'text', value, password, 'placeholder-style': placeholderStyle, disabled, maxlength = 140, 'auto-focus': autoFocus, focus, 'confirm-type': confirmType = 'done', 'confirm-hold': confirmHold = false, cursor, 'cursor-color': cursorColor, 'selection-start': selectionStart = -1, 'selection-end': selectionEnd = -1, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'adjust-position': adjustPosition = false, bindinput, bindfocus, bindblur, bindconfirm, bindselectionchange,
|
|
58
58
|
// private
|
|
59
59
|
multiline, 'auto-height': autoHeight, bindlinechange } = props;
|
|
60
60
|
const formContext = useContext(FormContext);
|
|
@@ -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(() => {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { SafeAreaView, StyleSheet } from 'react-native';
|
|
3
3
|
import Reanimated, { useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated';
|
|
4
|
-
import {
|
|
5
|
-
import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious } from './utils';
|
|
4
|
+
import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, useDebounceCallback, useStableCallback } from './utils';
|
|
6
5
|
import useNodesRef from './useNodesRef';
|
|
7
6
|
import PickerOverlay from './pickerViewOverlay';
|
|
8
7
|
import PickerMask from './pickerViewMask';
|
|
@@ -21,11 +20,8 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
21
20
|
style: normalStyle
|
|
22
21
|
});
|
|
23
22
|
const { height: pickerH, itemHeight } = wrapperStyle;
|
|
24
|
-
const [scrollViewWidth, setScrollViewWidth] = useState('100%');
|
|
25
|
-
const [itemRawW, setItemRawW] = useState('100%');
|
|
26
23
|
const [itemRawH, setItemRawH] = useState(itemHeight);
|
|
27
24
|
const maxIndex = useMemo(() => columnData.length - 1, [columnData]);
|
|
28
|
-
const maxScrollViewWidth = useRef(-1);
|
|
29
25
|
const prevScrollingInfo = useRef({ index: initialIndex, y: 0 });
|
|
30
26
|
const touching = useRef(false);
|
|
31
27
|
const scrolling = useRef(false);
|
|
@@ -39,13 +35,9 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
39
35
|
setHeight,
|
|
40
36
|
nodeRef: scrollViewRef
|
|
41
37
|
});
|
|
42
|
-
// console.log('[mpx-picker-view-column], render ---> columnIndex=', columnIndex, 'initialIndex=', initialIndex, 'columnData=', columnData.length)
|
|
43
|
-
|
|
44
|
-
// x: 0,
|
|
45
|
-
// y: itemRawH * initialIndex
|
|
46
|
-
// }), [itemRawH])
|
|
38
|
+
// console.log('[mpx-picker-view-column], render ---> columnIndex=', columnIndex, 'initialIndex=', initialIndex, 'columnData=', columnData.length, 'pickerH=', pickerH, 'itemRawH=', itemRawH, 'itemHeight=', itemHeight)
|
|
39
|
+
const paddingHeight = useMemo(() => Math.round((pickerH - itemHeight) / 2), [pickerH, itemHeight]);
|
|
47
40
|
const snapToOffsets = useMemo(() => columnData.map((_, i) => i * itemRawH), [columnData, itemRawH]);
|
|
48
|
-
const paddingHeight = useMemo(() => Math.round((pickerH - itemRawH) / 2), [pickerH, itemRawH]);
|
|
49
41
|
const contentContainerStyle = useMemo(() => {
|
|
50
42
|
return [{ paddingVertical: paddingHeight }];
|
|
51
43
|
}, [paddingHeight]);
|
|
@@ -53,6 +45,26 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
53
45
|
const calc = Math.round(y / itemRawH);
|
|
54
46
|
return Math.max(0, Math.min(calc, maxIndex));
|
|
55
47
|
}, [itemRawH, maxIndex]);
|
|
48
|
+
const getYofIndex = useCallback((index) => {
|
|
49
|
+
return index * itemRawH;
|
|
50
|
+
}, [itemRawH]);
|
|
51
|
+
const stableResetScrollPosition = useStableCallback((y) => {
|
|
52
|
+
// console.log('[mpx-picker-view-column], reset --->', 'columnIndex=', columnIndex, 'y=', y, touching.current, scrolling.current, itemRawH, 'snapToOffsets=', snapToOffsets)
|
|
53
|
+
if (touching.current || scrolling.current) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// needReset.current = true
|
|
57
|
+
if (y % itemRawH !== 0) {
|
|
58
|
+
scrolling.current = true;
|
|
59
|
+
const targetIndex = getIndex(y);
|
|
60
|
+
const targetY = getYofIndex(targetIndex);
|
|
61
|
+
scrollViewRef.current?.scrollTo({ x: 0, y: targetY, animated: false });
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
onMomentumScrollEnd({ nativeEvent: { contentOffset: { y } } });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
const debounceResetScrollPosition = useDebounceCallback(stableResetScrollPosition, 10);
|
|
56
68
|
useEffect(() => {
|
|
57
69
|
if (!scrollViewRef.current ||
|
|
58
70
|
!itemRawH ||
|
|
@@ -64,80 +76,68 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
64
76
|
maxIndex !== prevMaxIndex) {
|
|
65
77
|
return;
|
|
66
78
|
}
|
|
67
|
-
// console.log('[mpx-picker-view-column], useEffect ---> columnIndex=', columnIndex, 'initialIndex=', initialIndex, 'y=', itemRawH * initialIndex, `${scrollViewRef.current?.scrollTo}`)
|
|
68
79
|
setTimeout(() => {
|
|
69
80
|
scrollViewRef.current?.scrollTo({
|
|
70
81
|
x: 0,
|
|
71
|
-
y:
|
|
82
|
+
y: getYofIndex(initialIndex),
|
|
72
83
|
animated: false
|
|
73
84
|
});
|
|
74
|
-
}, 0);
|
|
85
|
+
}, isAndroid ? 200 : 0);
|
|
75
86
|
activeIndex.current = initialIndex;
|
|
76
87
|
}, [itemRawH, initialIndex]);
|
|
77
88
|
const onContentSizeChange = (_w, h) => {
|
|
78
|
-
const y =
|
|
79
|
-
// console.log('[mpx-picker-view-column], onContentSizeChange --->', 'columnIndex=', columnIndex, '_w=', _w, 'h=', h, 'y=', y)
|
|
89
|
+
const y = getYofIndex(initialIndex);
|
|
80
90
|
if (y <= h) {
|
|
81
91
|
setTimeout(() => {
|
|
82
92
|
scrollViewRef.current?.scrollTo({ x: 0, y, animated: false });
|
|
83
93
|
}, 0);
|
|
84
94
|
}
|
|
85
95
|
};
|
|
86
|
-
const onScrollViewLayout = (e) => {
|
|
87
|
-
const { width } = e.nativeEvent.layout;
|
|
88
|
-
const widthInt = Math.ceil(width);
|
|
89
|
-
// console.log('[mpx-picker-view-column], onScrollViewLayout --->', 'columnIndex=', columnIndex, 'widthInt=', widthInt, 'scrollViewWidth=', scrollViewWidth)
|
|
90
|
-
if (widthInt !== scrollViewWidth) {
|
|
91
|
-
const maxW = maxScrollViewWidth.current;
|
|
92
|
-
if (maxW !== -1 && widthInt > maxW) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
if (maxW === -1) {
|
|
96
|
-
maxScrollViewWidth.current = Math.ceil(widthInt * 1.5);
|
|
97
|
-
}
|
|
98
|
-
setScrollViewWidth(widthInt);
|
|
99
|
-
}
|
|
100
|
-
if (itemRawW === '100%') {
|
|
101
|
-
setItemRawW(widthInt);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
96
|
const onItemLayout = (e) => {
|
|
105
97
|
const { height: rawH } = e.nativeEvent.layout;
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
const roundedH = Math.round(rawH);
|
|
99
|
+
if (roundedH && roundedH !== itemRawH) {
|
|
100
|
+
setItemRawH(roundedH);
|
|
108
101
|
}
|
|
109
102
|
};
|
|
110
|
-
const
|
|
103
|
+
const onScrollBeginDrag = () => {
|
|
104
|
+
isIOS && debounceResetScrollPosition.clear();
|
|
111
105
|
touching.current = true;
|
|
112
106
|
prevScrollingInfo.current = {
|
|
113
107
|
index: activeIndex.current,
|
|
114
|
-
y: activeIndex.current
|
|
108
|
+
y: getYofIndex(activeIndex.current)
|
|
115
109
|
};
|
|
116
110
|
};
|
|
117
|
-
const
|
|
118
|
-
touching.current = false;
|
|
119
|
-
};
|
|
120
|
-
const onTouchCancel = () => {
|
|
111
|
+
const onScrollEndDrag = (e) => {
|
|
121
112
|
touching.current = false;
|
|
113
|
+
const { y } = e.nativeEvent.contentOffset;
|
|
114
|
+
if (isIOS) {
|
|
115
|
+
if (y >= 0 && y <= snapToOffsets[maxIndex]) {
|
|
116
|
+
debounceResetScrollPosition(y);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
122
119
|
};
|
|
123
120
|
const onMomentumScrollBegin = () => {
|
|
121
|
+
isIOS && debounceResetScrollPosition.clear();
|
|
124
122
|
scrolling.current = true;
|
|
125
123
|
};
|
|
126
124
|
const onMomentumScrollEnd = (e) => {
|
|
127
125
|
scrolling.current = false;
|
|
128
|
-
if (!itemRawH) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
126
|
const { y: scrollY } = e.nativeEvent.contentOffset;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
127
|
+
// console.log('[mpx-picker-view-column], onMomentumScrollEnd --->', 'columnIndex=', columnIndex, scrollY, itemRawH)
|
|
128
|
+
if (isIOS && scrollY % itemRawH !== 0) {
|
|
129
|
+
return debounceResetScrollPosition(scrollY);
|
|
130
|
+
}
|
|
131
|
+
const calcIndex = getIndex(scrollY);
|
|
132
|
+
if (calcIndex !== activeIndex.current) {
|
|
133
|
+
activeIndex.current = calcIndex;
|
|
136
134
|
onSelectChange(calcIndex);
|
|
137
135
|
}
|
|
138
136
|
};
|
|
139
137
|
const onScroll = (e) => {
|
|
140
|
-
|
|
138
|
+
// 全局注册的振动触感 hook
|
|
139
|
+
const pickerVibrate = global.__mpx?.config?.rnConfig?.pickerVibrate;
|
|
140
|
+
if (typeof pickerVibrate !== 'function') {
|
|
141
141
|
return;
|
|
142
142
|
}
|
|
143
143
|
const { y } = e.nativeEvent.contentOffset;
|
|
@@ -148,19 +148,20 @@ const _PickerViewColumn = forwardRef((props, ref) => {
|
|
|
148
148
|
if (currentId !== prevIndex) {
|
|
149
149
|
prevScrollingInfo.current = {
|
|
150
150
|
index: currentId,
|
|
151
|
-
y: currentId
|
|
151
|
+
y: getYofIndex(currentId)
|
|
152
152
|
};
|
|
153
|
-
vibrateShort({ type: 'selection' })
|
|
153
|
+
// vibrateShort({ type: 'selection' })
|
|
154
|
+
pickerVibrate();
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
158
|
};
|
|
158
159
|
const renderInnerchild = () => columnData.map((item, index) => {
|
|
159
|
-
return (<MpxPickerVIewColumnItem key={index} item={item} index={index} itemHeight={itemHeight}
|
|
160
|
+
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}/>);
|
|
160
161
|
});
|
|
161
162
|
const renderScollView = () => {
|
|
162
163
|
return (<PickerViewColumnAnimationContext.Provider value={offsetYShared}>
|
|
163
|
-
<Reanimated.ScrollView ref={scrollViewRef} bounces={true} horizontal={false} nestedScrollEnabled={true} removeClippedSubviews={false} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} scrollEventThrottle={16} {...layoutProps} style={[{ width:
|
|
164
|
+
<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}>
|
|
164
165
|
{renderInnerchild()}
|
|
165
166
|
</Reanimated.ScrollView>
|
|
166
167
|
</PickerViewColumnAnimationContext.Provider>);
|
|
@@ -45,7 +45,7 @@ const _PickerView = forwardRef((props, ref) => {
|
|
|
45
45
|
};
|
|
46
46
|
const onInitialChange = (isInvalid, value) => {
|
|
47
47
|
if (isInvalid || !snapActiveValueRef.current || hasDiff(snapActiveValueRef.current, value)) {
|
|
48
|
-
console.log('[mpx-picker-view], onInitialChange
|
|
48
|
+
console.log('[mpx-picker-view], onInitialChange ===> value=', value);
|
|
49
49
|
const eventData = getCustomEvent('change', {}, { detail: { value, source: 'change' }, layoutRef });
|
|
50
50
|
bindchange?.(eventData);
|
|
51
51
|
snapActiveValueRef.current = value.slice();
|
|
@@ -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, curPageId);
|
|
19
|
+
};
|
|
20
|
+
}, []);
|
|
21
|
+
return null;
|
|
22
|
+
};
|
|
23
|
+
export default PortalConsumer;
|
|
@@ -0,0 +1,124 @@
|
|
|
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
|
+
// fix react native web does not support DeviceEventEmitter
|
|
10
|
+
const TopViewEventEmitter = DeviceEventEmitter || new NativeEventEmitter();
|
|
11
|
+
const styles = StyleSheet.create({
|
|
12
|
+
container: {
|
|
13
|
+
flex: 1
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
class PortalGuard {
|
|
17
|
+
nextKey = 10000;
|
|
18
|
+
add = (e) => {
|
|
19
|
+
const key = this.nextKey++;
|
|
20
|
+
TopViewEventEmitter.emit(addType, e, key);
|
|
21
|
+
return key;
|
|
22
|
+
};
|
|
23
|
+
remove = (key) => {
|
|
24
|
+
TopViewEventEmitter.emit(removeType, key);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* portal
|
|
29
|
+
*/
|
|
30
|
+
export const portal = new PortalGuard();
|
|
31
|
+
const PortalHost = ({ children }) => {
|
|
32
|
+
const _nextKey = useRef(0);
|
|
33
|
+
const _queue = useRef([]);
|
|
34
|
+
const _addType = useRef(null);
|
|
35
|
+
const _removeType = useRef(null);
|
|
36
|
+
const manager = useRef(null);
|
|
37
|
+
let currentPageId;
|
|
38
|
+
const _mount = (children, _key, curPageId) => {
|
|
39
|
+
const navigation = getFocusedNavigation();
|
|
40
|
+
const pageId = navigation?.pageId;
|
|
41
|
+
if (pageId !== (curPageId ?? currentPageId)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const key = _key || _nextKey.current++;
|
|
45
|
+
if (manager.current) {
|
|
46
|
+
manager.current.mount(key, children);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
_queue.current.push({ type: 'mount', key, children });
|
|
50
|
+
}
|
|
51
|
+
return key;
|
|
52
|
+
};
|
|
53
|
+
const _unmount = (key, curPageId) => {
|
|
54
|
+
const navigation = getFocusedNavigation();
|
|
55
|
+
const pageId = navigation?.pageId;
|
|
56
|
+
if (pageId !== (curPageId ?? currentPageId)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (manager.current) {
|
|
60
|
+
manager.current.unmount(key);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
_queue.current.push({ type: 'unmount', key });
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const _update = (key, children, curPageId) => {
|
|
67
|
+
const navigation = getFocusedNavigation();
|
|
68
|
+
const pageId = navigation?.pageId;
|
|
69
|
+
if (pageId !== (curPageId ?? currentPageId)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (manager.current) {
|
|
73
|
+
manager.current.update(key, children);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const op = { type: 'mount', key, children };
|
|
77
|
+
const index = _queue.current.findIndex((o) => o.type === 'mount' || (o.type === 'update' && o.key === key));
|
|
78
|
+
if (index > -1) {
|
|
79
|
+
_queue.current[index] = op;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
_queue.current.push(op);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
const navigation = getFocusedNavigation();
|
|
88
|
+
currentPageId = navigation?.pageId;
|
|
89
|
+
_addType.current = TopViewEventEmitter.addListener(addType, _mount);
|
|
90
|
+
_removeType.current = TopViewEventEmitter.addListener(removeType, _unmount);
|
|
91
|
+
return () => {
|
|
92
|
+
while (_queue.current.length && manager.current) {
|
|
93
|
+
const action = _queue.current.pop();
|
|
94
|
+
if (!action) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
// tslint:disable-next-line:switch-default
|
|
98
|
+
switch (action.type) {
|
|
99
|
+
case 'mount':
|
|
100
|
+
manager.current?.mount(action.key, action.children);
|
|
101
|
+
break;
|
|
102
|
+
case 'update':
|
|
103
|
+
manager.current?.update(action.key, action.children);
|
|
104
|
+
break;
|
|
105
|
+
case 'unmount':
|
|
106
|
+
manager.current?.unmount(action.key);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}, []);
|
|
112
|
+
return (<PortalContext.Provider value={{
|
|
113
|
+
mount: _mount,
|
|
114
|
+
update: _update,
|
|
115
|
+
unmount: _unmount
|
|
116
|
+
}}>
|
|
117
|
+
{/* Need collapsable=false here to clip the elevations, otherwise they appear above Portal components */}
|
|
118
|
+
<View style={styles.container} collapsable={false}>
|
|
119
|
+
{children}
|
|
120
|
+
</View>
|
|
121
|
+
<PortalManager ref={manager}/>
|
|
122
|
+
</PortalContext.Provider>);
|
|
123
|
+
};
|
|
124
|
+
export default PortalHost;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useState, useCallback, forwardRef, useImperativeHandle } from 'react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
const _PortalManager = forwardRef((props, ref) => {
|
|
4
|
+
const [state, setState] = useState({
|
|
5
|
+
portals: []
|
|
6
|
+
});
|
|
7
|
+
const mount = useCallback((key, children) => {
|
|
8
|
+
setState((prevState) => ({
|
|
9
|
+
portals: [...prevState.portals, { key, children }]
|
|
10
|
+
}));
|
|
11
|
+
}, [state]);
|
|
12
|
+
const update = useCallback((key, children) => {
|
|
13
|
+
setState((prevState) => ({
|
|
14
|
+
portals: prevState.portals.map((item) => {
|
|
15
|
+
if (item.key === key) {
|
|
16
|
+
return { ...item, children };
|
|
17
|
+
}
|
|
18
|
+
return item;
|
|
19
|
+
})
|
|
20
|
+
}));
|
|
21
|
+
}, [state]);
|
|
22
|
+
const unmount = useCallback((key) => {
|
|
23
|
+
setState((prevState) => ({
|
|
24
|
+
portals: prevState.portals.filter((item) => item.key !== key)
|
|
25
|
+
}));
|
|
26
|
+
}, []);
|
|
27
|
+
useImperativeHandle(ref, () => ({
|
|
28
|
+
mount,
|
|
29
|
+
update,
|
|
30
|
+
unmount,
|
|
31
|
+
portals: state.portals
|
|
32
|
+
}));
|
|
33
|
+
return (<>
|
|
34
|
+
{state.portals.map(({ key, children }, i) => (<View key={key} collapsable={false} // Need collapsable=false here to clip the elevations
|
|
35
|
+
pointerEvents="box-none" style={[StyleSheet.absoluteFill, { zIndex: 1000 + i }]}>
|
|
36
|
+
{children}
|
|
37
|
+
</View>))}
|
|
38
|
+
</>);
|
|
39
|
+
});
|
|
40
|
+
export default _PortalManager;
|