@mpxjs/webpack-plugin 2.10.2 → 2.10.3-beta.10

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 (89) hide show
  1. package/lib/config.js +2 -1
  2. package/lib/dependencies/RecordPageConfigsMapDependency.js +45 -0
  3. package/lib/index.js +84 -52
  4. package/lib/platform/json/wx/index.js +6 -3
  5. package/lib/platform/style/wx/index.js +29 -16
  6. package/lib/platform/template/wx/component-config/button.js +19 -2
  7. package/lib/platform/template/wx/component-config/canvas.js +4 -0
  8. package/lib/platform/template/wx/component-config/checkbox-group.js +4 -0
  9. package/lib/platform/template/wx/component-config/checkbox.js +4 -0
  10. package/lib/platform/template/wx/component-config/cover-image.js +7 -1
  11. package/lib/platform/template/wx/component-config/cover-view.js +4 -0
  12. package/lib/platform/template/wx/component-config/fix-component-name.js +3 -2
  13. package/lib/platform/template/wx/component-config/form.js +7 -1
  14. package/lib/platform/template/wx/component-config/icon.js +4 -0
  15. package/lib/platform/template/wx/component-config/image.js +7 -1
  16. package/lib/platform/template/wx/component-config/input.js +17 -2
  17. package/lib/platform/template/wx/component-config/label.js +4 -0
  18. package/lib/platform/template/wx/component-config/movable-area.js +7 -1
  19. package/lib/platform/template/wx/component-config/movable-view.js +12 -3
  20. package/lib/platform/template/wx/component-config/navigator.js +4 -0
  21. package/lib/platform/template/wx/component-config/picker-view-column.js +4 -0
  22. package/lib/platform/template/wx/component-config/picker-view.js +7 -1
  23. package/lib/platform/template/wx/component-config/picker.js +7 -1
  24. package/lib/platform/template/wx/component-config/radio-group.js +4 -0
  25. package/lib/platform/template/wx/component-config/radio.js +4 -0
  26. package/lib/platform/template/wx/component-config/rich-text.js +4 -0
  27. package/lib/platform/template/wx/component-config/root-portal.js +4 -0
  28. package/lib/platform/template/wx/component-config/scroll-view.js +10 -2
  29. package/lib/platform/template/wx/component-config/swiper-item.js +7 -1
  30. package/lib/platform/template/wx/component-config/swiper.js +12 -3
  31. package/lib/platform/template/wx/component-config/switch.js +4 -0
  32. package/lib/platform/template/wx/component-config/text.js +7 -1
  33. package/lib/platform/template/wx/component-config/textarea.js +17 -2
  34. package/lib/platform/template/wx/component-config/unsupported.js +7 -0
  35. package/lib/platform/template/wx/component-config/video.js +10 -2
  36. package/lib/platform/template/wx/component-config/view.js +24 -2
  37. package/lib/platform/template/wx/component-config/web-view.js +4 -0
  38. package/lib/platform/template/wx/index.js +32 -13
  39. package/lib/react/index.js +0 -1
  40. package/lib/react/processJSON.js +13 -2
  41. package/lib/react/processScript.js +5 -3
  42. package/lib/react/processTemplate.js +18 -3
  43. package/lib/react/script-helper.js +18 -4
  44. package/lib/runtime/components/react/dist/mpx-input.jsx +15 -18
  45. package/lib/runtime/components/react/dist/{KeyboardAvoidingView.jsx → mpx-keyboard-avoiding-view.jsx} +25 -31
  46. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +2 -2
  47. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +1 -2
  48. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +10 -5
  49. package/lib/runtime/components/react/dist/mpx-simple-view.jsx +22 -0
  50. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +2 -2
  51. package/lib/runtime/components/react/dist/mpx-swiper.jsx +2 -2
  52. package/lib/runtime/components/react/dist/mpx-view.jsx +10 -5
  53. package/lib/runtime/components/react/dist/mpx-web-view.jsx +10 -7
  54. package/lib/runtime/components/react/dist/useAnimationHooks.js +46 -48
  55. package/lib/runtime/components/react/dist/utils.jsx +18 -7
  56. package/lib/runtime/components/react/mpx-input.tsx +17 -26
  57. package/lib/runtime/components/react/{KeyboardAvoidingView.tsx → mpx-keyboard-avoiding-view.tsx} +32 -41
  58. package/lib/runtime/components/react/mpx-picker-view-column.tsx +2 -2
  59. package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +1 -2
  60. package/lib/runtime/components/react/mpx-scroll-view.tsx +13 -4
  61. package/lib/runtime/components/react/mpx-simple-view.tsx +32 -0
  62. package/lib/runtime/components/react/mpx-swiper-item.tsx +2 -2
  63. package/lib/runtime/components/react/mpx-swiper.tsx +4 -2
  64. package/lib/runtime/components/react/mpx-view.tsx +17 -10
  65. package/lib/runtime/components/react/mpx-web-view.tsx +15 -12
  66. package/lib/runtime/components/react/types/getInnerListeners.d.ts +1 -1
  67. package/lib/runtime/components/react/types/global.d.ts +1 -1
  68. package/lib/runtime/components/react/useAnimationHooks.ts +46 -48
  69. package/lib/runtime/components/react/utils.tsx +23 -11
  70. package/lib/runtime/components/web/mini-video-controls.min.js +1 -1
  71. package/lib/runtime/components/web/mpx-titlebar.vue +243 -0
  72. package/lib/runtime/optionProcessor.js +3 -2
  73. package/lib/style-compiler/index.js +8 -6
  74. package/lib/template-compiler/compiler.js +29 -14
  75. package/lib/utils/env.js +1 -1
  76. package/lib/utils/match-condition.js +14 -8
  77. package/lib/web/processJSON.js +1 -3
  78. package/lib/web/processMainScript.js +3 -1
  79. package/package.json +3 -3
  80. package/LICENSE +0 -433
  81. package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
  82. package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
  83. package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
  84. package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
  85. package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
  86. package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
  87. package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
  88. package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
  89. package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
@@ -9,7 +9,7 @@ const { dash2hump } = require('../../../utils/hump-dash')
9
9
 
10
10
  module.exports = function getSpec ({ warn, error }) {
11
11
  const spec = {
12
- supportedModes: ['ali', 'swan', 'qq', 'tt', 'web', 'qa', 'jd', 'dd', 'ios', 'android'],
12
+ supportedModes: ['ali', 'swan', 'qq', 'tt', 'web', 'qa', 'jd', 'dd', 'ios', 'android', 'harmony'],
13
13
  // props预处理
14
14
  preProps: [],
15
15
  // props后处理
@@ -347,18 +347,6 @@ module.exports = function getSpec ({ warn, error }) {
347
347
  value
348
348
  }
349
349
  },
350
- // tt ({ name, value }, { eventRules }) {
351
- // const match = this.test.exec(name)
352
- // const prefix = match[1]
353
- // const eventName = match[2]
354
- // const modifierStr = match[3] || ''
355
- // const rEventName = runRules(eventRules, eventName, { mode: 'tt' })
356
- // return {
357
- // // 字节将所有事件转为小写
358
- // name: prefix + rEventName.toLowerCase() + modifierStr,
359
- // value
360
- // }
361
- // },
362
350
  tt ({ name, value }, { eventRules }) {
363
351
  const match = this.test.exec(name)
364
352
  const prefix = match[1]
@@ -430,6 +418,21 @@ module.exports = function getSpec ({ warn, error }) {
430
418
  name: rPrefix + rEventName + meta.modifierStr,
431
419
  value
432
420
  }
421
+ },
422
+ harmony ({ name, value }, { eventRules, el }) {
423
+ const match = this.test.exec(name)
424
+ const prefix = match[1]
425
+ const eventName = match[2]
426
+ const modifierStr = match[3] || ''
427
+ const meta = {
428
+ modifierStr
429
+ }
430
+ const rPrefix = runRules(spec.event.prefix, prefix, { mode: 'harmony' })
431
+ const rEventName = runRules(eventRules, eventName, { mode: 'harmony', data: { el } })
432
+ return {
433
+ name: rPrefix + rEventName + meta.modifierStr,
434
+ value
435
+ }
433
436
  }
434
437
  },
435
438
  // 无障碍
@@ -565,6 +568,22 @@ module.exports = function getSpec ({ warn, error }) {
565
568
  } else {
566
569
  error(`React native environment does not support [${eventName}] event!`)
567
570
  }
571
+ },
572
+ harmony (eventName) {
573
+ const eventMap = {
574
+ tap: 'tap',
575
+ longtap: 'longpress',
576
+ longpress: 'longpress',
577
+ touchstart: 'touchstart',
578
+ touchmove: 'touchmove',
579
+ touchend: 'touchend',
580
+ touchcancel: 'touchcancel'
581
+ }
582
+ if (eventMap[eventName]) {
583
+ return eventMap[eventName]
584
+ } else {
585
+ error(`React native environment does not support [${eventName}] event!`)
586
+ }
568
587
  }
569
588
  },
570
589
  // web event escape
@@ -85,7 +85,6 @@ module.exports = function ({
85
85
  srcMode,
86
86
  moduleId,
87
87
  isProduction,
88
- componentGenerics,
89
88
  jsonConfig: jsonRes.jsonObj,
90
89
  outputPath: queryObj.outputPath || '',
91
90
  builtInComponentsMap: templateRes.builtInComponentsMap,
@@ -12,6 +12,7 @@ const createJSONHelper = require('../json-compiler/helper')
12
12
  const getRulesRunner = require('../platform/index')
13
13
  const { RESOLVE_IGNORED_ERR } = require('../utils/const')
14
14
  const RecordResourceMapDependency = require('../dependencies/RecordResourceMapDependency')
15
+ const RecordPageConfigsMapDependency = require('../dependencies/RecordPageConfigsMapDependency')
15
16
 
16
17
  module.exports = function (jsonContent, {
17
18
  loaderContext,
@@ -79,7 +80,6 @@ module.exports = function (jsonContent, {
79
80
  })
80
81
  }
81
82
 
82
- const isApp = ctorType === 'app'
83
83
  if (!jsonContent) {
84
84
  return callback()
85
85
  }
@@ -99,7 +99,7 @@ module.exports = function (jsonContent, {
99
99
  }
100
100
  }
101
101
 
102
- if (!isApp) {
102
+ if (ctorType !== 'app') {
103
103
  rulesRunnerOptions.mainKey = ctorType
104
104
  }
105
105
 
@@ -112,6 +112,17 @@ module.exports = function (jsonContent, {
112
112
  return callback(e)
113
113
  }
114
114
 
115
+ if (ctorType === 'page') {
116
+ const keysToExtract = ['navigationStyle']
117
+ const configObj = {}
118
+ keysToExtract.forEach(key => {
119
+ if (jsonObj[key]) {
120
+ configObj[key] = jsonObj[key]
121
+ }
122
+ })
123
+ loaderContext._module.addPresentationalDependency(new RecordPageConfigsMapDependency(parseRequest(loaderContext.resource).resourcePath, configObj))
124
+ }
125
+
115
126
  const fs = loaderContext._compiler.inputFileSystem
116
127
 
117
128
  const defaultTabbar = {
@@ -12,7 +12,9 @@ module.exports = function (script, {
12
12
  outputPath,
13
13
  builtInComponentsMap,
14
14
  localComponentsMap,
15
- localPagesMap
15
+ localPagesMap,
16
+ componentGenerics,
17
+ genericsInfo
16
18
  }, callback) {
17
19
  let scriptSrcMode = srcMode
18
20
  const mode = loaderContext.getMpx().mode
@@ -27,7 +29,7 @@ module.exports = function (script, {
27
29
  output += `
28
30
  import { getComponent } from ${stringifyRequest(loaderContext, optionProcessorPath)}
29
31
  import { NavigationContainer, StackActions } from '@react-navigation/native'
30
- ${mode === 'ios' ? "import { createNativeStackNavigator as createStackNavigator } from '@react-navigation/native-stack'" : "import { createStackNavigator } from '@react-navigation/stack'"}
32
+ ${mode === 'ios' || mode === 'harmony' ? "import { createNativeStackNavigator as createStackNavigator } from '@react-navigation/native-stack'" : "import { createStackNavigator } from '@react-navigation/stack'"}
31
33
  import PortalHost from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/portal-host'
32
34
  import { useHeaderHeight } from '@react-navigation/elements';
33
35
  import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
@@ -68,7 +70,7 @@ global.__navigationHelper = {
68
70
  jsonConfig
69
71
  })
70
72
 
71
- output += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, ctorType, jsonConfig, componentsMap, outputPath })
73
+ output += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, ctorType, jsonConfig, componentsMap, outputPath, genericsInfo, componentGenerics })
72
74
  output += getRequireScript({ ctorType, script, loaderContext })
73
75
  output += `export default global.__mpxOptionsMap[${JSON.stringify(moduleId)}]\n`
74
76
  }
@@ -5,6 +5,8 @@ const loaderUtils = require('loader-utils')
5
5
  const templateCompiler = require('../template-compiler/compiler')
6
6
  const genNode = require('../template-compiler/gen-node-react')
7
7
  const bindThis = require('../template-compiler/bind-this')
8
+ const isEmptyObject = require('../utils/is-empty-object')
9
+ const dash2hump = require('../utils/hump-dash').dash2hump
8
10
 
9
11
  module.exports = function (template, {
10
12
  loaderContext,
@@ -75,16 +77,15 @@ module.exports = function (template, {
75
77
  defs,
76
78
  decodeHTMLText,
77
79
  externalClasses,
78
- // todo 后续输出web也采用mpx的scoped处理
79
80
  hasScoped: false,
80
81
  moduleId,
81
82
  filePath: rawResourcePath,
82
83
  // react中模版i18n不需要特殊处理
83
84
  i18n: null,
84
85
  checkUsingComponents,
85
- // web模式下全局组件不会被合入usingComponents中,故globalComponents可以传空
86
+ // rn模式下全局组件不会被合入usingComponents中,故globalComponents可以传空
86
87
  globalComponents: [],
87
- // web模式下实现抽象组件
88
+ // rn模式下实现抽象组件
88
89
  componentGenerics,
89
90
  hasVirtualHost: matchCondition(resourcePath, autoVirtualHostRules),
90
91
  forceProxyEvent: matchCondition(resourcePath, forceProxyEventRules),
@@ -148,6 +149,20 @@ ${e.stack}`)
148
149
  if (meta.options) {
149
150
  output += `global.currentInject.injectOptions = ${JSON.stringify(meta.options)};\n`
150
151
  }
152
+ if (!isEmptyObject(componentGenerics)) {
153
+ output += 'global.currentInject.injectProperties = {\n'
154
+ output += ' generichash: String,\n'
155
+
156
+ Object.keys(componentGenerics).forEach(genericName => {
157
+ const defaultValue = componentGenerics[genericName].default
158
+ if (defaultValue) {
159
+ output += ` generic${dash2hump(genericName)}: { type: String, value: '${genericName}default' },\n`
160
+ } else {
161
+ output += ` generic${dash2hump(genericName)}: String,\n`
162
+ }
163
+ })
164
+ output += '}\n'
165
+ }
151
166
  }
152
167
  }
153
168
 
@@ -89,7 +89,8 @@ function buildGlobalParams ({
89
89
  componentsMap,
90
90
  pagesMap,
91
91
  firstPage,
92
- outputPath
92
+ outputPath,
93
+ genericsInfo
93
94
  }) {
94
95
  let content = ''
95
96
  if (ctorType === 'app') {
@@ -115,9 +116,22 @@ global.currentInject.firstPage = ${JSON.stringify(firstPage)}\n`
115
116
  delete pageConfig.usingComponents
116
117
  content += `global.currentInject.pageConfig = ${JSON.stringify(pageConfig)}\n`
117
118
  }
118
- content += `global.currentInject.getComponents = function () {
119
- return ${shallowStringify(componentsMap)}
120
- }\n`
119
+
120
+ content += `
121
+
122
+ function getComponents() {
123
+ return ${shallowStringify(componentsMap)}
124
+ }
125
+
126
+ global.currentInject.getComponents = getComponents\n`
127
+ if (genericsInfo) {
128
+ content += `
129
+ const genericHash = ${JSON.stringify(genericsInfo.hash)}\n
130
+ global.__mpxGenericsMap[genericHash] = function (name) {
131
+ return getComponents()[name]
132
+ }
133
+ \n`
134
+ }
121
135
  if (ctorType === 'component') {
122
136
  content += `global.currentInject.componentPath = '/' + ${JSON.stringify(outputPath)}\n`
123
137
  }
@@ -82,7 +82,7 @@ const Input = forwardRef((props, ref) => {
82
82
  const lineCount = useRef(0);
83
83
  const [inputValue, setInputValue] = useState(defaultValue);
84
84
  const [contentHeight, setContentHeight] = useState(0);
85
- const [selection, setSelection] = useState({ start: -1, end: -1 });
85
+ const [selection, setSelection] = useState({ start: -1, end: tmpValue.current.length });
86
86
  const styleObj = extendObject({ padding: 0, backgroundColor: '#fff' }, style, multiline && autoHeight
87
87
  ? { height: 'auto', minHeight: Math.max(style?.minHeight || 35, contentHeight) }
88
88
  : {});
@@ -94,15 +94,17 @@ const Input = forwardRef((props, ref) => {
94
94
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef });
95
95
  useEffect(() => {
96
96
  if (inputValue !== value) {
97
- setInputValue(parseValue(value));
97
+ const parsed = parseValue(value);
98
+ tmpValue.current = parsed;
99
+ setInputValue(parsed);
98
100
  }
99
101
  }, [value]);
100
102
  useEffect(() => {
101
- if (typeof cursor === 'number') {
102
- setSelection({ start: cursor, end: cursor });
103
+ if (selectionStart > -1) {
104
+ setSelection({ start: selectionStart, end: selectionEnd === -1 ? tmpValue.current.length : selectionEnd });
103
105
  }
104
- else if (selectionStart >= 0 && selectionEnd >= 0 && selectionStart !== selectionEnd) {
105
- setSelection({ start: selectionStart, end: selectionEnd });
106
+ else if (typeof cursor === 'number') {
107
+ setSelection({ start: cursor, end: cursor });
106
108
  }
107
109
  }, [cursor, selectionStart, selectionEnd]);
108
110
  // have not selection on the Android platformg
@@ -152,6 +154,9 @@ const Input = forwardRef((props, ref) => {
152
154
  // sometimes the focus event occurs later than the keyboardWillShow event
153
155
  setKeyboardAvoidContext();
154
156
  };
157
+ const onTouchEnd = (evt) => {
158
+ evt.nativeEvent.origin = 'input';
159
+ };
155
160
  const onFocus = (evt) => {
156
161
  setKeyboardAvoidContext();
157
162
  bindfocus && bindfocus(getCustomEvent('focus', evt, {
@@ -170,15 +175,6 @@ const Input = forwardRef((props, ref) => {
170
175
  layoutRef
171
176
  }, props));
172
177
  };
173
- const onKeyPress = (evt) => {
174
- evt.nativeEvent.key === 'Enter' &&
175
- bindconfirm(getCustomEvent('confirm', evt, {
176
- detail: {
177
- value: tmpValue.current || ''
178
- },
179
- layoutRef
180
- }, props));
181
- };
182
178
  const onSubmitEditing = (evt) => {
183
179
  bindconfirm(getCustomEvent('confirm', evt, {
184
180
  detail: {
@@ -220,6 +216,7 @@ const Input = forwardRef((props, ref) => {
220
216
  }
221
217
  };
222
218
  const resetValue = () => {
219
+ tmpValue.current = '';
223
220
  setInputValue('');
224
221
  };
225
222
  const getValue = () => {
@@ -264,7 +261,7 @@ const Input = forwardRef((props, ref) => {
264
261
  maxLength: maxlength === -1 ? undefined : maxlength,
265
262
  editable: !disabled,
266
263
  autoFocus: !!autoFocus || !!focus,
267
- selection: selection,
264
+ selection: selectionStart > -1 || typeof cursor === 'number' ? selection : undefined,
268
265
  selectionColor: cursorColor,
269
266
  blurOnSubmit: !multiline && !confirmHold,
270
267
  underlineColorAndroid: 'rgba(0,0,0,0)',
@@ -273,13 +270,13 @@ const Input = forwardRef((props, ref) => {
273
270
  multiline: !!multiline
274
271
  }, !!multiline && confirmType === 'return' ? {} : { enterKeyHint: confirmType }, layoutProps, {
275
272
  onTouchStart,
273
+ onTouchEnd,
276
274
  onFocus,
277
275
  onBlur,
278
276
  onChange,
279
277
  onSelectionChange,
280
278
  onContentSizeChange,
281
- onKeyPress: bindconfirm && onKeyPress,
282
- onSubmitEditing: bindconfirm && multiline && onSubmitEditing
279
+ onSubmitEditing: bindconfirm && !multiline && onSubmitEditing
283
280
  }), [
284
281
  'type',
285
282
  'password',
@@ -1,7 +1,6 @@
1
- import React, { useContext, useEffect, useMemo } from 'react';
2
- import { Keyboard, Platform, View } from 'react-native';
3
- import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing, runOnJS } from 'react-native-reanimated';
4
- import { GestureDetector, Gesture } from 'react-native-gesture-handler';
1
+ import React, { useContext, useEffect } from 'react';
2
+ import { Keyboard, View, Platform } from 'react-native';
3
+ import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
5
4
  import { KeyboardAvoidContext } from './context';
6
5
  const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
7
6
  const isIOS = Platform.OS === 'ios';
@@ -10,20 +9,10 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
10
9
  const offset = useSharedValue(0);
11
10
  const basic = useSharedValue('auto');
12
11
  const keyboardAvoid = useContext(KeyboardAvoidContext);
13
- const dismiss = () => {
14
- Keyboard.isVisible() && Keyboard.dismiss();
15
- };
16
- const gesture = useMemo(() => {
17
- return Gesture.Tap()
18
- .onEnd(() => {
19
- runOnJS(dismiss)();
20
- });
21
- }, []);
22
- const animatedStyle = useAnimatedStyle(() => {
23
- return Object.assign({
24
- transform: [{ translateY: -offset.value }]
25
- }, isIOS ? {} : { flexBasis: basic.value });
26
- });
12
+ const animatedStyle = useAnimatedStyle(() => ({
13
+ transform: [{ translateY: -offset.value }],
14
+ flexBasis: basic.value
15
+ }));
27
16
  const resetKeyboard = () => {
28
17
  if (keyboardAvoid?.current) {
29
18
  keyboardAvoid.current = null;
@@ -31,6 +20,11 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
31
20
  offset.value = withTiming(0, { duration, easing });
32
21
  basic.value = 'auto';
33
22
  };
23
+ const onTouchEnd = ({ nativeEvent }) => {
24
+ if (nativeEvent.origin !== 'input') {
25
+ Keyboard.isVisible() && Keyboard.dismiss();
26
+ }
27
+ };
34
28
  useEffect(() => {
35
29
  let subscriptions = [];
36
30
  if (isIOS) {
@@ -46,7 +40,12 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
46
40
  const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
47
41
  const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing);
48
42
  const value = aboveOffset > 0 ? belowValue : aboveValue;
49
- offset.value = withTiming(value, { duration, easing });
43
+ offset.value = withTiming(value, { duration, easing }, (finished) => {
44
+ if (finished) {
45
+ // Set flexBasic after animation to trigger re-layout and reset layout information
46
+ basic.value = '99.99%';
47
+ }
48
+ });
50
49
  });
51
50
  });
52
51
  }),
@@ -68,11 +67,7 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
68
67
  const value = aboveOffset > 0 ? belowValue : aboveValue;
69
68
  offset.value = withTiming(value, { duration, easing }, (finished) => {
70
69
  if (finished) {
71
- /**
72
- * In the Android environment, the layout information is not synchronized after the animation,
73
- * which results in the inability to correctly trigger element events.
74
- * Here, we utilize flexBasic to proactively trigger a re-layout
75
- */
70
+ // Set flexBasic after animation to trigger re-layout and reset layout information
76
71
  basic.value = '99.99%';
77
72
  }
78
73
  });
@@ -85,15 +80,14 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
85
80
  subscriptions.forEach(subscription => subscription.remove());
86
81
  };
87
82
  }, [keyboardAvoid]);
88
- return (<GestureDetector gesture={gesture}>
89
- <View style={style}>
90
- <Animated.View style={[
83
+ return (<View style={style} onTouchEnd={onTouchEnd}>
84
+ <Animated.View style={[
91
85
  contentContainerStyle,
92
86
  animatedStyle
93
87
  ]}>
94
- {children}
95
- </Animated.View>
96
- </View>
97
- </GestureDetector>);
88
+ {children}
89
+ </Animated.View>
90
+ </View>);
98
91
  };
92
+ KeyboardAvoidingView.displayName = 'MpxKeyboardAvoidingView';
99
93
  export default KeyboardAvoidingView;
@@ -1,7 +1,7 @@
1
1
  import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react';
2
2
  import { StyleSheet, View } from 'react-native';
3
3
  import Reanimated, { useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated';
4
- import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS } from './utils';
4
+ import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, isHarmony } from './utils';
5
5
  import useNodesRef from './useNodesRef';
6
6
  import PickerIndicator from './pickerViewIndicator';
7
7
  import PickerMask from './pickerViewMask';
@@ -81,7 +81,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
81
81
  y: initialIndex * itemRawH,
82
82
  animated: false
83
83
  });
84
- }, isAndroid ? 200 : 0);
84
+ }, isAndroid || isHarmony ? 200 : 0);
85
85
  activeIndex.current = initialIndex;
86
86
  }, [itemRawH, maxIndex, initialIndex]);
87
87
  const onContentSizeChange = useCallback((_w, h) => {
@@ -1,6 +1,5 @@
1
1
  import { useState, useCallback, forwardRef, useImperativeHandle } from 'react';
2
2
  import { View, StyleSheet } from 'react-native';
3
- import { extendObject } from '../utils';
4
3
  const _PortalManager = forwardRef((props, ref) => {
5
4
  const [state, setState] = useState({
6
5
  portals: []
@@ -14,7 +13,7 @@ const _PortalManager = forwardRef((props, ref) => {
14
13
  setState((prevState) => ({
15
14
  portals: prevState.portals.map((item) => {
16
15
  if (item.key === key) {
17
- return extendObject({}, item, { children });
16
+ return Object.assign({}, item, { children });
18
17
  }
19
18
  return item;
20
19
  })
@@ -41,7 +41,7 @@ import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, ext
41
41
  import { IntersectionObserverContext, ScrollViewContext } from './context';
42
42
  const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
43
43
  const { textProps, innerProps: props = {} } = splitProps(scrollViewProps);
44
- const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, __selectRef } = props;
44
+ const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, 'scroll-event-throttle': scrollEventThrottle = 0, __selectRef } = props;
45
45
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
46
46
  const waitForHandlers = flatGesture(waitFor);
47
47
  const [refreshing, setRefreshing] = useState(true);
@@ -54,7 +54,6 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
54
54
  scrollTop: 0,
55
55
  visibleLength: 0
56
56
  });
57
- const scrollEventThrottle = 50;
58
57
  const hasCallScrollToUpper = useRef(true);
59
58
  const hasCallScrollToLower = useRef(false);
60
59
  const initialTimeout = useRef(null);
@@ -83,6 +82,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
83
82
  };
84
83
  }, []);
85
84
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
85
+ const lastOffset = useRef(0);
86
86
  if (scrollX && scrollY) {
87
87
  warn('scroll-x and scroll-y cannot be set to true at the same time, Mpx will use the value of scroll-y as the criterion');
88
88
  }
@@ -133,7 +133,8 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
133
133
  function onStartReached(e) {
134
134
  const { bindscrolltoupper } = props;
135
135
  const { offset } = scrollOptions.current;
136
- if (bindscrolltoupper && (offset <= upperThreshold)) {
136
+ const isScrollingBackward = offset < lastOffset.current;
137
+ if (bindscrolltoupper && (offset <= upperThreshold) && isScrollingBackward) {
137
138
  if (!hasCallScrollToUpper.current) {
138
139
  bindscrolltoupper(getCustomEvent('scrolltoupper', e, {
139
140
  detail: {
@@ -152,12 +153,13 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
152
153
  const { bindscrolltolower } = props;
153
154
  const { contentLength, visibleLength, offset } = scrollOptions.current;
154
155
  const distanceFromEnd = contentLength - visibleLength - offset;
155
- if (bindscrolltolower && (distanceFromEnd < lowerThreshold)) {
156
+ const isScrollingForward = offset > lastOffset.current;
157
+ if (bindscrolltolower && (distanceFromEnd < lowerThreshold) && isScrollingForward) {
156
158
  if (!hasCallScrollToLower.current) {
157
159
  hasCallScrollToLower.current = true;
158
160
  bindscrolltolower(getCustomEvent('scrolltolower', e, {
159
161
  detail: {
160
- direction: scrollX ? 'right' : 'botttom'
162
+ direction: scrollX ? 'right' : 'bottom'
161
163
  },
162
164
  layoutRef
163
165
  }, props));
@@ -206,6 +208,8 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
206
208
  onStartReached(e);
207
209
  onEndReached(e);
208
210
  updateIntersection();
211
+ // 在 onStartReached、onEndReached 执行完后更新 lastOffset
212
+ lastOffset.current = scrollOptions.current.offset;
209
213
  }
210
214
  function onScrollEnd(e) {
211
215
  const { bindscrollend } = props;
@@ -225,6 +229,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
225
229
  onStartReached(e);
226
230
  onEndReached(e);
227
231
  updateIntersection();
232
+ lastOffset.current = scrollOptions.current.offset;
228
233
  }
229
234
  function updateIntersection() {
230
235
  if (enableTriggerIntersectionObserver && intersectionObservers) {
@@ -0,0 +1,22 @@
1
+ import { View } from 'react-native';
2
+ import { createElement, forwardRef, useRef } from 'react';
3
+ import useNodesRef from './useNodesRef';
4
+ import { extendObject, splitProps, splitStyle, wrapChildren } from './utils';
5
+ const _View2 = forwardRef((simpleViewProps, ref) => {
6
+ const nodeRef = useRef(null);
7
+ const { textProps, innerProps: props = {} } = splitProps(simpleViewProps);
8
+ const { textStyle, innerStyle = {} } = splitStyle(props.style || {});
9
+ useNodesRef(props, ref, nodeRef, {
10
+ style: innerStyle || {}
11
+ });
12
+ return createElement(View, extendObject({}, props, {
13
+ style: innerStyle,
14
+ ref: nodeRef
15
+ }), wrapChildren(props, {
16
+ hasVarDec: false,
17
+ textStyle: textStyle,
18
+ textProps
19
+ }));
20
+ });
21
+ _View2.displayName = 'MpxSimpleView';
22
+ export default _View2;
@@ -2,7 +2,7 @@ import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated
2
2
  import { forwardRef, useRef, useContext } from 'react';
3
3
  import useInnerProps from './getInnerListeners';
4
4
  import useNodesRef from './useNodesRef'; // 引入辅助函数
5
- import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout } from './utils';
5
+ import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout, isHarmony } from './utils';
6
6
  import { SwiperContext } from './context';
7
7
  const _SwiperItem = forwardRef((props, ref) => {
8
8
  const { 'enable-var': enableVar, 'external-var-context': externalVarContext, style, customStyle, itemIndex } = props;
@@ -30,7 +30,7 @@ const _SwiperItem = forwardRef((props, ref) => {
30
30
  'style'
31
31
  ], { layoutRef });
32
32
  const itemAnimatedStyle = useAnimatedStyle(() => {
33
- if (!step.value)
33
+ if (!step.value && !isHarmony)
34
34
  return {};
35
35
  const inputRange = [step.value, 0];
36
36
  const outputRange = [0.7, 1];
@@ -70,7 +70,7 @@ const easeMap = {
70
70
  easeInOutCubic: Easing.inOut(Easing.cubic)
71
71
  };
72
72
  const SwiperWrapper = forwardRef((props, ref) => {
73
- const { 'indicator-dots': showsPagination, 'indicator-color': dotColor = 'rgba(0, 0, 0, .3)', 'indicator-active-color': activeDotColor = '#000000', 'enable-var': enableVar = false, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'external-var-context': externalVarContext, style = {}, autoplay = false, circular = false } = props;
73
+ const { 'indicator-dots': showsPagination, 'indicator-color': dotColor = 'rgba(0, 0, 0, .3)', 'indicator-active-color': activeDotColor = '#000000', 'enable-var': enableVar = false, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'external-var-context': externalVarContext, style = {}, autoplay = false, circular = false, disableGesture = false } = props;
74
74
  const easeingFunc = props['easing-function'] || 'default';
75
75
  const easeDuration = props.duration || 500;
76
76
  const horizontal = props.vertical !== undefined ? !props.vertical : true;
@@ -658,7 +658,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
658
658
  {showsPagination && renderPagination()}
659
659
  </View>);
660
660
  }
661
- if (children.length === 1) {
661
+ if (children.length === 1 || disableGesture) {
662
662
  return renderSwiper();
663
663
  }
664
664
  else {
@@ -14,6 +14,7 @@ import { parseUrl, PERCENT_REGEX, splitStyle, splitProps, useTransformStyle, wra
14
14
  import { error } from '@mpxjs/utils';
15
15
  import LinearGradient from 'react-native-linear-gradient';
16
16
  import { GestureDetector } from 'react-native-gesture-handler';
17
+ import Portal from './mpx-portal';
17
18
  const linearMap = new Map([
18
19
  ['top', 0],
19
20
  ['bottom', 180],
@@ -555,7 +556,7 @@ const _View = forwardRef((viewProps, ref) => {
555
556
  const enableHover = !!hoverStyle;
556
557
  const { isHover, gesture } = useHover({ enableHover, hoverStartTime, hoverStayTime });
557
558
  const styleObj = extendObject({}, defaultStyle, style, isHover ? hoverStyle : {});
558
- const { normalStyle, hasSelfPercent, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, {
559
+ const { normalStyle, hasSelfPercent, hasPositionFixed, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, {
559
560
  enableVar,
560
561
  externalVarContext,
561
562
  parentFontSize,
@@ -600,12 +601,16 @@ const _View = forwardRef((viewProps, ref) => {
600
601
  innerStyle,
601
602
  enableFastImage
602
603
  });
603
- const BaseComponent = enableStyleAnimation
604
+ let finalComponent = enableStyleAnimation
604
605
  ? createElement(Animated.View, innerProps, childNode)
605
606
  : createElement(View, innerProps, childNode);
606
- return enableHover
607
- ? createElement(GestureDetector, { gesture: gesture }, BaseComponent)
608
- : BaseComponent;
607
+ if (enableHover) {
608
+ finalComponent = createElement(GestureDetector, { gesture: gesture }, finalComponent);
609
+ }
610
+ if (hasPositionFixed) {
611
+ finalComponent = createElement(Portal, null, finalComponent);
612
+ }
613
+ return finalComponent;
609
614
  });
610
615
  _View.displayName = 'MpxView';
611
616
  export default _View;