@mpxjs/webpack-plugin 2.9.73 → 2.10.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 (82) 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/event.config.js +1 -0
  15. package/lib/runtime/components/react/dist/getInnerListeners.js +8 -5
  16. package/lib/runtime/components/react/dist/mpx-button.jsx +1 -1
  17. package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
  18. package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
  19. package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
  20. package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
  21. package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
  22. package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
  23. package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
  24. package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
  25. package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
  26. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +50 -0
  27. package/lib/runtime/components/react/dist/mpx-image.jsx +19 -18
  28. package/lib/runtime/components/react/dist/mpx-input.jsx +48 -19
  29. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +16 -14
  30. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +2 -1
  31. package/lib/runtime/components/react/dist/mpx-portal/index.jsx +30 -0
  32. package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +112 -0
  33. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +41 -0
  34. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +1 -1
  35. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +20 -9
  36. package/lib/runtime/components/react/dist/mpx-swiper.jsx +3 -2
  37. package/lib/runtime/components/react/dist/mpx-textarea.jsx +11 -3
  38. package/lib/runtime/components/react/dist/mpx-video.jsx +248 -0
  39. package/lib/runtime/components/react/dist/mpx-web-view.jsx +149 -50
  40. package/lib/runtime/components/react/dist/useAnimationHooks.js +4 -4
  41. package/lib/runtime/components/react/dist/utils.jsx +8 -3
  42. package/lib/runtime/components/react/event.config.ts +2 -0
  43. package/lib/runtime/components/react/getInnerListeners.ts +8 -5
  44. package/lib/runtime/components/react/mpx-button.tsx +1 -1
  45. package/lib/runtime/components/react/mpx-icon/icons/cancel.png +0 -0
  46. package/lib/runtime/components/react/mpx-icon/icons/clear.png +0 -0
  47. package/lib/runtime/components/react/mpx-icon/icons/download.png +0 -0
  48. package/lib/runtime/components/react/mpx-icon/icons/info.png +0 -0
  49. package/lib/runtime/components/react/mpx-icon/icons/search.png +0 -0
  50. package/lib/runtime/components/react/mpx-icon/icons/success.png +0 -0
  51. package/lib/runtime/components/react/mpx-icon/icons/success_no_circle.png +0 -0
  52. package/lib/runtime/components/react/mpx-icon/icons/waiting.png +0 -0
  53. package/lib/runtime/components/react/mpx-icon/icons/warn.png +0 -0
  54. package/lib/runtime/components/react/mpx-icon/index.tsx +111 -0
  55. package/lib/runtime/components/react/mpx-image.tsx +26 -14
  56. package/lib/runtime/components/react/mpx-input.tsx +52 -20
  57. package/lib/runtime/components/react/mpx-movable-view.tsx +19 -16
  58. package/lib/runtime/components/react/mpx-picker/time.tsx +2 -1
  59. package/lib/runtime/components/react/mpx-portal/index.tsx +39 -0
  60. package/lib/runtime/components/react/mpx-portal/portal-host.tsx +141 -0
  61. package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +64 -0
  62. package/lib/runtime/components/react/mpx-root-portal.tsx +1 -1
  63. package/lib/runtime/components/react/mpx-scroll-view.tsx +22 -10
  64. package/lib/runtime/components/react/mpx-swiper.tsx +3 -2
  65. package/lib/runtime/components/react/mpx-textarea.tsx +13 -3
  66. package/lib/runtime/components/react/mpx-video.tsx +388 -0
  67. package/lib/runtime/components/react/mpx-web-view.tsx +180 -49
  68. package/lib/runtime/components/react/types/getInnerListeners.d.ts +1 -1
  69. package/lib/runtime/components/react/types/global.d.ts +8 -0
  70. package/lib/runtime/components/react/useAnimationHooks.ts +4 -4
  71. package/lib/runtime/components/react/utils.tsx +12 -6
  72. package/lib/script-setup-compiler/index.js +6 -2
  73. package/lib/style-compiler/index.js +3 -4
  74. package/lib/style-compiler/strip-conditional-loader.js +127 -0
  75. package/lib/template-compiler/compiler.js +19 -8
  76. package/lib/template-compiler/index.js +4 -4
  77. package/lib/web/processTemplate.js +7 -5
  78. package/lib/wxs/loader.js +2 -2
  79. package/lib/wxs/pre-loader.js +2 -2
  80. package/package.json +7 -5
  81. package/lib/runtime/components/react/dist/mpx-icon.jsx +0 -41
  82. package/lib/runtime/components/react/mpx-icon.tsx +0 -102
@@ -7,6 +7,8 @@ const RecordResourceMapDependency = require('./dependencies/RecordResourceMapDep
7
7
  module.exports = function loader (content, prevOptions) {
8
8
  const options = prevOptions || loaderUtils.getOptions(this) || {}
9
9
  const context = options.context || this.rootContext
10
+ const mpx = this.getMpx()
11
+ const isRN = ['ios', 'android', 'harmony'].includes(mpx.mode)
10
12
 
11
13
  let url = loaderUtils.interpolateName(this, options.name, {
12
14
  context,
@@ -33,6 +35,9 @@ module.exports = function loader (content, prevOptions) {
33
35
 
34
36
  let publicPath = `__webpack_public_path__ + ${JSON.stringify(url)}`
35
37
 
38
+ // todo 未来添加分包处理后相对地址不一定是./开头的,需要考虑通过dependency的方式在sourceModule时通过最终的chunkName得到准确的相对路径
39
+ if (isRN) publicPath = `__non_webpack_require__(${JSON.stringify(`./${url}`)})`
40
+
36
41
  if (options.publicPath) {
37
42
  if (typeof options.publicPath === 'function') {
38
43
  publicPath = options.publicPath(url, this.resourcePath, context)
package/lib/index.js CHANGED
@@ -54,6 +54,7 @@ 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 styleStripConditionalPath = 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')
@@ -1506,12 +1507,12 @@ class MpxWebpackPlugin {
1506
1507
  ' code = code.replace(/const { (.*?) } = ctx/g, function (match, $1) {\n' +
1507
1508
  ' var arr = $1.split(", ")\n' +
1508
1509
  ' var str = ""\n' +
1509
- ' var pattern = /(.*):(.*)/\n' +
1510
+ ' var pattern = /(\\w+)(?::\\s*(\\w+))?/\n' +
1510
1511
  ' for (var i = 0; i < arr.length; i++) {\n' +
1511
1512
  ' var result = arr[i].match(pattern)\n' +
1512
1513
  ' var left = result[1]\n' +
1513
- ' var right = result[2]\n' +
1514
- ' str += "var" + right + " = ctx." + left\n' +
1514
+ ' var right = result[2] || left\n' +
1515
+ ' str += "var " + right + " = ctx." + left + ";\\n "\n' +
1515
1516
  ' }\n' +
1516
1517
  ' return str\n' +
1517
1518
  ' })\n' +
@@ -1786,6 +1787,23 @@ try {
1786
1787
  if (queryObj.mpx && queryObj.mpx !== MPX_PROCESSED_FLAG) {
1787
1788
  const type = queryObj.type
1788
1789
  const extract = queryObj.extract
1790
+
1791
+ if (type === 'styles') {
1792
+ let insertBeforeIndex = -1
1793
+ // 单次遍历收集所有索引
1794
+ loaders.forEach((loader, index) => {
1795
+ const currentLoader = toPosix(loader.loader)
1796
+ if (currentLoader.includes('node_modules/stylus-loader') || currentLoader.includes('node_modules/sass-loader') || currentLoader.includes('node_modules/less-loader')) {
1797
+ insertBeforeIndex = index
1798
+ }
1799
+ })
1800
+
1801
+ if (insertBeforeIndex !== -1) {
1802
+ loaders.splice(insertBeforeIndex, 0, { loader: styleStripConditionalPath })
1803
+ }
1804
+ loaders.push({ loader: styleStripConditionalPath })
1805
+ }
1806
+
1789
1807
  switch (type) {
1790
1808
  case 'styles':
1791
1809
  case 'template': {
@@ -92,7 +92,7 @@ module.exports = function ({ print }) {
92
92
  qa: qaPropLog
93
93
  },
94
94
  {
95
- test: /^(placeholder-style|placeholder-class|cursor-spacing|always-embed|hold-keyboard|safe-password-.+)$/,
95
+ test: /^(placeholder-style|placeholder-class|always-embed|hold-keyboard|safe-password-.+)$/,
96
96
  ios: iosPropLog,
97
97
  android: androidPropLog
98
98
  }
@@ -78,7 +78,7 @@ module.exports = function ({ print }) {
78
78
  }
79
79
  },
80
80
  {
81
- test: /^(placeholder-style|placeholder-class|cursor-spacing|always-embed|hold-keyboard|disable-default-padding|adjust-keyboard-to|fixed|show-confirm-bar)$/,
81
+ test: /^(placeholder-style|placeholder-class|always-embed|hold-keyboard|disable-default-padding|adjust-keyboard-to|fixed|show-confirm-bar)$/,
82
82
  ios: iosPropLog,
83
83
  android: androidPropLog
84
84
  }
@@ -11,7 +11,7 @@ const JD_UNSUPPORTED_TAG_NAME_ARR = ['functional-page-navigator', 'live-pusher',
11
11
  // 快应用不支持的标签集合
12
12
  const QA_UNSUPPORTED_TAG_NAME_ARR = ['movable-view', 'movable-area', 'open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'cover-image']
13
13
  // RN不支持的标签集合
14
- const RN_UNSUPPORTED_TAG_NAME_ARR = ['open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'progress', 'slider', 'audio', 'camera', 'video', 'match-media', 'page-container', 'editor', 'keyboard-accessory', 'map']
14
+ const RN_UNSUPPORTED_TAG_NAME_ARR = ['open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'progress', 'slider', 'audio', 'camera', 'match-media', 'page-container', 'editor', 'keyboard-accessory', 'map']
15
15
 
16
16
  /**
17
17
  * @param {function(object): function} print
@@ -11,12 +11,24 @@ module.exports = function ({ print }) {
11
11
  const aliEventLogError = print({ platform: 'ali', tag: TAG_NAME, isError: false, type: 'event' })
12
12
  const qaPropLog = print({ platform: 'qa', tag: TAG_NAME, isError: false })
13
13
  const qaEventLogError = print({ platform: 'qa', tag: TAG_NAME, isError: false, type: 'event' })
14
+ const iosPropLog = print({ platform: 'ios', tag: TAG_NAME, isError: false })
15
+ const iosEventLogError = print({ platform: 'ios', tag: TAG_NAME, isError: false, type: 'event' })
16
+ const androidPropLog = print({ platform: 'android', tag: TAG_NAME, isError: false })
17
+ const androidEventLogError = print({ platform: 'android', tag: TAG_NAME, isError: false, type: 'event' })
14
18
  return {
15
19
  test: TAG_NAME,
16
20
  web (tag, { el }) {
17
21
  el.isBuiltIn = true
18
22
  return 'mpx-video'
19
23
  },
24
+ android (tag, { el }) {
25
+ el.isBuiltIn = true
26
+ return 'mpx-video'
27
+ },
28
+ ios (tag, { el }) {
29
+ el.isBuiltIn = true
30
+ return 'mpx-video'
31
+ },
20
32
  props: [
21
33
  {
22
34
  test: /^(enable-danmu|danmu-btn|show-progress|play-btn-position|enable-play-gesture|auto-pause-if-navigate|auto-pause-if-open-native|vslide-gesture|vslide-gesture-in-fullscreen|ad-unit-id|poster-for-crawler|show-casting-button|picture-in-picture-mode|picture-in-picture-show-progress|enable-auto-rotation|show-snapshot-button|show-screen-lock-button)$/,
@@ -41,13 +53,20 @@ module.exports = function ({ print }) {
41
53
  qq: qqPropLog
42
54
  },
43
55
  {
44
-
45
56
  test: /^(duration|danmu-list|danmu-btn|enable-danmu|muted|initial-time|page-gesture|direction|show-progress|show-center-play-btn|enable-progress-gesture|show-mute-btn|title|enable-play-gesture|auto-pause-if-navigate|auto-pause-if-open-native|vslide-gesture|vslide-gesture-in-fullscreen|ad-unit-id|poster-for-crawler|show-casting-button|picture-in-picture-mode|picture-in-picture-show-progress|enable-auto-rotation|show-screen-lock-button|show-snapshot-button)$/,
46
57
  tt: ttPropLog
47
58
  },
48
59
  {
49
60
  test: /^(duration|danmu-list|danmu-btn|enable-danmu|muted|initial-time|page-gesture|direction|show-progress|show-center-play-btn|enable-progress-gesture|show-mute-btn|enable-play-gesture|auto-pause-if-navigate|auto-pause-if-open-native|vslide-gesture|vslide-gesture-in-fullscreen|ad-unit-id|poster-for-crawler|show-casting-button|picture-in-picture-mode|picture-in-picture-show-progress|enable-auto-rotation|show-screen-lock-button|show-snapshot-button)$/,
50
61
  qa: qaPropLog
62
+ },
63
+ {
64
+ test: /^(duration|enable-danmu|danmu-btn|page-gesture|direction|show-progress|show-fullscreen-btn|show-center-play-btn|enable-progress-gesture|show-mute-btn|title|play-btn-position|enable-play-gesture|auto-pause-if-navigate|auto-pause-if-open-native|vslide-gesture|vslide-gesture-in-fullscreen|show-bottom-progress|ad-unit-id|poster-for-crawler|show-casting-button|picture-in-picture-mode|picture-in-picture-show-progress| picture-in-picture-init-position|show-snapshot-button|show-screen-lock-button|show-background-playback-button|background-poster|referrer-policy|is-live)$/,
65
+ ios: iosPropLog
66
+ },
67
+ {
68
+ test: /^(duration|enable-danmu|danmu-btn|page-gesture|direction|show-progress|show-fullscreen-btn|show-center-play-btn|enable-progress-gesture|show-mute-btn|title|play-btn-position|enable-play-gesture|auto-pause-if-navigate|auto-pause-if-open-native|vslide-gesture|vslide-gesture-in-fullscreen|show-bottom-progress|ad-unit-id|poster-for-crawler|show-casting-button|picture-in-picture-mode|picture-in-picture-show-progress| picture-in-picture-init-position|enable-auto-rotation|show-snapshot-button|show-screen-lock-button|show-background-playback-button|background-poster|referrer-policy|is-live)$/,
69
+ android: androidPropLog
51
70
  }
52
71
  ],
53
72
  event: [
@@ -82,6 +101,14 @@ module.exports = function ({ print }) {
82
101
  {
83
102
  test: /^(progress|enterpictureinpicture|leavepictureinpicture|controlstoggle|loadedmetadata|seekcomplete)$/,
84
103
  qa: qaEventLogError
104
+ },
105
+ {
106
+ test: /^(progress|controlstoggle|enterpictureinpicture|leavepictureinpicture|castinguserselect|castingstatechange|castinginterrupt)$/,
107
+ ios: iosEventLogError
108
+ },
109
+ {
110
+ test: /^(progress|enterpictureinpicture|leavepictureinpicture|castinguserselect|castingstatechange|castinginterrupt)$/,
111
+ android: androidEventLogError
85
112
  }
86
113
  ]
87
114
  }
@@ -68,7 +68,7 @@ module.exports = function getSpec ({ warn, error }) {
68
68
  if (el) {
69
69
  const injectWxsProp = {
70
70
  injectWxsPath: '~' + normalize.lib('runtime/swanHelper.wxs'),
71
- injectWxsModuleName: '__swanHelper__'
71
+ injectWxsModuleName: 'mpxSwanHelper'
72
72
  }
73
73
  if (el.injectWxsProps && Array.isArray(el.injectWxsProps)) {
74
74
  el.injectWxsProps.push(injectWxsProp)
@@ -78,7 +78,7 @@ module.exports = function getSpec ({ warn, error }) {
78
78
  }
79
79
  return {
80
80
  name: 's-for',
81
- value: `${itemName}, ${indexName} in __swanHelper__.processFor(${listName})${keyStr}`
81
+ value: `${itemName}, ${indexName} in mpxSwanHelper.processFor(${listName})${keyStr}`
82
82
  }
83
83
  },
84
84
  web ({ value }, { el }) {
@@ -26,17 +26,19 @@ module.exports = function (script, {
26
26
  output += `
27
27
  import { getComponent } from ${stringifyRequest(loaderContext, optionProcessorPath)}
28
28
  import { NavigationContainer, StackActions } from '@react-navigation/native'
29
- import { createStackNavigator } from '@react-navigation/stack'
30
- import { Provider } from '@ant-design/react-native'
29
+ import { createNativeStackNavigator } from '@react-navigation/native-stack'
30
+ import PortalHost from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/portal-host'
31
+ import { useHeaderHeight } from '@react-navigation/elements';
31
32
  import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
32
33
  import { GestureHandlerRootView } from 'react-native-gesture-handler'
33
34
 
34
35
  global.__navigationHelper = {
35
36
  NavigationContainer: NavigationContainer,
36
- createStackNavigator: createStackNavigator,
37
+ createStackNavigator: createNativeStackNavigator,
38
+ useHeaderHeight: useHeaderHeight,
37
39
  StackActions: StackActions,
38
40
  GestureHandlerRootView: GestureHandlerRootView,
39
- Provider: Provider,
41
+ PortalHost: PortalHost,
40
42
  SafeAreaProvider: SafeAreaProvider,
41
43
  useSafeAreaInsets: useSafeAreaInsets
42
44
  }\n`
@@ -28,9 +28,10 @@ module.exports = function (template, {
28
28
  externalClasses,
29
29
  checkUsingComponents,
30
30
  autoVirtualHostRules,
31
+ forceProxyEventRules,
31
32
  customTextRules
32
33
  } = mpx
33
- const { resourcePath } = parseRequest(loaderContext.resource)
34
+ const { resourcePath, rawResourcePath } = parseRequest(loaderContext.resource)
34
35
  const builtInComponentsMap = {}
35
36
 
36
37
  let genericsInfo
@@ -77,7 +78,7 @@ module.exports = function (template, {
77
78
  // todo 后续输出web也采用mpx的scoped处理
78
79
  hasScoped: false,
79
80
  moduleId,
80
- filePath: resourcePath,
81
+ filePath: rawResourcePath,
81
82
  // react中模版i18n不需要特殊处理
82
83
  i18n: null,
83
84
  checkUsingComponents,
@@ -86,12 +87,13 @@ module.exports = function (template, {
86
87
  // web模式下实现抽象组件
87
88
  componentGenerics,
88
89
  hasVirtualHost: matchCondition(resourcePath, autoVirtualHostRules),
90
+ forceProxyEvent: matchCondition(resourcePath, forceProxyEventRules),
89
91
  isCustomText: matchCondition(resourcePath, customTextRules)
90
92
  })
91
93
 
92
94
  if (meta.wxsContentMap) {
93
95
  for (const module in meta.wxsContentMap) {
94
- wxsContentMap[`${resourcePath}~${module}`] = meta.wxsContentMap[module]
96
+ wxsContentMap[`${rawResourcePath}~${module}`] = meta.wxsContentMap[module]
95
97
  }
96
98
  }
97
99
  if (meta.builtInComponentsMap) {
@@ -0,0 +1,108 @@
1
+ import React, { ReactNode, useContext, useEffect } from 'react'
2
+ import { DimensionValue, EmitterSubscription, Keyboard, Platform, View, ViewStyle } from 'react-native'
3
+ import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated'
4
+ import { KeyboardAvoidContext } from './context'
5
+ import { extendObject } from './utils'
6
+
7
+ type KeyboardAvoidViewProps = {
8
+ children?: ReactNode
9
+ style?: ViewStyle
10
+ contentContainerStyle?: ViewStyle
11
+ }
12
+
13
+ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: KeyboardAvoidViewProps) => {
14
+ const isIOS = Platform.OS === 'ios'
15
+ const duration = isIOS ? 250 : 300
16
+ const easing = isIOS ? Easing.inOut(Easing.ease) : Easing.out(Easing.quad)
17
+
18
+ const offset = useSharedValue(0)
19
+ const basic = useSharedValue('auto')
20
+ const keyboardAvoid = useContext(KeyboardAvoidContext)
21
+
22
+ const animatedStyle = useAnimatedStyle(() => {
23
+ return Object.assign(
24
+ {
25
+ transform: [{ translateY: -offset.value }]
26
+ },
27
+ isIOS ? {} : { flexBasis: basic.value as DimensionValue }
28
+ )
29
+ })
30
+
31
+ const resetKeyboard = () => {
32
+ keyboardAvoid?.current && extendObject(keyboardAvoid.current, {
33
+ cursorSpacing: 0,
34
+ ref: null
35
+ })
36
+ offset.value = withTiming(0, { duration, easing })
37
+ basic.value = 'auto'
38
+ }
39
+
40
+ useEffect(() => {
41
+ let subscriptions: EmitterSubscription[] = []
42
+
43
+ if (isIOS) {
44
+ subscriptions = [
45
+ Keyboard.addListener('keyboardWillShow', (evt: any) => {
46
+ if (!keyboardAvoid?.current) return
47
+ const { endCoordinates } = evt
48
+ const { ref, cursorSpacing = 0 } = keyboardAvoid.current
49
+ setTimeout(() => {
50
+ ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
51
+ const aboveOffset = offset.value + pageY + height - endCoordinates.screenY
52
+ const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
53
+ const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing)
54
+ const value = aboveOffset > 0 ? belowValue : aboveValue
55
+ offset.value = withTiming(value, { duration, easing })
56
+ })
57
+ })
58
+ }),
59
+ Keyboard.addListener('keyboardWillHide', resetKeyboard)
60
+ ]
61
+ } else {
62
+ subscriptions = [
63
+ Keyboard.addListener('keyboardDidShow', (evt: any) => {
64
+ if (!keyboardAvoid?.current) return
65
+ const { endCoordinates } = evt
66
+ const { ref, cursorSpacing = 0 } = keyboardAvoid.current
67
+ ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
68
+ const aboveOffset = pageY + height - endCoordinates.screenY
69
+ const belowOffset = endCoordinates.height - aboveOffset
70
+ const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
71
+ const belowValue = Math.min(belowOffset, cursorSpacing)
72
+ const value = aboveOffset > 0 ? belowValue : aboveValue
73
+ offset.value = withTiming(value, { duration, easing }, (finished) => {
74
+ if (finished) {
75
+ /**
76
+ * In the Android environment, the layout information is not synchronized after the animation,
77
+ * which results in the inability to correctly trigger element events.
78
+ * Here, we utilize flexBasic to proactively trigger a re-layout
79
+ */
80
+ basic.value = '99.99%'
81
+ }
82
+ })
83
+ })
84
+ }),
85
+ Keyboard.addListener('keyboardDidHide', resetKeyboard)
86
+ ]
87
+ }
88
+
89
+ return () => {
90
+ subscriptions.forEach(subscription => subscription.remove())
91
+ }
92
+ }, [keyboardAvoid])
93
+
94
+ return (
95
+ <View style={style}>
96
+ <Animated.View
97
+ style={[
98
+ contentContainerStyle,
99
+ animatedStyle
100
+ ]}
101
+ >
102
+ {children}
103
+ </Animated.View>
104
+ </View>
105
+ )
106
+ }
107
+
108
+ export default KeyboardAvoidingView
@@ -5,7 +5,10 @@ export type LabelContextValue = MutableRefObject<{
5
5
  triggerChange: (evt: NativeSyntheticEvent<TouchEvent>) => void
6
6
  }>
7
7
 
8
- export type KeyboardAvoidContextValue = (enabled: boolean) => void
8
+ export type KeyboardAvoidContextValue = MutableRefObject<{
9
+ cursorSpacing: number
10
+ ref: MutableRefObject<any>
11
+ }>
9
12
 
10
13
  export interface GroupValue {
11
14
  [key: string]: { checked: boolean; setValue: Dispatch<SetStateAction<boolean>> }
@@ -33,10 +36,21 @@ export interface IntersectionObserver {
33
36
  }
34
37
  }
35
38
 
39
+ export interface PortalContextValue {
40
+ mount: (children: React.ReactNode, key?: number | null, id?: number| null) => number| undefined
41
+ update: (key: number, children: React.ReactNode) => void
42
+ unmount: (key: number) => void
43
+ }
44
+
36
45
  export interface ScrollViewContextValue {
37
46
  gestureRef: React.RefObject<any> | null
38
47
  }
39
48
 
49
+ export interface RouteContextValue {
50
+ pageId: number
51
+ navigation: Record<string, any>
52
+ }
53
+
40
54
  export const MovableAreaContext = createContext({ width: 0, height: 0 })
41
55
 
42
56
  export const FormContext = createContext<FormContextValue | null>(null)
@@ -53,10 +67,12 @@ export const VarContext = createContext({})
53
67
 
54
68
  export const IntersectionObserverContext = createContext<IntersectionObserver | null>(null)
55
69
 
56
- export const RouteContext = createContext<number | null>(null)
70
+ export const RouteContext = createContext<RouteContextValue | null>(null)
57
71
 
58
72
  export const SwiperContext = createContext({})
59
73
 
60
74
  export const KeyboardAvoidContext = createContext<KeyboardAvoidContextValue | null>(null)
61
75
 
62
76
  export const ScrollViewContext = createContext<ScrollViewContextValue>({ gestureRef: null })
77
+
78
+ export const PortalContext = createContext<PortalContextValue>(null as any)
@@ -0,0 +1,89 @@
1
+ import React, { useContext, useEffect } from 'react';
2
+ import { Keyboard, Platform, View } from 'react-native';
3
+ import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
4
+ import { KeyboardAvoidContext } from './context';
5
+ import { extendObject } from './utils';
6
+ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
7
+ const isIOS = Platform.OS === 'ios';
8
+ const duration = isIOS ? 250 : 300;
9
+ const easing = isIOS ? Easing.inOut(Easing.ease) : Easing.out(Easing.quad);
10
+ const offset = useSharedValue(0);
11
+ const basic = useSharedValue('auto');
12
+ const keyboardAvoid = useContext(KeyboardAvoidContext);
13
+ const animatedStyle = useAnimatedStyle(() => {
14
+ return Object.assign({
15
+ transform: [{ translateY: -offset.value }]
16
+ }, isIOS ? {} : { flexBasis: basic.value });
17
+ });
18
+ const resetKeyboard = () => {
19
+ keyboardAvoid?.current && extendObject(keyboardAvoid.current, {
20
+ cursorSpacing: 0,
21
+ ref: null
22
+ });
23
+ offset.value = withTiming(0, { duration, easing });
24
+ basic.value = 'auto';
25
+ };
26
+ useEffect(() => {
27
+ let subscriptions = [];
28
+ if (isIOS) {
29
+ subscriptions = [
30
+ Keyboard.addListener('keyboardWillShow', (evt) => {
31
+ if (!keyboardAvoid?.current)
32
+ return;
33
+ const { endCoordinates } = evt;
34
+ const { ref, cursorSpacing = 0 } = keyboardAvoid.current;
35
+ setTimeout(() => {
36
+ ref?.current?.measure((x, y, width, height, pageX, pageY) => {
37
+ const aboveOffset = offset.value + pageY + height - endCoordinates.screenY;
38
+ const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
39
+ const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing);
40
+ const value = aboveOffset > 0 ? belowValue : aboveValue;
41
+ offset.value = withTiming(value, { duration, easing });
42
+ });
43
+ });
44
+ }),
45
+ Keyboard.addListener('keyboardWillHide', resetKeyboard)
46
+ ];
47
+ }
48
+ else {
49
+ subscriptions = [
50
+ Keyboard.addListener('keyboardDidShow', (evt) => {
51
+ if (!keyboardAvoid?.current)
52
+ return;
53
+ const { endCoordinates } = evt;
54
+ const { ref, cursorSpacing = 0 } = keyboardAvoid.current;
55
+ ref?.current?.measure((x, y, width, height, pageX, pageY) => {
56
+ const aboveOffset = pageY + height - endCoordinates.screenY;
57
+ const belowOffset = endCoordinates.height - aboveOffset;
58
+ const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
59
+ const belowValue = Math.min(belowOffset, cursorSpacing);
60
+ const value = aboveOffset > 0 ? belowValue : aboveValue;
61
+ offset.value = withTiming(value, { duration, easing }, (finished) => {
62
+ if (finished) {
63
+ /**
64
+ * In the Android environment, the layout information is not synchronized after the animation,
65
+ * which results in the inability to correctly trigger element events.
66
+ * Here, we utilize flexBasic to proactively trigger a re-layout
67
+ */
68
+ basic.value = '99.99%';
69
+ }
70
+ });
71
+ });
72
+ }),
73
+ Keyboard.addListener('keyboardDidHide', resetKeyboard)
74
+ ];
75
+ }
76
+ return () => {
77
+ subscriptions.forEach(subscription => subscription.remove());
78
+ };
79
+ }, [keyboardAvoid]);
80
+ return (<View style={style}>
81
+ <Animated.View style={[
82
+ contentContainerStyle,
83
+ animatedStyle
84
+ ]}>
85
+ {children}
86
+ </Animated.View>
87
+ </View>);
88
+ };
89
+ export default KeyboardAvoidingView;
@@ -11,3 +11,4 @@ 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);
@@ -1,3 +1,4 @@
1
+ export const TAP_EVENTS = ['bindtap', 'catchtap', 'capture-bindtap', 'capture-catchtap'];
1
2
  const eventConfigMap = {
2
3
  bindtap: { bitFlag: '0', events: ['onTouchStart', 'onTouchMove', 'onTouchEnd'] },
3
4
  bindlongpress: { bitFlag: '1', events: ['onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'] },
@@ -1,8 +1,7 @@
1
1
  import { useRef, useMemo } from 'react';
2
2
  import { hasOwn, collectDataset } from '@mpxjs/utils';
3
- import { useNavigation } from '@react-navigation/native';
4
- import { omit, extendObject } from './utils';
5
- import eventConfigMap from './event.config';
3
+ import { omit, extendObject, useNavigation } from './utils';
4
+ import eventConfigMap, { TAP_EVENTS } from './event.config';
6
5
  const globalEventState = {
7
6
  needPress: true
8
7
  };
@@ -137,11 +136,15 @@ function handleTouchmove(e, type, ref, propsRef, config, navigation) {
137
136
  ];
138
137
  const currentTouchEvent = type === 'bubble' ? bubbleTouchEvent : captureTouchEvent;
139
138
  handleEmitEvent(currentTouchEvent, 'touchmove', e, propsRef, config, navigation);
140
- checkIsNeedPress(e, type, ref);
139
+ if (TAP_EVENTS.some(eventName => propsRef.current[eventName])) {
140
+ checkIsNeedPress(e, type, ref);
141
+ }
141
142
  }
142
143
  function handleTouchend(e, type, ref, propsRef, config, navigation) {
143
144
  // move event may not be triggered
144
- checkIsNeedPress(e, type, ref);
145
+ if (TAP_EVENTS.some(eventName => propsRef.current[eventName])) {
146
+ checkIsNeedPress(e, type, ref);
147
+ }
145
148
  const bubbleTouchEvent = ['catchtouchend', 'bindtouchend'];
146
149
  const bubbleTapEvent = ['catchtap', 'bindtap'];
147
150
  const captureTouchEvent = [
@@ -130,7 +130,7 @@ const Loading = ({ alone = false }) => {
130
130
  const Button = forwardRef((buttonProps, ref) => {
131
131
  const { textProps, innerProps: props = {} } = splitProps(buttonProps);
132
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;
133
- const pageId = useContext(RouteContext);
133
+ const { pageId } = useContext(RouteContext) || {};
134
134
  const formContext = useContext(FormContext);
135
135
  const enableHover = hoverClass !== 'none';
136
136
  const { isHover, gesture } = useHover({ enableHover, hoverStartTime, hoverStayTime, disabled });
@@ -0,0 +1,50 @@
1
+ /**
2
+ * ✔ type
3
+ * ✔ size
4
+ * ✔ color
5
+ */
6
+ import { forwardRef, useRef, createElement } from 'react';
7
+ import { Image } from 'react-native';
8
+ import useInnerProps from '../getInnerListeners';
9
+ import useNodesRef 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
+ const IconTypeMap = new Map([
21
+ ['success', Success],
22
+ ['success_no_circle', SuccessNoCircle],
23
+ ['info', Info],
24
+ ['warn', Warn],
25
+ ['waiting', Waiting],
26
+ ['cancel', Cancel],
27
+ ['download', Download],
28
+ ['search', Search],
29
+ ['clear', Clear]
30
+ ]);
31
+ const Icon = forwardRef((props, ref) => {
32
+ const { type, size = 23, color, style = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
33
+ const source = IconTypeMap.get(type);
34
+ const defaultStyle = { width: ~~size, height: ~~size };
35
+ const styleObj = extendObject({}, defaultStyle, style);
36
+ const { hasSelfPercent, normalStyle, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
37
+ const nodeRef = useRef(null);
38
+ useNodesRef(props, ref, nodeRef, { style: normalStyle });
39
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef });
40
+ const innerProps = useInnerProps(props, extendObject({
41
+ ref: nodeRef,
42
+ source,
43
+ style: extendObject({}, normalStyle, layoutStyle, { tintColor: color })
44
+ }, layoutProps), [], {
45
+ layoutRef
46
+ });
47
+ return createElement(Image, innerProps);
48
+ });
49
+ Icon.displayName = 'MpxIcon';
50
+ export default Icon;