@mpxjs/webpack-plugin 2.9.69-beta.3 → 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 (65) hide show
  1. package/lib/index.js +17 -1
  2. package/lib/platform/style/wx/index.js +18 -18
  3. package/lib/platform/template/wx/component-config/movable-view.js +8 -1
  4. package/lib/platform/template/wx/component-config/scroll-view.js +1 -1
  5. package/lib/react/processScript.js +1 -1
  6. package/lib/resolver/AddEnvPlugin.js +1 -0
  7. package/lib/resolver/AddModePlugin.js +1 -0
  8. package/lib/runtime/components/react/context.ts +25 -0
  9. package/lib/runtime/components/react/dist/context.js +2 -0
  10. package/lib/runtime/components/react/dist/getInnerListeners.js +2 -2
  11. package/lib/runtime/components/react/dist/locale-provider.jsx +15 -0
  12. package/lib/runtime/components/react/dist/mpx-button.jsx +9 -37
  13. package/lib/runtime/components/react/dist/mpx-image.jsx +13 -9
  14. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +2 -1
  15. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +12 -13
  16. package/lib/runtime/components/react/dist/mpx-portal/portal-consumer.jsx +23 -0
  17. package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +124 -0
  18. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +40 -0
  19. package/lib/runtime/components/react/dist/mpx-portal.jsx +12 -0
  20. package/lib/runtime/components/react/dist/mpx-provider.jsx +31 -0
  21. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +1 -1
  22. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +10 -16
  23. package/lib/runtime/components/react/dist/mpx-swiper.jsx +10 -5
  24. package/lib/runtime/components/react/dist/mpx-view.jsx +15 -21
  25. package/lib/runtime/components/react/dist/mpx-web-view.jsx +78 -20
  26. package/lib/runtime/components/react/dist/pickerFaces.js +1 -1
  27. package/lib/runtime/components/react/dist/useAnimationHooks.js +14 -2
  28. package/lib/runtime/components/react/getInnerListeners.ts +5 -7
  29. package/lib/runtime/components/react/locale-provider.tsx +83 -0
  30. package/lib/runtime/components/react/mpx-button.tsx +13 -49
  31. package/lib/runtime/components/react/mpx-image.tsx +41 -25
  32. package/lib/runtime/components/react/mpx-input.tsx +1 -1
  33. package/lib/runtime/components/react/mpx-movable-view.tsx +1 -1
  34. package/lib/runtime/components/react/mpx-picker/time.tsx +2 -1
  35. package/lib/runtime/components/react/mpx-picker-view-column-item.tsx +88 -0
  36. package/lib/runtime/components/react/mpx-picker-view-column.tsx +180 -163
  37. package/lib/runtime/components/react/mpx-picker-view.tsx +35 -37
  38. package/lib/runtime/components/react/mpx-portal/portal-consumer.tsx +32 -0
  39. package/lib/runtime/components/react/mpx-portal/portal-host.tsx +158 -0
  40. package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +64 -0
  41. package/lib/runtime/components/react/mpx-portal.tsx +29 -0
  42. package/lib/runtime/components/react/mpx-provider.tsx +51 -0
  43. package/lib/runtime/components/react/mpx-rich-text/index.tsx +12 -18
  44. package/lib/runtime/components/react/mpx-root-portal.tsx +1 -1
  45. package/lib/runtime/components/react/mpx-scroll-view.tsx +29 -18
  46. package/lib/runtime/components/react/mpx-swiper-item.tsx +38 -10
  47. package/lib/runtime/components/react/mpx-swiper.tsx +696 -0
  48. package/lib/runtime/components/react/mpx-view.tsx +25 -71
  49. package/lib/runtime/components/react/mpx-web-view.tsx +156 -23
  50. package/lib/runtime/components/react/pickerFaces.ts +15 -7
  51. package/lib/runtime/components/react/pickerVIewContext.ts +18 -0
  52. package/lib/runtime/components/react/pickerViewMask.tsx +30 -0
  53. package/lib/runtime/components/react/{pickerOverlay.tsx → pickerViewOverlay.tsx} +5 -3
  54. package/lib/runtime/components/react/types/global.d.ts +6 -1
  55. package/lib/runtime/components/react/useAnimationHooks.ts +20 -4
  56. package/lib/runtime/components/react/utils.tsx +75 -5
  57. package/lib/style-compiler/index.js +3 -4
  58. package/lib/style-compiler/strip-conditional-loader.js +118 -0
  59. package/lib/template-compiler/compiler.js +9 -14
  60. package/lib/utils/hump-dash.js +1 -1
  61. package/lib/utils/pre-process-json.js +5 -9
  62. package/package.json +1 -1
  63. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +0 -527
  64. package/lib/runtime/components/react/mpx-swiper/index.tsx +0 -80
  65. package/lib/runtime/components/react/mpx-swiper/type.ts +0 -87
package/lib/index.js CHANGED
@@ -54,11 +54,13 @@ const wxssLoaderPath = normalize.lib('wxss/index')
54
54
  const wxmlLoaderPath = normalize.lib('wxml/loader')
55
55
  const wxsLoaderPath = normalize.lib('wxs/loader')
56
56
  const styleCompilerPath = normalize.lib('style-compiler/index')
57
+ const styleStripConditaionalPath = normalize.lib('style-compiler/strip-conditional-loader')
57
58
  const templateCompilerPath = normalize.lib('template-compiler/index')
58
59
  const jsonCompilerPath = normalize.lib('json-compiler/index')
59
60
  const jsonThemeCompilerPath = normalize.lib('json-compiler/theme')
60
61
  const jsonPluginCompilerPath = normalize.lib('json-compiler/plugin')
61
62
  const extractorPath = normalize.lib('extractor')
63
+ const selectorPath = normalize.lib('selector')
62
64
  const async = require('async')
63
65
  const { parseQuery } = require('loader-utils')
64
66
  const stringifyLoadersAndResource = require('./utils/stringify-loaders-resource')
@@ -1775,7 +1777,7 @@ try {
1775
1777
  })
1776
1778
 
1777
1779
  const typeLoaderProcessInfo = {
1778
- styles: ['node_modules/css-loader', wxssLoaderPath, styleCompilerPath],
1780
+ styles: ['node_modules/css-loader', wxssLoaderPath, styleCompilerPath, styleStripConditaionalPath],
1779
1781
  template: ['node_modules/html-loader', wxmlLoaderPath, templateCompilerPath]
1780
1782
  }
1781
1783
 
@@ -1833,6 +1835,20 @@ try {
1833
1835
  loader: extractorPath
1834
1836
  })
1835
1837
  }
1838
+ if (type === 'styles') {
1839
+ // 判断最后一个loader是否是 selectorPath, 如果是,则在sectorPath之前插入strip-conditional
1840
+ const lastLoader = loaders[loaders.length - 1]
1841
+ if (lastLoader.loader.includes(selectorPath)) {
1842
+ loaders.splice(loaders.length - 1, 0, {
1843
+ loader: styleStripConditaionalPath
1844
+ })
1845
+ } else {
1846
+ // 在最后一个插入strip-conditional
1847
+ loaders.push({
1848
+ loader: styleStripConditaionalPath
1849
+ })
1850
+ }
1851
+ }
1836
1852
  createData.resource = addQuery(createData.resource, { mpx: MPX_PROCESSED_FLAG }, true)
1837
1853
  }
1838
1854
  // mpxStyleOptions 为 mpx style 文件的标识,避免 Vue 文件插入 styleCompiler 后导致 vue scoped 样式隔离失效
@@ -374,11 +374,11 @@ module.exports = function getSpec ({ warn, error }) {
374
374
  // transform 转换
375
375
  const formatTransform = ({ prop, value, selector }, { mode }) => {
376
376
  // css var & 数组直接返回
377
- if (Array.isArray(value) || calcExp.test(value)) return { prop, value }
377
+ if (Array.isArray(value) || cssVariableExp.test(value)) return { prop, value }
378
378
  const values = parseValues(value)
379
379
  const transform = []
380
380
  values.forEach(item => {
381
- const match = item.match(/([/\w]+)\(([^)]+)\)/)
381
+ const match = item.match(/([/\w]+)\((.+)\)/)
382
382
  if (match && match.length >= 3) {
383
383
  let key = match[1]
384
384
  const val = match[2]
@@ -407,23 +407,23 @@ module.exports = function getSpec ({ warn, error }) {
407
407
  case 'rotate3d': // x y z angle
408
408
  case 'translate3d': // x y 支持 z不支持
409
409
  case 'scale3d': // x y 支持 z不支持
410
- {
411
- // 2 个以上的值处理
412
- key = key.replace('3d', '')
413
- const vals = parseValues(val, ',').splice(0, key === 'rotate' ? 4 : 3)
414
- // scale(.5) === scaleX(.5) scaleY(.5)
415
- if (vals.length === 1 && key === 'scale') {
416
- vals.push(vals[0])
417
- }
418
- const xyz = ['X', 'Y', 'Z']
419
- transform.push(...vals.map((v, index) => {
420
- if (key !== 'rotate' && index > 1) {
421
- unsupportedPropError({ prop: `${key}Z`, value, selector }, { mode })
422
- }
423
- return { [`${key}${xyz[index] || ''}`]: v.trim() }
424
- }))
425
- break
410
+ {
411
+ // 2 个以上的值处理
412
+ key = key.replace('3d', '')
413
+ const vals = parseValues(val, ',').splice(0, key === 'rotate' ? 4 : 3)
414
+ // scale(.5) === scaleX(.5) scaleY(.5)
415
+ if (vals.length === 1 && key === 'scale') {
416
+ vals.push(vals[0])
426
417
  }
418
+ const xyz = ['X', 'Y', 'Z']
419
+ transform.push(...vals.map((v, index) => {
420
+ if (key !== 'rotate' && index > 1) {
421
+ unsupportedPropError({ prop: `${key}Z`, value, selector }, { mode })
422
+ }
423
+ return { [`${key}${xyz[index] || ''}`]: v.trim() }
424
+ }))
425
+ break
426
+ }
427
427
  case 'translateZ':
428
428
  case 'scaleZ':
429
429
  default:
@@ -2,6 +2,8 @@ const TAG_NAME = 'movable-view'
2
2
 
3
3
  module.exports = function ({ print }) {
4
4
  const aliEventLog = print({ platform: 'ali', tag: TAG_NAME, isError: false, type: 'event' })
5
+ const androidEventLog = print({ platform: 'android', tag: TAG_NAME, isError: false, type: 'event' })
6
+ const iosEventLog = print({ platform: 'ios', tag: TAG_NAME, isError: false, type: 'event' })
5
7
  const qaPropLog = print({ platform: 'qa', tag: TAG_NAME, isError: false })
6
8
  const androidPropLog = print({ platform: 'android', tag: TAG_NAME, isError: false })
7
9
  const iosPropLog = print({ platform: 'ios', tag: TAG_NAME, isError: false })
@@ -27,7 +29,7 @@ module.exports = function ({ print }) {
27
29
  android: androidPropLog
28
30
  },
29
31
  {
30
- test: /^(inertia|damping|animation)$/,
32
+ test: /^(damping|friction|scale|scale-min|scale-max|scale-value)$/,
31
33
  ios: iosPropLog,
32
34
  android: androidPropLog
33
35
  }
@@ -36,6 +38,11 @@ module.exports = function ({ print }) {
36
38
  {
37
39
  test: /^(htouchmove|vtouchmove)$/,
38
40
  ali: aliEventLog
41
+ },
42
+ {
43
+ test: /^(bindscale)$/,
44
+ ios: iosEventLog,
45
+ android: androidEventLog
39
46
  }
40
47
  ]
41
48
  }
@@ -53,7 +53,7 @@ module.exports = function ({ print }) {
53
53
  qa: qaPropLog
54
54
  },
55
55
  {
56
- test: /^(scroll-into-view|refresher-threshold|enable-passive|scroll-anchoring|using-sticky|fast-deceleration|enable-flex)$/,
56
+ test: /^(refresher-threshold|enable-passive|scroll-anchoring|using-sticky|fast-deceleration|enable-flex)$/,
57
57
  android: androidPropLog,
58
58
  ios: iosPropLog
59
59
  },
@@ -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
 
@@ -34,6 +34,7 @@ module.exports = class AddEnvPlugin {
34
34
  if (!extname || !matchCondition(resourcePath, this.fileConditionRules)) return callback()
35
35
  const queryObj = parseQuery(request.query || '?')
36
36
  queryObj.infix = `${queryObj.infix || ''}.${env}`
37
+ // css | stylus | less | sass 中 import file 过滤query,避免在对应的 loader 中无法读取到文件
37
38
  obj.query = stringifyQuery(queryObj)
38
39
  obj.path = addInfix(resourcePath, env, extname)
39
40
  obj.relativePath = request.relativePath && addInfix(request.relativePath, env, extname)
@@ -43,6 +43,7 @@ module.exports = class AddModePlugin {
43
43
  resolver.doResolve(target, Object.assign({}, request, obj), 'add mode: ' + mode, resolveContext, (err, result) => {
44
44
  if (defaultMode && !result) {
45
45
  queryObj.infix = `${queryInfix || ''}.${defaultMode}`
46
+ // css | stylus | less | sass 中 import file 过滤query,避免在对应的 loader 中无法读取到文件
46
47
  obj.query = stringifyQuery(queryObj)
47
48
  obj.path = addInfix(resourcePath, defaultMode, extname)
48
49
  resolver.doResolve(target, Object.assign({}, request, obj), 'add mode: ' + defaultMode, resolveContext, (err, result) => {
@@ -32,6 +32,23 @@ 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
+ }
48
+
49
+ export interface ScrollViewContextValue {
50
+ gestureRef: React.RefObject<any> | null
51
+ }
35
52
 
36
53
  export const MovableAreaContext = createContext({ width: 0, height: 0 })
37
54
 
@@ -51,4 +68,12 @@ export const IntersectionObserverContext = createContext<IntersectionObserver |
51
68
 
52
69
  export const RouteContext = createContext<number | null>(null)
53
70
 
71
+ export const SwiperContext = createContext({})
72
+
54
73
  export const KeyboardAvoidContext = createContext<KeyboardAvoidContextValue | null>(null)
74
+
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;