@mpxjs/webpack-plugin 2.9.69-beta.2 → 2.9.69-beta.4

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 (41) hide show
  1. package/lib/react/processScript.js +1 -1
  2. package/lib/runtime/components/react/context.ts +17 -0
  3. package/lib/runtime/components/react/dist/context.js +2 -0
  4. package/lib/runtime/components/react/dist/getInnerListeners.js +2 -2
  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-picker/time.jsx +2 -1
  9. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +12 -13
  10. package/lib/runtime/components/react/dist/mpx-portal/portal-consumer.jsx +23 -0
  11. package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +124 -0
  12. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +40 -0
  13. package/lib/runtime/components/react/dist/mpx-portal.jsx +12 -0
  14. package/lib/runtime/components/react/dist/mpx-provider.jsx +31 -0
  15. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +1 -1
  16. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +10 -16
  17. package/lib/runtime/components/react/dist/mpx-swiper.jsx +10 -5
  18. package/lib/runtime/components/react/dist/mpx-view.jsx +15 -21
  19. package/lib/runtime/components/react/dist/mpx-web-view.jsx +78 -20
  20. package/lib/runtime/components/react/dist/pickerFaces.js +1 -1
  21. package/lib/runtime/components/react/dist/useAnimationHooks.js +14 -2
  22. package/lib/runtime/components/react/getInnerListeners.ts +2 -2
  23. package/lib/runtime/components/react/locale-provider.tsx +83 -0
  24. package/lib/runtime/components/react/mpx-button.tsx +13 -49
  25. package/lib/runtime/components/react/mpx-image.tsx +41 -25
  26. package/lib/runtime/components/react/mpx-picker/time.tsx +2 -1
  27. package/lib/runtime/components/react/mpx-picker-view-column.tsx +12 -13
  28. package/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +32 -0
  29. package/lib/runtime/components/react/mpx-portal/portal-host.tsx +158 -0
  30. package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +64 -0
  31. package/lib/runtime/components/react/mpx-portal.tsx +29 -0
  32. package/lib/runtime/components/react/mpx-provider.tsx +51 -0
  33. package/lib/runtime/components/react/mpx-root-portal.tsx +1 -1
  34. package/lib/runtime/components/react/mpx-scroll-view.tsx +9 -15
  35. package/lib/runtime/components/react/mpx-swiper.tsx +11 -5
  36. package/lib/runtime/components/react/mpx-view.tsx +17 -23
  37. package/lib/runtime/components/react/mpx-web-view.tsx +110 -21
  38. package/lib/runtime/components/react/pickerFaces.ts +1 -1
  39. package/lib/runtime/components/react/types/global.d.ts +3 -0
  40. package/lib/runtime/components/react/useAnimationHooks.ts +19 -3
  41. package/package.json +1 -1
@@ -26,7 +26,7 @@ module.exports = function (script, {
26
26
  import { getComponent } from ${stringifyRequest(loaderContext, optionProcessorPath)}
27
27
  import { NavigationContainer, StackActions } from '@react-navigation/native'
28
28
  import { createStackNavigator } from '@react-navigation/stack'
29
- import { Provider } from '@ant-design/react-native'
29
+ import Provider from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-provider'
30
30
  import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
31
31
  import { GestureHandlerRootView } from 'react-native-gesture-handler'
32
32
 
@@ -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, pageId?: number|null) => 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) > 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]);
@@ -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;
@@ -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
  // 可见应用窗口的大小。
@@ -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, useDebounceCallback, useStableCallback, isIOS } 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';
@@ -49,7 +49,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
49
49
  return index * itemRawH;
50
50
  }, [itemRawH]);
51
51
  const stableResetScrollPosition = useStableCallback((y) => {
52
- console.log('[mpx-picker-view-column], reset --->', 'columnIndex=', columnIndex, 'y=', y, touching.current, scrolling.current);
52
+ // console.log('[mpx-picker-view-column], reset --->', 'columnIndex=', columnIndex, 'y=', y, touching.current, scrolling.current, itemRawH, 'snapToOffsets=', snapToOffsets)
53
53
  if (touching.current || scrolling.current) {
54
54
  return;
55
55
  }
@@ -95,8 +95,9 @@ const _PickerViewColumn = forwardRef((props, ref) => {
95
95
  };
96
96
  const onItemLayout = (e) => {
97
97
  const { height: rawH } = e.nativeEvent.layout;
98
- if (rawH && itemRawH !== rawH) {
99
- setItemRawH(rawH);
98
+ const roundedH = Math.round(rawH);
99
+ if (roundedH && roundedH !== itemRawH) {
100
+ setItemRawH(roundedH);
100
101
  }
101
102
  };
102
103
  const onScrollBeginDrag = () => {
@@ -111,7 +112,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
111
112
  touching.current = false;
112
113
  const { y } = e.nativeEvent.contentOffset;
113
114
  if (isIOS) {
114
- if (y > 0 && y < snapToOffsets[maxIndex]) {
115
+ if (y >= 0 && y <= snapToOffsets[maxIndex]) {
115
116
  debounceResetScrollPosition(y);
116
117
  }
117
118
  }
@@ -123,25 +124,23 @@ const _PickerViewColumn = forwardRef((props, ref) => {
123
124
  const onMomentumScrollEnd = (e) => {
124
125
  scrolling.current = false;
125
126
  const { y: scrollY } = e.nativeEvent.contentOffset;
127
+ // console.log('[mpx-picker-view-column], onMomentumScrollEnd --->', 'columnIndex=', columnIndex, scrollY, itemRawH)
126
128
  if (isIOS && scrollY % itemRawH !== 0) {
127
129
  return debounceResetScrollPosition(scrollY);
128
130
  }
129
131
  const calcIndex = getIndex(scrollY);
130
- activeIndex.current = calcIndex;
131
- if (calcIndex !== initialIndex) {
132
+ if (calcIndex !== activeIndex.current) {
133
+ activeIndex.current = calcIndex;
132
134
  onSelectChange(calcIndex);
133
135
  }
134
136
  };
135
137
  const onScroll = (e) => {
136
- const { y } = e.nativeEvent.contentOffset;
137
- if (isAndroid) {
138
- return;
139
- }
140
- // 全局注册的震动触感 hook
141
- const pickerVibrate = global.__mpx.config.rnConfig.pickerVibrate;
138
+ // 全局注册的振动触感 hook
139
+ const pickerVibrate = global.__mpx?.config?.rnConfig?.pickerVibrate;
142
140
  if (typeof pickerVibrate !== 'function') {
143
141
  return;
144
142
  }
143
+ const { y } = e.nativeEvent.contentOffset;
145
144
  const { index: prevIndex, y: _y } = prevScrollingInfo.current;
146
145
  if (touching.current || scrolling.current) {
147
146
  if (Math.abs(y - _y) >= itemRawH) {
@@ -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;
@@ -0,0 +1,12 @@
1
+ import { PortalContext } from './context';
2
+ import PortalConsumer from './mpx-portal/portal-consumer';
3
+ import PortalHost, { portal } from './mpx-portal/portal-host';
4
+ const Portal = ({ children }) => {
5
+ return (<PortalContext.Consumer>
6
+ {(manager) => (<PortalConsumer manager={manager}>{children}</PortalConsumer>)}
7
+ </PortalContext.Consumer>);
8
+ };
9
+ Portal.Host = PortalHost;
10
+ Portal.add = portal.add;
11
+ Portal.remove = portal.remove;
12
+ export default Portal;
@@ -0,0 +1,31 @@
1
+ import { createContext, useMemo } from 'react';
2
+ import LocaleProvider from './locale-provider';
3
+ import Portal from './mpx-portal';
4
+ import { extendObject } from './utils';
5
+ const defaultTheme = {
6
+ color_text_base: '#000000',
7
+ color_text_base_inverse: '#ffffff',
8
+ color_text_secondary: '#a4a9b0',
9
+ color_text_placeholder: '#bbbbbb',
10
+ color_text_disabled: '#bbbbbb',
11
+ color_text_caption: '#888888',
12
+ color_text_paragraph: '#333333',
13
+ color_error: '#ff4d4f',
14
+ color_warning: '#faad14',
15
+ color_success: '#52c41a',
16
+ color_primary: '#1677ff'
17
+ };
18
+ export const ThemeContext = createContext(defaultTheme);
19
+ const ThemeProvider = (props) => {
20
+ const { value, children } = props;
21
+ const theme = useMemo(() => (extendObject({}, defaultTheme, value)), [value]);
22
+ return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>;
23
+ };
24
+ const Provider = ({ locale, theme, children }) => {
25
+ return (<LocaleProvider locale={locale}>
26
+ <ThemeProvider value={theme}>
27
+ <Portal.Host>{children}</Portal.Host>
28
+ </ThemeProvider>
29
+ </LocaleProvider>);
30
+ };
31
+ export default Provider;
@@ -2,7 +2,7 @@
2
2
  * ✔ enable
3
3
  */
4
4
  import { createElement, Fragment } from 'react';
5
- import { Portal } from '@ant-design/react-native';
5
+ import Portal from './mpx-portal';
6
6
  import { warn } from '@mpxjs/utils';
7
7
  const _RootPortal = (props) => {
8
8
  const { children, enable = true } = props;
@@ -32,7 +32,7 @@
32
32
  * ✔ bindscroll
33
33
  */
34
34
  import { ScrollView } from 'react-native-gesture-handler';
35
- import { RefreshControl, Platform } from 'react-native';
35
+ import { RefreshControl } from 'react-native';
36
36
  import { useRef, useState, useEffect, forwardRef, useContext, createElement, useMemo } from 'react';
37
37
  import { useAnimatedRef } from 'react-native-reanimated';
38
38
  import { warn } from '@mpxjs/utils';
@@ -184,7 +184,6 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
184
184
  visibleLength
185
185
  });
186
186
  }
187
- const observerTimers = {};
188
187
  function onScroll(e) {
189
188
  const { bindscroll } = props;
190
189
  const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset;
@@ -202,20 +201,6 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
202
201
  layoutRef
203
202
  }, props));
204
203
  updateScrollOptions(e, { scrollLeft, scrollTop });
205
- if (enableTriggerIntersectionObserver && intersectionObservers) {
206
- for (const key in intersectionObservers) {
207
- if (Platform.OS === 'android') {
208
- intersectionObservers[key].throttleMeasure();
209
- }
210
- else {
211
- // TODO: 由于iOS在onScroll滚动的过程中view的计算measureInWindow计算的值不发生变化,所以暂时在ios上加一个延时计算
212
- observerTimers[key] && clearTimeout(observerTimers[key]);
213
- observerTimers[key] = setTimeout(() => {
214
- intersectionObservers[key].throttleMeasure();
215
- }, 300);
216
- }
217
- }
218
- }
219
204
  }
220
205
  function onScrollEnd(e) {
221
206
  const { bindscrollend } = props;
@@ -234,6 +219,14 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
234
219
  updateScrollOptions(e, { scrollLeft, scrollTop });
235
220
  onStartReached(e);
236
221
  onEndReached(e);
222
+ updateIntersection();
223
+ }
224
+ function updateIntersection() {
225
+ if (enableTriggerIntersectionObserver && intersectionObservers) {
226
+ for (const key in intersectionObservers) {
227
+ intersectionObservers[key].throttleMeasure();
228
+ }
229
+ }
237
230
  }
238
231
  function scrollToOffset(x = 0, y = 0) {
239
232
  if (scrollViewRef.current) {
@@ -292,6 +285,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
292
285
  function onScrollDrag(e) {
293
286
  const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset;
294
287
  updateScrollOptions(e, { scrollLeft, scrollTop });
288
+ updateIntersection();
295
289
  }
296
290
  const scrollAdditionalProps = extendObject({
297
291
  style: extendObject({}, innerStyle, layoutStyle),
@@ -361,8 +361,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
361
361
  let isCriticalItem = false;
362
362
  // 真实滚动到的偏移量坐标
363
363
  let moveToTargetPos = 0;
364
- // 当前的位置
365
- const currentOffset = offset.value - previousMargin;
364
+ const currentOffset = translation < 0 ? offset.value - previousMargin : offset.value + previousMargin;
366
365
  const computedIndex = Math.abs(currentOffset) / step.value;
367
366
  const moveToIndex = translation < 0 ? Math.ceil(computedIndex) : Math.floor(computedIndex);
368
367
  // 实际应该定位的索引值
@@ -442,10 +441,13 @@ const SwiperWrapper = forwardRef((props, ref) => {
442
441
  'worklet';
443
442
  const { translation } = eventData;
444
443
  // 向右滑动的back:trans < 0, 向左滑动的back: trans < 0
445
- const currentOffset = Math.abs(offset.value);
444
+ let currentOffset = Math.abs(offset.value);
445
+ if (props.circular) {
446
+ currentOffset += translation < 0 ? previousMargin : -previousMargin;
447
+ }
446
448
  const curIndex = currentOffset / step.value;
447
449
  const moveToIndex = (translation < 0 ? Math.floor(curIndex) : Math.ceil(curIndex)) - patchElementNum;
448
- const targetOffset = -(moveToIndex + patchElementNum) * step.value + (translation < 0 ? -previousMargin : previousMargin);
450
+ const targetOffset = -(moveToIndex + patchElementNum) * step.value + (props.circular ? -previousMargin : 0);
449
451
  offset.value = withTiming(targetOffset, {
450
452
  duration: easeDuration,
451
453
  easing: easeMap[easeingFunc]
@@ -459,7 +461,10 @@ const SwiperWrapper = forwardRef((props, ref) => {
459
461
  function handleLongPress(eventData) {
460
462
  'worklet';
461
463
  const { translation } = eventData;
462
- const currentOffset = Math.abs(offset.value) + (translation < 0 ? previousMargin : -previousMargin);
464
+ let currentOffset = Math.abs(offset.value);
465
+ if (props.circular) {
466
+ currentOffset += previousMargin;
467
+ }
463
468
  const half = currentOffset % step.value > step.value / 2;
464
469
  // 向右trans < 0, 向左trans > 0
465
470
  const isExceedHalf = translation < 0 ? half : !half;