@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.
Files changed (33) hide show
  1. package/lib/index.js +1 -1
  2. package/lib/loader.js +0 -1
  3. package/lib/runtime/components/react/dist/context.js +4 -0
  4. package/lib/runtime/components/react/dist/getInnerListeners.js +5 -6
  5. package/lib/runtime/components/react/dist/locale-provider.jsx +15 -0
  6. package/lib/runtime/components/react/dist/mpx-button.jsx +9 -37
  7. package/lib/runtime/components/react/dist/mpx-image.jsx +13 -9
  8. package/lib/runtime/components/react/dist/mpx-input.jsx +1 -1
  9. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +1 -1
  10. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +2 -1
  11. package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +1 -1
  12. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +56 -55
  13. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +1 -1
  14. package/lib/runtime/components/react/dist/mpx-portal/portal-consumer.jsx +23 -0
  15. package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +124 -0
  16. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +40 -0
  17. package/lib/runtime/components/react/dist/mpx-portal.jsx +12 -0
  18. package/lib/runtime/components/react/dist/mpx-provider.jsx +31 -0
  19. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +10 -11
  20. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +1 -1
  21. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +18 -9
  22. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +25 -8
  23. package/lib/runtime/components/react/dist/mpx-swiper.jsx +157 -150
  24. package/lib/runtime/components/react/dist/mpx-view.jsx +21 -58
  25. package/lib/runtime/components/react/dist/mpx-web-view.jsx +118 -22
  26. package/lib/runtime/components/react/dist/pickerFaces.js +2 -2
  27. package/lib/runtime/components/react/dist/useAnimationHooks.js +15 -3
  28. package/lib/runtime/components/react/dist/utils.jsx +63 -4
  29. package/lib/runtime/optionProcessor.tenon.js +3 -3
  30. package/lib/tenon/index.js +2 -7
  31. package/lib/tenon/processScript.js +0 -5
  32. package/lib/tenon/processTemplate.js +1 -2
  33. 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
@@ -135,7 +135,6 @@ module.exports = function (content) {
135
135
  const hasComment = templateAttrs && templateAttrs.comments
136
136
  const isNative = false
137
137
 
138
-
139
138
  if (mode === 'tenon') {
140
139
  let output = ''
141
140
  if (ctorType === 'app' && !queryObj.app) {
@@ -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) > 1 ||
86
- Math.abs(currentPageY - tapDetailInfo.y) > 1) {
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, useState, forwardRef, useContext } from 'react';
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 { getCurrentPage, splitProps, splitStyle, useLayout, useTransformStyle, wrapChildren, extendObject } from './utils';
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, bindtouchstart, bindtouchend } = props;
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
- return createElement(View, innerProps, loading && createElement(Loading, { alone: !children }), wrapChildren(props, {
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
- return createElement(View, innerProps, isSvg
272
- ? createElement(SvgCssUri, {
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 = true, bindinput, bindfocus, bindblur, bindconfirm, bindselectionchange,
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);
@@ -11,7 +11,7 @@
11
11
  * ✘ scale-min
12
12
  * ✘ scale-max
13
13
  * ✘ scale-value
14
- * animation
14
+ * animation
15
15
  * ✔ bindchange
16
16
  * ✘ bindscale
17
17
  * ✔ htouchmove
@@ -1,5 +1,6 @@
1
1
  import { View, Text, Modal, TouchableWithoutFeedback } from 'react-native';
2
- import { PickerView, Portal } from '@ant-design/react-native';
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 { Platform, SafeAreaView, StyleSheet } from 'react-native';
2
+ import { SafeAreaView, StyleSheet } from 'react-native';
3
3
  import Reanimated, { useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated';
4
- import { vibrateShort } from '@mpxjs/api-proxy';
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
- // const initialOffset = useMemo(() => ({
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: itemRawH * initialIndex,
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 = itemRawH * initialIndex;
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
- if (rawH && itemRawH !== rawH) {
107
- setItemRawH(rawH);
98
+ const roundedH = Math.round(rawH);
99
+ if (roundedH && roundedH !== itemRawH) {
100
+ setItemRawH(roundedH);
108
101
  }
109
102
  };
110
- const onTouchStart = () => {
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 * itemRawH
108
+ y: getYofIndex(activeIndex.current)
115
109
  };
116
110
  };
117
- const onTouchEnd = () => {
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
- let calcIndex = Math.round(scrollY / itemRawH);
133
- activeIndex.current = calcIndex;
134
- if (calcIndex !== initialIndex) {
135
- calcIndex = Math.max(0, Math.min(calcIndex, maxIndex)) || 0;
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
- if (Platform.OS === 'android') {
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 * itemRawH
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} itemWidth={itemRawW} textStyleFromParent={textStyleFromParent} textStyle={textStyle} hasVarDec={hasVarDec} varContext={varContextRef.current} textProps={textProps} visibleCount={visibleCount} onItemLayout={onItemLayout}/>);
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: scrollViewWidth }]} decelerationRate="fast" snapToOffsets={snapToOffsets} onScroll={onScroll} onLayout={onScrollViewLayout} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd} onTouchCancel={onTouchCancel} onMomentumScrollBegin={onMomentumScrollBegin} onMomentumScrollEnd={onMomentumScrollEnd} onContentSizeChange={onContentSizeChange} contentContainerStyle={contentContainerStyle}>
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-1 ===> value=', value);
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;