@mpxjs/webpack-plugin 2.9.72 → 2.10.0

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 (80) hide show
  1. package/lib/file-loader.js +5 -0
  2. package/lib/index.js +21 -3
  3. package/lib/platform/template/wx/component-config/input.js +1 -1
  4. package/lib/platform/template/wx/component-config/textarea.js +1 -1
  5. package/lib/platform/template/wx/component-config/unsupported.js +1 -1
  6. package/lib/platform/template/wx/component-config/video.js +28 -1
  7. package/lib/platform/template/wx/index.js +2 -2
  8. package/lib/react/processScript.js +6 -4
  9. package/lib/react/processTemplate.js +5 -3
  10. package/lib/runtime/components/react/KeyboardAvoidingView.tsx +108 -0
  11. package/lib/runtime/components/react/context.ts +18 -2
  12. package/lib/runtime/components/react/dist/KeyboardAvoidingView.jsx +89 -0
  13. package/lib/runtime/components/react/dist/context.js +1 -0
  14. package/lib/runtime/components/react/dist/getInnerListeners.js +1 -2
  15. package/lib/runtime/components/react/dist/mpx-button.jsx +1 -1
  16. package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
  17. package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
  18. package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
  19. package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
  20. package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
  21. package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
  22. package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
  23. package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
  24. package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
  25. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +50 -0
  26. package/lib/runtime/components/react/dist/mpx-image.jsx +19 -18
  27. package/lib/runtime/components/react/dist/mpx-input.jsx +48 -19
  28. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +16 -14
  29. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +2 -1
  30. package/lib/runtime/components/react/dist/mpx-portal/index.jsx +30 -0
  31. package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +112 -0
  32. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +41 -0
  33. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +1 -1
  34. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +19 -7
  35. package/lib/runtime/components/react/dist/mpx-swiper.jsx +3 -2
  36. package/lib/runtime/components/react/dist/mpx-textarea.jsx +11 -3
  37. package/lib/runtime/components/react/dist/mpx-video.jsx +248 -0
  38. package/lib/runtime/components/react/dist/mpx-web-view.jsx +149 -50
  39. package/lib/runtime/components/react/dist/useAnimationHooks.js +4 -4
  40. package/lib/runtime/components/react/dist/utils.jsx +8 -3
  41. package/lib/runtime/components/react/getInnerListeners.ts +1 -2
  42. package/lib/runtime/components/react/mpx-button.tsx +1 -1
  43. package/lib/runtime/components/react/mpx-icon/icons/cancel.png +0 -0
  44. package/lib/runtime/components/react/mpx-icon/icons/clear.png +0 -0
  45. package/lib/runtime/components/react/mpx-icon/icons/download.png +0 -0
  46. package/lib/runtime/components/react/mpx-icon/icons/info.png +0 -0
  47. package/lib/runtime/components/react/mpx-icon/icons/search.png +0 -0
  48. package/lib/runtime/components/react/mpx-icon/icons/success.png +0 -0
  49. package/lib/runtime/components/react/mpx-icon/icons/success_no_circle.png +0 -0
  50. package/lib/runtime/components/react/mpx-icon/icons/waiting.png +0 -0
  51. package/lib/runtime/components/react/mpx-icon/icons/warn.png +0 -0
  52. package/lib/runtime/components/react/mpx-icon/index.tsx +111 -0
  53. package/lib/runtime/components/react/mpx-image.tsx +26 -14
  54. package/lib/runtime/components/react/mpx-input.tsx +52 -20
  55. package/lib/runtime/components/react/mpx-movable-view.tsx +19 -16
  56. package/lib/runtime/components/react/mpx-picker/time.tsx +2 -1
  57. package/lib/runtime/components/react/mpx-portal/index.tsx +39 -0
  58. package/lib/runtime/components/react/mpx-portal/portal-host.tsx +141 -0
  59. package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +64 -0
  60. package/lib/runtime/components/react/mpx-root-portal.tsx +1 -1
  61. package/lib/runtime/components/react/mpx-scroll-view.tsx +20 -8
  62. package/lib/runtime/components/react/mpx-swiper.tsx +3 -2
  63. package/lib/runtime/components/react/mpx-textarea.tsx +13 -3
  64. package/lib/runtime/components/react/mpx-video.tsx +388 -0
  65. package/lib/runtime/components/react/mpx-web-view.tsx +180 -49
  66. package/lib/runtime/components/react/types/getInnerListeners.d.ts +1 -1
  67. package/lib/runtime/components/react/types/global.d.ts +8 -0
  68. package/lib/runtime/components/react/useAnimationHooks.ts +4 -4
  69. package/lib/runtime/components/react/utils.tsx +12 -6
  70. package/lib/script-setup-compiler/index.js +6 -2
  71. package/lib/style-compiler/index.js +3 -4
  72. package/lib/style-compiler/strip-conditional-loader.js +127 -0
  73. package/lib/template-compiler/compiler.js +23 -9
  74. package/lib/template-compiler/index.js +4 -4
  75. package/lib/web/processTemplate.js +7 -5
  76. package/lib/wxs/loader.js +2 -2
  77. package/lib/wxs/pre-loader.js +2 -2
  78. package/package.json +7 -5
  79. package/lib/runtime/components/react/dist/mpx-icon.jsx +0 -41
  80. package/lib/runtime/components/react/mpx-icon.tsx +0 -102
@@ -0,0 +1,111 @@
1
+ /**
2
+ * ✔ type
3
+ * ✔ size
4
+ * ✔ color
5
+ */
6
+ import { JSX, forwardRef, useRef, createElement } from 'react'
7
+ import { Text, TextStyle, Image } from 'react-native'
8
+ import useInnerProps from '../getInnerListeners'
9
+ import useNodesRef, { HandlerRef } from '../useNodesRef'
10
+ import { useLayout, useTransformStyle, extendObject } from '../utils'
11
+ import Success from './icons/success.png'
12
+ import SuccessNoCircle from './icons/success_no_circle.png'
13
+ import Info from './icons/info.png'
14
+ import Warn from './icons/warn.png'
15
+ import Waiting from './icons/waiting.png'
16
+ import Cancel from './icons/cancel.png'
17
+ import Download from './icons/download.png'
18
+ import Search from './icons/search.png'
19
+ import Clear from './icons/clear.png'
20
+
21
+ export type IconType =
22
+ | 'success'
23
+ | 'success_no_circle'
24
+ | 'info'
25
+ | 'warn'
26
+ | 'waiting'
27
+ | 'cancel'
28
+ | 'download'
29
+ | 'search'
30
+ | 'clear'
31
+
32
+ export interface IconProps {
33
+ type: IconType
34
+ size?: number
35
+ color?: string
36
+ style?: TextStyle & Record<string, any>
37
+ 'enable-offset'?: boolean
38
+ 'enable-var'?: boolean
39
+ 'external-var-context'?: Record<string, any>
40
+ 'parent-font-size'?: number
41
+ 'parent-width'?: number
42
+ 'parent-height'?: number
43
+ }
44
+
45
+ const IconTypeMap = new Map<IconType, string>([
46
+ ['success', Success],
47
+ ['success_no_circle', SuccessNoCircle],
48
+ ['info', Info],
49
+ ['warn', Warn],
50
+ ['waiting', Waiting],
51
+ ['cancel', Cancel],
52
+ ['download', Download],
53
+ ['search', Search],
54
+ ['clear', Clear]
55
+ ])
56
+
57
+ const Icon = forwardRef<HandlerRef<Text, IconProps>, IconProps>(
58
+ (props, ref): JSX.Element => {
59
+ const {
60
+ type,
61
+ size = 23,
62
+ color,
63
+ style = {},
64
+ 'enable-var': enableVar,
65
+ 'external-var-context': externalVarContext,
66
+ 'parent-font-size': parentFontSize,
67
+ 'parent-width': parentWidth,
68
+ 'parent-height': parentHeight
69
+ } = props
70
+
71
+ const source = IconTypeMap.get(type)
72
+
73
+ const defaultStyle = { width: ~~size, height: ~~size }
74
+
75
+ const styleObj = extendObject({}, defaultStyle, style)
76
+
77
+ const {
78
+ hasSelfPercent,
79
+ normalStyle,
80
+ setWidth,
81
+ setHeight
82
+ } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
83
+
84
+ const nodeRef = useRef(null)
85
+ useNodesRef(props, ref, nodeRef, { style: normalStyle })
86
+
87
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef })
88
+
89
+ const innerProps = useInnerProps(
90
+ props,
91
+ extendObject(
92
+ {
93
+ ref: nodeRef,
94
+ source,
95
+ style: extendObject({}, normalStyle, layoutStyle, { tintColor: color })
96
+ },
97
+ layoutProps
98
+ ),
99
+ [],
100
+ {
101
+ layoutRef
102
+ }
103
+ )
104
+
105
+ return createElement(Image, innerProps)
106
+ }
107
+ )
108
+
109
+ Icon.displayName = 'MpxIcon'
110
+
111
+ export default Icon
@@ -49,7 +49,7 @@ export interface ImageProps {
49
49
  mode?: Mode
50
50
  svg?: boolean
51
51
  style?: ImageStyle & Record<string, any>
52
- 'enable-offset'?: boolean;
52
+ 'enable-offset'?: boolean
53
53
  'enable-var'?: boolean
54
54
  'external-var-context'?: Record<string, any>
55
55
  'parent-font-size'?: number
@@ -403,18 +403,23 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
403
403
  }
404
404
  )
405
405
 
406
- return createElement(View, innerProps,
407
- isSvg
408
- ? createElement(SvgCssUri, {
409
- uri: src,
410
- onLayout: onSvgLoad,
411
- onError: binderror && onSvgError,
412
- style: extendObject(
413
- { transformOrigin: 'top left' },
414
- modeStyle
415
- )
416
- })
417
- : loaded && renderImage({
406
+ const SvgImage = createElement(
407
+ View,
408
+ innerProps,
409
+ createElement(SvgCssUri, {
410
+ uri: src,
411
+ onLayout: onSvgLoad,
412
+ onError: binderror && onSvgError,
413
+ style: extendObject(
414
+ { transformOrigin: 'top left' },
415
+ modeStyle
416
+ )
417
+ })
418
+ )
419
+
420
+ const BaseImage = renderImage(
421
+ extendObject(
422
+ {
418
423
  source: { uri: src },
419
424
  resizeMode: resizeMode,
420
425
  onLoad: bindload && onImageLoad,
@@ -427,8 +432,15 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
427
432
  },
428
433
  isCropMode ? modeStyle : {}
429
434
  )
430
- }, enableFastImage)
435
+ },
436
+ isLayoutMode ? {} : innerProps
437
+ ),
438
+ enableFastImage
431
439
  )
440
+
441
+ const LayoutImage = createElement(View, innerProps, loaded && BaseImage)
442
+
443
+ return isSvg ? SvgImage : isLayoutMode ? LayoutImage : BaseImage
432
444
  })
433
445
 
434
446
  Image.displayName = 'mpx-image'
@@ -7,7 +7,7 @@
7
7
  * ✘ placeholder-class
8
8
  * ✔ disabled
9
9
  * ✔ maxlength
10
- * cursor-spacing
10
+ * cursor-spacing
11
11
  * ✔ auto-focus
12
12
  * ✔ focus
13
13
  * ✔ confirm-type
@@ -37,9 +37,8 @@
37
37
  * ✘ bind:keyboardcompositionend
38
38
  * ✘ bind:onkeyboardheightchange
39
39
  */
40
- import { JSX, forwardRef, useMemo, useRef, useState, useContext, useEffect, createElement } from 'react'
40
+ import { JSX, forwardRef, useRef, useState, useContext, useEffect, createElement } from 'react'
41
41
  import {
42
- KeyboardTypeOptions,
43
42
  Platform,
44
43
  TextInput,
45
44
  TextStyle,
@@ -77,11 +76,12 @@ type Type = 'text' | 'number' | 'idcard' | 'digit'
77
76
  export interface InputProps {
78
77
  name?: string
79
78
  style?: InputStyle & Record<string, any>
80
- value?: string
79
+ value?: string | number
81
80
  type?: Type
82
81
  password?: boolean
83
82
  placeholder?: string
84
83
  disabled?: boolean
84
+ 'cursor-spacing'?: number
85
85
  maxlength?: number
86
86
  'auto-focus'?: boolean
87
87
  focus?: boolean
@@ -136,6 +136,7 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
136
136
  'placeholder-style': placeholderStyle,
137
137
  disabled,
138
138
  maxlength = 140,
139
+ 'cursor-spacing': cursorSpacing = 0,
139
140
  'auto-focus': autoFocus,
140
141
  focus,
141
142
  'confirm-type': confirmType = 'done',
@@ -163,7 +164,7 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
163
164
 
164
165
  const formContext = useContext(FormContext)
165
166
 
166
- const setKeyboardAvoidEnabled = useContext(KeyboardAvoidContext)
167
+ const keyboardAvoid = useContext(KeyboardAvoidContext)
167
168
 
168
169
  let formValuesMap: Map<string, FormFieldValue> | undefined
169
170
 
@@ -171,8 +172,19 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
171
172
  formValuesMap = formContext.formValuesMap
172
173
  }
173
174
 
175
+ const parseValue = (value: string | number | undefined): string => {
176
+ if (typeof value === 'string') {
177
+ if (value.length > maxlength && maxlength >= 0) {
178
+ return value.slice(0, maxlength)
179
+ }
180
+ return value
181
+ }
182
+ if (typeof value === 'number') return value + ''
183
+ return ''
184
+ }
185
+
174
186
  const keyboardType = keyboardTypeMap[type]
175
- const defaultValue = type === 'number' && value ? value + '' : value
187
+ const defaultValue = parseValue(value)
176
188
  const placeholderTextColor = parseInlineStyle(placeholderStyle)?.color
177
189
  const textAlignVertical = multiline ? 'top' : 'auto'
178
190
 
@@ -182,6 +194,7 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
182
194
 
183
195
  const [inputValue, setInputValue] = useState(defaultValue)
184
196
  const [contentHeight, setContentHeight] = useState(0)
197
+ const [selection, setSelection] = useState({ start: -1, end: -1 })
185
198
 
186
199
  const styleObj = extendObject(
187
200
  { padding: 0, backgroundColor: '#fff' },
@@ -207,15 +220,15 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
207
220
 
208
221
  useEffect(() => {
209
222
  if (inputValue !== value) {
210
- setInputValue(value)
223
+ setInputValue(parseValue(value))
211
224
  }
212
225
  }, [value])
213
226
 
214
- const selection = useMemo(() => {
215
- if (selectionStart >= 0 && selectionEnd >= 0) {
216
- return { start: selectionStart, end: selectionEnd }
217
- } else if (typeof cursor === 'number') {
218
- return { start: cursor, end: cursor }
227
+ useEffect(() => {
228
+ if (typeof cursor === 'number') {
229
+ setSelection({ start: cursor, end: cursor })
230
+ } else if (selectionStart >= 0 && selectionEnd >= 0 && selectionStart !== selectionEnd) {
231
+ setSelection({ start: selectionStart, end: selectionEnd })
219
232
  }
220
233
  }, [cursor, selectionStart, selectionEnd])
221
234
 
@@ -253,8 +266,23 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
253
266
  }
254
267
  }
255
268
 
269
+ const setKeyboardAvoidContext = () => {
270
+ if (adjustPosition && keyboardAvoid?.current) {
271
+ extendObject(keyboardAvoid.current, {
272
+ cursorSpacing,
273
+ ref: nodeRef
274
+ })
275
+ }
276
+ }
277
+
278
+ const onInputTouchStart = () => {
279
+ // sometimes the focus event occurs later than the keyboardWillShow event
280
+ setKeyboardAvoidContext()
281
+ }
282
+
256
283
  const onInputFocus = (evt: NativeSyntheticEvent<TextInputFocusEventData>) => {
257
- bindfocus!(
284
+ setKeyboardAvoidContext()
285
+ bindfocus && bindfocus(
258
286
  getCustomEvent(
259
287
  'focus',
260
288
  evt,
@@ -270,7 +298,7 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
270
298
  }
271
299
 
272
300
  const onInputBlur = (evt: NativeSyntheticEvent<TextInputFocusEventData>) => {
273
- bindblur!(
301
+ bindblur && bindblur(
274
302
  getCustomEvent(
275
303
  'blur',
276
304
  evt,
@@ -320,7 +348,8 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
320
348
  }
321
349
 
322
350
  const onSelectionChange = (evt: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => {
323
- bindselectionchange!(
351
+ setSelection(evt.nativeEvent.selection)
352
+ bindselectionchange && bindselectionchange(
324
353
  getCustomEvent(
325
354
  'selectionchange',
326
355
  evt,
@@ -387,8 +416,10 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
387
416
  }, [])
388
417
 
389
418
  useEffect(() => {
390
- setKeyboardAvoidEnabled?.(adjustPosition)
391
- }, [adjustPosition])
419
+ if (focus) {
420
+ setKeyboardAvoidContext()
421
+ }
422
+ }, [focus])
392
423
 
393
424
  useUpdateEffect(() => {
394
425
  if (!nodeRef?.current) {
@@ -424,11 +455,12 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
424
455
  },
425
456
  layoutProps,
426
457
  {
427
- onFocus: bindfocus && onInputFocus,
428
- onBlur: bindblur && onInputBlur,
458
+ onTouchStart: onInputTouchStart,
459
+ onFocus: onInputFocus,
460
+ onBlur: onInputBlur,
429
461
  onKeyPress: bindconfirm && onKeyPress,
430
462
  onSubmitEditing: bindconfirm && multiline && onSubmitEditing,
431
- onSelectionChange: bindselectionchange && onSelectionChange,
463
+ onSelectionChange: onSelectionChange,
432
464
  onTextInput: onTextInput,
433
465
  onChange: onChange,
434
466
  onContentSizeChange: onContentSizeChange
@@ -18,11 +18,11 @@
18
18
  * ✔ vtouchmove
19
19
  */
20
20
  import { useEffect, forwardRef, ReactNode, useContext, useCallback, useRef, useMemo, createElement } from 'react'
21
- import { StyleSheet, NativeSyntheticEvent, View, LayoutChangeEvent } from 'react-native'
21
+ import { StyleSheet, View, LayoutChangeEvent } from 'react-native'
22
22
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
23
23
  import useNodesRef, { HandlerRef } from './useNodesRef'
24
24
  import { MovableAreaContext } from './context'
25
- import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, GestureHandler, flatGesture, extendObject, omit } from './utils'
25
+ import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, GestureHandler, flatGesture, extendObject, omit, useNavigation } from './utils'
26
26
  import { GestureDetector, Gesture, GestureTouchEvent, GestureStateChangeEvent, PanGestureHandlerEventPayload, PanGesture } from 'react-native-gesture-handler'
27
27
  import Animated, {
28
28
  useSharedValue,
@@ -126,6 +126,8 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
126
126
  setHeight
127
127
  } = useTransformStyle(Object.assign({}, style, styles.container), { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
128
128
 
129
+ const navigation = useNavigation()
130
+
129
131
  const prevSimultaneousHandlersRef = useRef<Array<GestureHandler>>(originSimultaneousHandlers || [])
130
132
  const prevWaitForHandlersRef = useRef<Array<GestureHandler>>(waitFor || [])
131
133
  const gestureSwitch = useRef(false)
@@ -333,43 +335,44 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
333
335
  setHeight(height || 0)
334
336
  }
335
337
  nodeRef.current?.measure((x: number, y: number, width: number, height: number) => {
336
- layoutRef.current = { x, y, width, height, offsetLeft: 0, offsetTop: 0 }
338
+ const { y: navigationY = 0 } = navigation?.layout || {}
339
+ layoutRef.current = { x, y: y - navigationY, width, height, offsetLeft: 0, offsetTop: 0 }
337
340
  resetBoundaryAndCheck({ width, height })
338
341
  })
339
342
  props.onLayout && props.onLayout(e)
340
343
  }
341
344
 
342
- const extendEvent = useCallback((e: any, obj?: Record<string, any>) => {
345
+ const extendEvent = useCallback((e: any, type: 'start'|'move'|'end') => {
346
+ const { y: navigationY = 0 } = navigation?.layout || {}
343
347
  const touchArr = [e.changedTouches, e.allTouches]
344
348
  touchArr.forEach(touches => {
345
- touches && touches.forEach((item: { absoluteX: number; absoluteY: number; pageX: number; pageY: number }) => {
349
+ touches && touches.forEach((item: { absoluteX: number; absoluteY: number; pageX: number; pageY: number ; clientX: number; clientY: number}) => {
346
350
  item.pageX = item.absoluteX
347
- item.pageY = item.absoluteY
351
+ item.pageY = item.absoluteY - navigationY
352
+ item.clientX = item.absoluteX
353
+ item.clientY = item.absoluteY - navigationY
348
354
  })
349
355
  })
350
356
  Object.assign(e, {
351
- touches: e.allTouches,
352
- detail: {
353
- x: e.changedTouches[0].absoluteX,
354
- y: e.changedTouches[0].absoluteY
355
- },
357
+ touches: type === 'end' ? [] : e.allTouches,
356
358
  currentTarget: {
357
359
  id: props.id || '',
358
360
  dataset: collectDataset(props),
359
361
  offsetLeft: 0,
360
362
  offsetTop: 0
361
- }
362
- }, obj)
363
+ },
364
+ detail: {}
365
+ })
363
366
  }, [])
364
367
 
365
368
  const triggerStartOnJS = ({ e }: { e: GestureTouchEvent }) => {
366
- extendEvent(e)
369
+ extendEvent(e, 'start')
367
370
  bindtouchstart && bindtouchstart(e)
368
371
  catchtouchstart && catchtouchstart(e)
369
372
  }
370
373
 
371
374
  const triggerMoveOnJS = ({ e, hasTouchmove, hasCatchTouchmove, touchEvent }: { e: GestureTouchEvent; hasTouchmove: boolean; hasCatchTouchmove: boolean; touchEvent: string }) => {
372
- extendEvent(e)
375
+ extendEvent(e, 'move')
373
376
  if (hasTouchmove) {
374
377
  if (touchEvent === 'htouchmove') {
375
378
  bindhtouchmove && bindhtouchmove(e)
@@ -390,7 +393,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
390
393
  }
391
394
 
392
395
  const triggerEndOnJS = ({ e }: { e: GestureTouchEvent }) => {
393
- extendEvent(e)
396
+ extendEvent(e, 'end')
394
397
  bindtouchend && bindtouchend(e)
395
398
  catchtouchend && catchtouchend(e)
396
399
  }
@@ -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/index'
3
+ import { PickerView } from '@ant-design/react-native'
3
4
  import React, { forwardRef, useState, useRef, useEffect } from 'react'
4
5
  import useNodesRef, { HandlerRef } from '../useNodesRef' // 引入辅助函数
5
6
  import { TimeProps } from './type'
@@ -0,0 +1,39 @@
1
+ import { ReactNode, useContext, useEffect, useRef } from 'react'
2
+ import { PortalContext, RouteContext, VarContext } from '../context'
3
+ import PortalHost, { portal } from './portal-host'
4
+
5
+ export type PortalProps = {
6
+ children?: ReactNode
7
+ }
8
+
9
+ const Portal = ({ children }:PortalProps): null => {
10
+ const manager = useContext(PortalContext)
11
+ const keyRef = useRef<any>(null)
12
+ const { pageId } = useContext(RouteContext) || {}
13
+ const varContext = useContext(VarContext)
14
+ if (varContext) {
15
+ children = (<VarContext.Provider value={varContext} key='varContextWrap'>{children}</VarContext.Provider>)
16
+ }
17
+ useEffect(() => {
18
+ manager.update(keyRef.current, children)
19
+ }, [children])
20
+ useEffect(() => {
21
+ if (!manager) {
22
+ throw new Error(
23
+ 'Looks like you forgot to wrap your root component with `PortalHost` component from `@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/index`.\n\n'
24
+ )
25
+ }
26
+ keyRef.current = manager.mount(children, null, pageId)
27
+ return () => {
28
+ manager.unmount(keyRef.current)
29
+ }
30
+ }, [])
31
+ return null
32
+ }
33
+
34
+ Portal.Host = PortalHost
35
+ Portal.add = portal.add
36
+ Portal.remove = portal.remove
37
+ Portal.update = portal.update
38
+
39
+ export default Portal
@@ -0,0 +1,141 @@
1
+ import { useEffect, useRef, ReactNode, useMemo, useContext } from 'react'
2
+ import {
3
+ View,
4
+ DeviceEventEmitter,
5
+ NativeEventEmitter,
6
+ StyleSheet
7
+ } from 'react-native'
8
+ import PortalManager from './portal-manager'
9
+ import { PortalContext, RouteContext } from '../context'
10
+
11
+ type PortalHostProps = {
12
+ children: ReactNode,
13
+ pageId: number
14
+ }
15
+
16
+ interface PortalManagerContextValue {
17
+ mount: (key: number, children: React.ReactNode) => void
18
+ update: (key: number, children: React.ReactNode) => void
19
+ unmount: (key: number) => void
20
+ }
21
+
22
+ export type Operation =
23
+ | { type: 'mount'; key: number; children: ReactNode }
24
+ | { type: 'update'; key: number; children: ReactNode }
25
+ | { type: 'unmount'; key: number }
26
+
27
+ // events
28
+ const addType = 'MPX_RN_ADD_PORTAL'
29
+ const removeType = 'MPX_RN_REMOVE_PORTAL'
30
+ const updateType = 'MPX_RN_UPDATE_PORTAL'
31
+ // fix react native web does not support DeviceEventEmitter
32
+ const TopViewEventEmitter = DeviceEventEmitter || new NativeEventEmitter()
33
+
34
+ const styles = StyleSheet.create({
35
+ container: {
36
+ flex: 1
37
+ }
38
+ })
39
+
40
+ class PortalGuard {
41
+ private nextKey = 10000
42
+ add = (e: ReactNode, id: number|null) => {
43
+ const key = this.nextKey++
44
+ TopViewEventEmitter.emit(addType, e, key, id)
45
+ return key
46
+ }
47
+
48
+ remove = (key: number) => {
49
+ TopViewEventEmitter.emit(removeType, key)
50
+ }
51
+
52
+ update = (key: number, e: ReactNode) => {
53
+ TopViewEventEmitter.emit(updateType, key, e)
54
+ }
55
+ }
56
+ /**
57
+ * portal
58
+ */
59
+ export const portal = new PortalGuard()
60
+
61
+ const PortalHost = ({ children } :PortalHostProps): JSX.Element => {
62
+ const _nextKey = useRef(0)
63
+ const manager = useRef<PortalManagerContextValue | null>(null)
64
+ const queue = useRef<Array<{ type: string, key: number; children: ReactNode }>>([])
65
+ const { pageId } = useContext(RouteContext) || {}
66
+ const mount = (children: ReactNode, _key?: number, id?: number|null) => {
67
+ if (id !== pageId) return
68
+ const key = _key || _nextKey.current++
69
+ if (manager.current) {
70
+ manager.current.mount(key, children)
71
+ } else {
72
+ queue.current.push({ type: 'mount', key, children })
73
+ }
74
+ return key
75
+ }
76
+
77
+ const unmount = (key: number) => {
78
+ if (manager.current) {
79
+ manager.current.unmount(key)
80
+ } else {
81
+ queue.current.push({ type: 'unmount', key, children })
82
+ }
83
+ }
84
+
85
+ const update = (key: number, children?: ReactNode) => {
86
+ if (manager.current) {
87
+ manager.current.update(key, children)
88
+ } else {
89
+ const operation = { type: 'mount', key, children }
90
+ const index = queue.current.findIndex((q) => q.type === 'mount' && q.key === key)
91
+ if (index > -1) {
92
+ queue.current[index] = operation
93
+ } else {
94
+ queue.current.push(operation)
95
+ }
96
+ }
97
+ }
98
+ const subScriptions = useMemo(() => {
99
+ return [
100
+ TopViewEventEmitter.addListener(addType, mount),
101
+ TopViewEventEmitter.addListener(removeType, unmount),
102
+ TopViewEventEmitter.addListener(updateType, update)
103
+ ]
104
+ }, [])
105
+ useEffect(() => {
106
+ while (queue.current.length && manager.current) {
107
+ const operation = queue.current.shift()
108
+ if (!operation) return
109
+ switch (operation.type) {
110
+ case 'mount':
111
+ manager.current.mount(operation.key, operation.children)
112
+ break
113
+ case 'unmount':
114
+ manager.current.unmount(operation.key)
115
+ break
116
+ }
117
+ }
118
+
119
+ return () => {
120
+ subScriptions.forEach((subScription:any) => {
121
+ subScription.remove()
122
+ })
123
+ }
124
+ }, [])
125
+ return (
126
+ <PortalContext.Provider
127
+ value={{
128
+ mount,
129
+ update,
130
+ unmount
131
+ }}
132
+ >
133
+ <View style={styles.container} collapsable={false}>
134
+ {children}
135
+ </View>
136
+ <PortalManager ref={manager} />
137
+ </PortalContext.Provider>
138
+ )
139
+ }
140
+
141
+ export default PortalHost
@@ -0,0 +1,64 @@
1
+ import { useState, useCallback, forwardRef, ForwardedRef, useImperativeHandle, ReactNode, ReactElement } from 'react'
2
+ import { View, StyleSheet } from 'react-native'
3
+ import { extendObject } from '../utils'
4
+
5
+ export type State = {
6
+ portals: Array<{
7
+ key: number
8
+ children: ReactNode
9
+ }>
10
+ }
11
+
12
+ type PortalManagerProps = {
13
+ }
14
+
15
+ const _PortalManager = forwardRef((props: PortalManagerProps, ref:ForwardedRef<unknown>): ReactElement => {
16
+ const [state, setState] = useState<State>({
17
+ portals: []
18
+ })
19
+
20
+ const mount = useCallback((key: number, children: ReactNode) => {
21
+ setState((prevState) => ({
22
+ portals: [...prevState.portals, { key, children }]
23
+ }))
24
+ }, [state])
25
+
26
+ const update = useCallback((key: number, children: ReactNode) => {
27
+ setState((prevState) => ({
28
+ portals: prevState.portals.map((item) => {
29
+ if (item.key === key) {
30
+ return extendObject({}, item, { children })
31
+ }
32
+ return item
33
+ })
34
+ }))
35
+ }, [state])
36
+
37
+ const unmount = useCallback((key: number) => {
38
+ setState((prevState) => ({
39
+ portals: prevState.portals.filter((item) => item.key !== key)
40
+ }))
41
+ }, [])
42
+
43
+ useImperativeHandle(ref, () => ({
44
+ mount,
45
+ update,
46
+ unmount,
47
+ portals: state.portals
48
+ }))
49
+
50
+ return (
51
+ <>
52
+ {state.portals.map(({ key, children }, i) => (
53
+ <View
54
+ key={key}
55
+ collapsable={false} // Need collapsable=false here to clip the elevations
56
+ style={[StyleSheet.absoluteFill, { zIndex: 1000 + i, pointerEvents: 'box-none' }]}>
57
+ {children}
58
+ </View>
59
+ ))}
60
+ </>
61
+ )
62
+ })
63
+
64
+ export default _PortalManager