@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
@@ -45,7 +45,7 @@ const _WebView = forwardRef((props, ref) => {
45
45
  button: 'Reload'
46
46
  }
47
47
  };
48
- const currentErrorText = errorText[mpx.i18n.locale || 'zh-CN'];
48
+ const currentErrorText = errorText[mpx.i18n?.locale || 'zh-CN'];
49
49
  if (props.style) {
50
50
  warn('The web-view component does not support the style prop.');
51
51
  }
@@ -75,7 +75,10 @@ const _WebView = forwardRef((props, ref) => {
75
75
  };
76
76
  const navigation = useNavigation();
77
77
  useEffect(() => {
78
- const beforeRemoveSubscription = navigation?.addListener?.('beforeRemove', beforeRemoveHandle);
78
+ let beforeRemoveSubscription;
79
+ if (__mpx_mode__ !== 'ios') {
80
+ beforeRemoveSubscription = navigation?.addListener?.('beforeRemove', beforeRemoveHandle);
81
+ }
79
82
  return () => {
80
83
  if (isFunction(beforeRemoveSubscription)) {
81
84
  beforeRemoveSubscription();
@@ -89,7 +92,7 @@ const _WebView = forwardRef((props, ref) => {
89
92
  return null;
90
93
  }
91
94
  const _reload = function () {
92
- if (__mpx_mode__ === 'android') {
95
+ if (__mpx_mode__ === 'android' || __mpx_mode__ === 'harmony') {
93
96
  fristLoaded.current = false; // 安卓需要重新设置
94
97
  }
95
98
  setPageLoadErr(false);
@@ -133,7 +136,7 @@ const _WebView = forwardRef((props, ref) => {
133
136
  }
134
137
  };
135
138
  const _onLoadProgress = function (event) {
136
- if (__mpx_mode__ === 'android') {
139
+ if (__mpx_mode__ === 'android' || __mpx_mode__ === 'harmony') {
137
140
  canGoBack.current = event.nativeEvent.canGoBack;
138
141
  }
139
142
  };
@@ -251,8 +254,8 @@ const _WebView = forwardRef((props, ref) => {
251
254
  }
252
255
  };
253
256
  const onLoadEnd = function (res) {
254
- if (__mpx_mode__ === 'android') {
255
- res.persist();
257
+ res.persist();
258
+ if (__mpx_mode__ === 'android' || __mpx_mode__ === 'harmony') {
256
259
  setTimeout(() => {
257
260
  onLoadEndHandle(res);
258
261
  }, 0);
@@ -277,7 +280,7 @@ const _WebView = forwardRef((props, ref) => {
277
280
  setIsLoaded(false);
278
281
  }
279
282
  };
280
- return (<Portal key={pageLoadErr ? 'error' : 'webview'}>
283
+ return (<Portal>
281
284
  {pageLoadErr
282
285
  ? (<View style={[styles.loadErrorContext, defaultWebViewStyle]}>
283
286
  <View style={styles.loadErrorText}><Text style={{ fontSize: 14, color: '#999999' }}>{currentErrorText.text}</Text></View>
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useMemo, useRef } from 'react';
2
2
  import { Easing, useSharedValue, withTiming, useAnimatedStyle, withSequence, withDelay, makeMutable, cancelAnimation } from 'react-native-reanimated';
3
- import { error } from '@mpxjs/utils';
3
+ import { error, hasOwn } from '@mpxjs/utils';
4
4
  // 微信 timingFunction 和 RN Easing 对应关系
5
5
  const EasingKey = {
6
6
  linear: Easing.linear,
@@ -14,20 +14,20 @@ const EasingKey = {
14
14
  const TransformInitial = {
15
15
  // matrix: 0,
16
16
  // matrix3d: 0,
17
- rotate: '0deg',
17
+ // rotate: '0deg',
18
18
  rotateX: '0deg',
19
19
  rotateY: '0deg',
20
20
  rotateZ: '0deg',
21
21
  // rotate3d:[0,0,0]
22
- scale: 1,
22
+ // scale: 1,
23
23
  // scale3d: [1, 1, 1],
24
24
  scaleX: 1,
25
25
  scaleY: 1,
26
26
  // scaleZ: 1,
27
- skew: 0,
27
+ // skew: 0,
28
28
  skewX: '0deg',
29
29
  skewY: '0deg',
30
- translate: 0,
30
+ // translate: 0,
31
31
  // translate3d: 0,
32
32
  translateX: 0,
33
33
  translateY: 0
@@ -92,23 +92,23 @@ const parseTransform = (transformStr) => {
92
92
  case 'skewX':
93
93
  case 'skewY':
94
94
  case 'perspective':
95
+ // rotate 处理成 rotateZ
96
+ key = key === 'rotate' ? 'rotateZ' : key;
95
97
  // 单个值处理
96
98
  transform.push({ [key]: global.__formatValue(val) });
97
99
  break;
98
100
  case 'matrix':
99
- case 'matrix3d':
100
101
  transform.push({ [key]: parseValues(val, ',').map(val => +val) });
101
102
  break;
102
103
  case 'translate':
103
104
  case 'scale':
104
105
  case 'skew':
105
- case 'rotate3d': // x y z angle
106
106
  case 'translate3d': // x y 支持 z不支持
107
107
  case 'scale3d': // x y 支持 z不支持
108
108
  {
109
109
  // 2 个以上的值处理
110
110
  key = key.replace('3d', '');
111
- const vals = parseValues(val, ',').splice(0, key === 'rotate' ? 4 : 3);
111
+ const vals = parseValues(val, ',').splice(0, 3);
112
112
  // scale(.5) === scaleX(.5) scaleY(.5)
113
113
  if (vals.length === 1 && key === 'scale') {
114
114
  vals.push(vals[0]);
@@ -132,6 +132,13 @@ const formatStyle = (style) => {
132
132
  transform: parseTransform(style.transform)
133
133
  });
134
134
  };
135
+ // transform 数组转对象
136
+ function getTransformObj(transforms) {
137
+ 'worklet';
138
+ return transforms.reduce((transformObj, item) => {
139
+ return Object.assign(transformObj, item);
140
+ }, {});
141
+ }
135
142
  export default function useAnimationHooks(props) {
136
143
  const { style = {}, animation, enableAnimation } = props;
137
144
  const enableStyleAnimation = enableAnimation || !!animation;
@@ -150,7 +157,9 @@ export default function useAnimationHooks(props) {
150
157
  // 记录动画key的style样式值 没有的话设置为false
151
158
  // eslint-disable-next-line react-hooks/rules-of-hooks
152
159
  const animatedKeys = useRef({});
153
- // const animatedKeys = useRef({} as {[propName: keyof ExtendedViewStyle]: boolean|number|string})
160
+ // 记录上次style map
161
+ // eslint-disable-next-line react-hooks/rules-of-hooks
162
+ const lastStyleRef = useRef({});
154
163
  // ** 全量 style prop sharedValue
155
164
  // 不能做增量的原因:
156
165
  // 1 尝试用 useRef,但 useAnimatedStyle 访问后的 ref 不能在增加新的值,被冻结
@@ -163,6 +172,12 @@ export default function useAnimationHooks(props) {
163
172
  return valMap;
164
173
  }, {});
165
174
  }, []);
175
+ // ** style更新同步
176
+ // eslint-disable-next-line react-hooks/rules-of-hooks
177
+ useEffect(() => {
178
+ // style 更新后同步更新 lastStyleRef & shareValMap
179
+ updateStyleVal();
180
+ }, [style]);
166
181
  // ** 获取动画样式prop & 驱动动画
167
182
  // eslint-disable-next-line react-hooks/rules-of-hooks
168
183
  useEffect(() => {
@@ -175,16 +190,6 @@ export default function useAnimationHooks(props) {
175
190
  // 驱动动画
176
191
  createAnimation(keys);
177
192
  }, [id]);
178
- // 同步style更新
179
- // useEffect(() => {
180
- // Object.keys(animatedKeys.current).forEach(key => {
181
- // const originVal = getOriginalStyleVal(key, isTransform(key))
182
- // if (originVal && animatedKeys.current[key] !== originVal) {
183
- // animatedKeys.current[key] = originVal
184
- // shareValMap[key].value = originVal
185
- // }
186
- // })
187
- // }, [style])
188
193
  // ** 清空动画
189
194
  // eslint-disable-next-line react-hooks/rules-of-hooks
190
195
  useEffect(() => {
@@ -194,7 +199,7 @@ export default function useAnimationHooks(props) {
194
199
  });
195
200
  };
196
201
  }, []);
197
- // 根据 animation action 创建&驱动动画 key => wi
202
+ // 根据 animation action 创建&驱动动画
198
203
  function createAnimation(animatedKeys = []) {
199
204
  const actions = animation?.actions || [];
200
205
  const sequence = {};
@@ -251,6 +256,7 @@ export default function useAnimationHooks(props) {
251
256
  : withTiming(value, { duration, easing });
252
257
  return delay ? withDelay(delay, animation) : animation;
253
258
  }
259
+ // 获取样式初始值(prop style or 默认值)
254
260
  function getInitialVal(key, isTransform = false) {
255
261
  if (isTransform && Array.isArray(originalStyle.transform)) {
256
262
  let initialVal = InitialValue[key];
@@ -263,31 +269,12 @@ export default function useAnimationHooks(props) {
263
269
  }
264
270
  return originalStyle[key] === undefined ? InitialValue[key] : originalStyle[key];
265
271
  }
266
- // 从 prop style 中获取样式初始值 没有为undefined
267
- // function getOriginalStyleVal (key: keyof ExtendedViewStyle, isTransform = false) {
268
- // if (isTransform && Array.isArray(originalStyle.transform)) {
269
- // let initialVal = undefined // InitialValue[key]
270
- // // 仅支持 { transform: [{rotateX: '45deg'}, {rotateZ: '0.785398rad'}] } 格式的初始样式
271
- // originalStyle.transform.forEach(item => {
272
- // if (item[key] !== undefined) initialVal = item[key]
273
- // })
274
- // return initialVal
275
- // }
276
- // return originalStyle[key] // === undefined ? InitialValue[key] : originalStyle[key]
277
- // }
278
- // 获取动画shareVal初始值(prop style or 默认值)
279
- // function getInitialVal (key: keyof ExtendedViewStyle, isTransform = false) {
280
- // const originalVal = getOriginalStyleVal(key, isTransform)
281
- // return originalVal === undefined ? InitialValue[key] : originalStyle[key]
282
- // }
283
272
  // 循环 animation actions 获取所有有动画的 style prop name
284
273
  function getAnimatedStyleKeys() {
285
274
  return (animation?.actions || []).reduce((keyMap, action) => {
286
275
  const { rules, transform } = action;
287
276
  const ruleArr = [...rules.keys(), ...transform.keys()];
288
277
  ruleArr.forEach(key => {
289
- // const originalVal = getOriginalStyleVal(key, isTransform(key))
290
- // if (!keyMap[key]) keyMap[key] = originalVal === undefined ? false : originalVal
291
278
  if (!keyMap[key])
292
279
  keyMap[key] = true;
293
280
  });
@@ -295,7 +282,7 @@ export default function useAnimationHooks(props) {
295
282
  }, animatedKeys.current);
296
283
  }
297
284
  // animated key transform 格式化
298
- function formatAnimatedKeys(keys = []) {
285
+ function formatAnimatedKeys(keys) {
299
286
  const animatedKeys = [];
300
287
  const transforms = [];
301
288
  keys.forEach(key => {
@@ -310,13 +297,24 @@ export default function useAnimationHooks(props) {
310
297
  animatedKeys.push(transforms);
311
298
  return animatedKeys;
312
299
  }
313
- // transform 数组转对象
314
- function getTransformObj() {
315
- 'worklet';
316
- const transforms = originalStyle.transform || [];
317
- return transforms.reduce((transformObj, item) => {
318
- return Object.assign(transformObj, item);
319
- }, {});
300
+ // 设置 lastShareValRef & shareValMap
301
+ function updateStyleVal() {
302
+ Object.entries(originalStyle).forEach(([key, value]) => {
303
+ if (key === 'transform') {
304
+ Object.entries(getTransformObj(value)).forEach(([key, value]) => {
305
+ if (value !== lastStyleRef.current[key]) {
306
+ lastStyleRef.current[key] = value;
307
+ shareValMap[key].value = value;
308
+ }
309
+ });
310
+ }
311
+ else if (hasOwn(shareValMap, key)) {
312
+ if (value !== lastStyleRef.current[key]) {
313
+ lastStyleRef.current[key] = value;
314
+ shareValMap[key].value = value;
315
+ }
316
+ }
317
+ });
320
318
  }
321
319
  // ** 生成动画样式
322
320
  // eslint-disable-next-line react-hooks/rules-of-hooks
@@ -325,7 +323,7 @@ export default function useAnimationHooks(props) {
325
323
  return animatedStyleKeys.value.reduce((styles, key) => {
326
324
  // console.info('getAnimationStyles', key, shareValMap[key].value)
327
325
  if (Array.isArray(key)) {
328
- const transformStyle = getTransformObj();
326
+ const transformStyle = getTransformObj(originalStyle.transform || []);
329
327
  key.forEach((transformKey) => {
330
328
  transformStyle[transformKey] = shareValMap[transformKey].value;
331
329
  });
@@ -19,6 +19,7 @@ export const HIDDEN_STYLE = {
19
19
  };
20
20
  export const isIOS = __mpx_mode__ === 'ios';
21
21
  export const isAndroid = __mpx_mode__ === 'android';
22
+ export const isHarmony = __mpx_mode__ === 'harmony';
22
23
  const varDecRegExp = /^--/;
23
24
  const varUseRegExp = /var\(/;
24
25
  const unoVarDecRegExp = /^--un-/;
@@ -239,12 +240,8 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
239
240
  const normalStyleChangedRef = useRef(false);
240
241
  let hasVarDec = false;
241
242
  let hasVarUse = false;
242
- let hasSelfPercent = false;
243
243
  const varKeyPaths = [];
244
244
  const unoVarKeyPaths = [];
245
- const percentKeyPaths = [];
246
- const calcKeyPaths = [];
247
- const envKeyPaths = [];
248
245
  const [width, setWidth] = useState(0);
249
246
  const [height, setHeight] = useState(0);
250
247
  const navigation = useNavigation();
@@ -304,6 +301,11 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
304
301
  normalStyleChangedRef.current = !normalStyleChangedRef.current;
305
302
  }
306
303
  const memoResult = useMemo(() => {
304
+ let hasSelfPercent = false;
305
+ let hasPositionFixed = false;
306
+ const percentKeyPaths = [];
307
+ const calcKeyPaths = [];
308
+ const envKeyPaths = [];
307
309
  // transform can be memoized
308
310
  function envVisitor({ value, keyPath }) {
309
311
  if (envUseRegExp.test(value)) {
@@ -324,6 +326,12 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
324
326
  percentKeyPaths.push(keyPath.slice());
325
327
  }
326
328
  }
329
+ function transformPosition(styleObj) {
330
+ if (styleObj.position === 'fixed') {
331
+ hasPositionFixed = true;
332
+ styleObj.position = 'absolute';
333
+ }
334
+ }
327
335
  // traverse env & calc & percent
328
336
  traverseStyle(normalStyle, [envVisitor, percentVisitor, calcVisitor]);
329
337
  const percentConfig = {
@@ -355,11 +363,14 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
355
363
  }
356
364
  }
357
365
  });
366
+ // apply position
367
+ transformPosition(normalStyle);
358
368
  // transform number enum stringify
359
369
  transformStringify(normalStyle);
360
370
  return {
361
371
  normalStyle,
362
- hasSelfPercent
372
+ hasSelfPercent,
373
+ hasPositionFixed
363
374
  };
364
375
  }, [normalStyleChangedRef.current, width, height, parentWidth, parentHeight, parentFontSize]);
365
376
  return extendObject({
@@ -434,8 +445,8 @@ export const useLayout = ({ props, hasSelfPercent, setWidth, setHeight, onLayout
434
445
  hasLayoutRef.current = true;
435
446
  if (hasSelfPercent) {
436
447
  const { width, height } = e?.nativeEvent?.layout || {};
437
- setWidth(width || 0);
438
- setHeight(height || 0);
448
+ setWidth && setWidth(width || 0);
449
+ setHeight && setHeight(height || 0);
439
450
  }
440
451
  if (enableOffset) {
441
452
  nodeRef.current?.measure((x, y, width, height, offsetLeft, offsetTop) => {
@@ -51,7 +51,8 @@ import {
51
51
  TextInputSelectionChangeEventData,
52
52
  TextInputFocusEventData,
53
53
  TextInputChangeEventData,
54
- TextInputSubmitEditingEventData
54
+ TextInputSubmitEditingEventData,
55
+ NativeTouchEvent
55
56
  } from 'react-native'
56
57
  import { warn } from '@mpxjs/utils'
57
58
  import { useUpdateEffect, useTransformStyle, useLayout, extendObject } from './utils'
@@ -195,7 +196,7 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
195
196
 
196
197
  const [inputValue, setInputValue] = useState(defaultValue)
197
198
  const [contentHeight, setContentHeight] = useState(0)
198
- const [selection, setSelection] = useState({ start: -1, end: -1 })
199
+ const [selection, setSelection] = useState({ start: -1, end: tmpValue.current.length })
199
200
 
200
201
  const styleObj = extendObject(
201
202
  { padding: 0, backgroundColor: '#fff' },
@@ -221,15 +222,17 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
221
222
 
222
223
  useEffect(() => {
223
224
  if (inputValue !== value) {
224
- setInputValue(parseValue(value))
225
+ const parsed = parseValue(value)
226
+ tmpValue.current = parsed
227
+ setInputValue(parsed)
225
228
  }
226
229
  }, [value])
227
230
 
228
231
  useEffect(() => {
229
- if (typeof cursor === 'number') {
232
+ if (selectionStart > -1) {
233
+ setSelection({ start: selectionStart, end: selectionEnd === -1 ? tmpValue.current.length : selectionEnd })
234
+ } else if (typeof cursor === 'number') {
230
235
  setSelection({ start: cursor, end: cursor })
231
- } else if (selectionStart >= 0 && selectionEnd >= 0 && selectionStart !== selectionEnd) {
232
- setSelection({ start: selectionStart, end: selectionEnd })
233
236
  }
234
237
  }, [cursor, selectionStart, selectionEnd])
235
238
 
@@ -290,6 +293,10 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
290
293
  setKeyboardAvoidContext()
291
294
  }
292
295
 
296
+ const onTouchEnd = (evt: NativeSyntheticEvent<NativeTouchEvent & { origin?: string }>) => {
297
+ evt.nativeEvent.origin = 'input'
298
+ }
299
+
293
300
  const onFocus = (evt: NativeSyntheticEvent<TextInputFocusEventData>) => {
294
301
  setKeyboardAvoidContext()
295
302
  bindfocus && bindfocus(
@@ -324,23 +331,6 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
324
331
  )
325
332
  }
326
333
 
327
- const onKeyPress = (evt: NativeSyntheticEvent<TextInputKeyPressEventData>) => {
328
- evt.nativeEvent.key === 'Enter' &&
329
- bindconfirm!(
330
- getCustomEvent(
331
- 'confirm',
332
- evt,
333
- {
334
- detail: {
335
- value: tmpValue.current || ''
336
- },
337
- layoutRef
338
- },
339
- props
340
- )
341
- )
342
- }
343
-
344
334
  const onSubmitEditing = (evt: NativeSyntheticEvent<TextInputSubmitEditingEventData>) => {
345
335
  bindconfirm!(
346
336
  getCustomEvent(
@@ -405,6 +395,7 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
405
395
  }
406
396
 
407
397
  const resetValue = () => {
398
+ tmpValue.current = ''
408
399
  setInputValue('')
409
400
  }
410
401
 
@@ -457,7 +448,7 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
457
448
  maxLength: maxlength === -1 ? undefined : maxlength,
458
449
  editable: !disabled,
459
450
  autoFocus: !!autoFocus || !!focus,
460
- selection: selection,
451
+ selection: selectionStart > -1 || typeof cursor === 'number' ? selection : undefined,
461
452
  selectionColor: cursorColor,
462
453
  blurOnSubmit: !multiline && !confirmHold,
463
454
  underlineColorAndroid: 'rgba(0,0,0,0)',
@@ -469,13 +460,13 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
469
460
  layoutProps,
470
461
  {
471
462
  onTouchStart,
463
+ onTouchEnd,
472
464
  onFocus,
473
465
  onBlur,
474
466
  onChange,
475
467
  onSelectionChange,
476
468
  onContentSizeChange,
477
- onKeyPress: bindconfirm && onKeyPress,
478
- onSubmitEditing: bindconfirm && multiline && onSubmitEditing
469
+ onSubmitEditing: bindconfirm && !multiline && onSubmitEditing
479
470
  }
480
471
  ),
481
472
  [
@@ -1,7 +1,6 @@
1
- import React, { ReactNode, useContext, useEffect, useMemo } from 'react'
2
- import { DimensionValue, EmitterSubscription, Keyboard, Platform, View, ViewStyle } 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, { ReactNode, useContext, useEffect } from 'react'
2
+ import { DimensionValue, EmitterSubscription, Keyboard, View, ViewStyle, Platform, NativeSyntheticEvent, NativeTouchEvent } from 'react-native'
3
+ import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated'
5
4
  import { KeyboardAvoidContext } from './context'
6
5
 
7
6
  type KeyboardAvoidViewProps = {
@@ -19,25 +18,10 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
19
18
  const basic = useSharedValue('auto')
20
19
  const keyboardAvoid = useContext(KeyboardAvoidContext)
21
20
 
22
- const dismiss = () => {
23
- Keyboard.isVisible() && Keyboard.dismiss()
24
- }
25
-
26
- const gesture = useMemo(() => {
27
- return Gesture.Tap()
28
- .onEnd(() => {
29
- runOnJS(dismiss)()
30
- })
31
- }, [])
32
-
33
- const animatedStyle = useAnimatedStyle(() => {
34
- return Object.assign(
35
- {
36
- transform: [{ translateY: -offset.value }]
37
- },
38
- isIOS ? {} : { flexBasis: basic.value as DimensionValue }
39
- )
40
- })
21
+ const animatedStyle = useAnimatedStyle(() => ({
22
+ transform: [{ translateY: -offset.value }],
23
+ flexBasis: basic.value as DimensionValue
24
+ }))
41
25
 
42
26
  const resetKeyboard = () => {
43
27
  if (keyboardAvoid?.current) {
@@ -47,6 +31,12 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
47
31
  basic.value = 'auto'
48
32
  }
49
33
 
34
+ const onTouchEnd = ({ nativeEvent }: NativeSyntheticEvent<NativeTouchEvent & { origin?: string }>) => {
35
+ if (nativeEvent.origin !== 'input') {
36
+ Keyboard.isVisible() && Keyboard.dismiss()
37
+ }
38
+ }
39
+
50
40
  useEffect(() => {
51
41
  let subscriptions: EmitterSubscription[] = []
52
42
 
@@ -62,7 +52,12 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
62
52
  const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
63
53
  const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing)
64
54
  const value = aboveOffset > 0 ? belowValue : aboveValue
65
- offset.value = withTiming(value, { duration, easing })
55
+ offset.value = withTiming(value, { duration, easing }, (finished) => {
56
+ if (finished) {
57
+ // Set flexBasic after animation to trigger re-layout and reset layout information
58
+ basic.value = '99.99%'
59
+ }
60
+ })
66
61
  })
67
62
  })
68
63
  }),
@@ -82,11 +77,7 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
82
77
  const value = aboveOffset > 0 ? belowValue : aboveValue
83
78
  offset.value = withTiming(value, { duration, easing }, (finished) => {
84
79
  if (finished) {
85
- /**
86
- * In the Android environment, the layout information is not synchronized after the animation,
87
- * which results in the inability to correctly trigger element events.
88
- * Here, we utilize flexBasic to proactively trigger a re-layout
89
- */
80
+ // Set flexBasic after animation to trigger re-layout and reset layout information
90
81
  basic.value = '99.99%'
91
82
  }
92
83
  })
@@ -102,19 +93,19 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: Keyboa
102
93
  }, [keyboardAvoid])
103
94
 
104
95
  return (
105
- <GestureDetector gesture={gesture}>
106
- <View style={style}>
107
- <Animated.View
108
- style={[
109
- contentContainerStyle,
110
- animatedStyle
111
- ]}
112
- >
113
- {children}
114
- </Animated.View>
115
- </View>
116
- </GestureDetector>
96
+ <View style={style} onTouchEnd={onTouchEnd}>
97
+ <Animated.View
98
+ style={[
99
+ contentContainerStyle,
100
+ animatedStyle
101
+ ]}
102
+ >
103
+ {children}
104
+ </Animated.View>
105
+ </View>
117
106
  )
118
107
  }
119
108
 
109
+ KeyboardAvoidingView.displayName = 'MpxKeyboardAvoidingView'
110
+
120
111
  export default KeyboardAvoidingView
@@ -1,7 +1,7 @@
1
1
  import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react'
2
2
  import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, ScrollView, StyleSheet, View } from 'react-native'
3
3
  import Reanimated, { AnimatedRef, 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, { HandlerRef } from './useNodesRef'
6
6
  import PickerIndicator from './pickerViewIndicator'
7
7
  import PickerMask from './pickerViewMask'
@@ -140,7 +140,7 @@ const _PickerViewColumn = forwardRef<HandlerRef<ScrollView & View, ColumnProps>,
140
140
  y: initialIndex * itemRawH,
141
141
  animated: false
142
142
  })
143
- }, isAndroid ? 200 : 0)
143
+ }, isAndroid || isHarmony ? 200 : 0)
144
144
  activeIndex.current = initialIndex
145
145
  }, [itemRawH, maxIndex, initialIndex])
146
146
 
@@ -1,6 +1,5 @@
1
1
  import { useState, useCallback, forwardRef, ForwardedRef, useImperativeHandle, ReactNode, ReactElement } from 'react'
2
2
  import { View, StyleSheet } from 'react-native'
3
- import { extendObject } from '../utils'
4
3
 
5
4
  export type State = {
6
5
  portals: Array<{
@@ -27,7 +26,7 @@ const _PortalManager = forwardRef((props: PortalManagerProps, ref:ForwardedRef<u
27
26
  setState((prevState) => ({
28
27
  portals: prevState.portals.map((item) => {
29
28
  if (item.key === key) {
30
- return extendObject({}, item, { children })
29
+ return Object.assign({}, item, { children })
31
30
  }
32
31
  return item
33
32
  })
@@ -70,6 +70,7 @@ interface ScrollViewProps {
70
70
  'parent-height'?: number;
71
71
  'wait-for'?: Array<GestureHandler>;
72
72
  'simultaneous-handlers'?: Array<GestureHandler>;
73
+ 'scroll-event-throttle'?:number;
73
74
  bindscrolltoupper?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
74
75
  bindscrolltolower?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
75
76
  bindscroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
@@ -140,6 +141,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
140
141
  'parent-height': parentHeight,
141
142
  'simultaneous-handlers': originSimultaneousHandlers,
142
143
  'wait-for': waitFor,
144
+ 'scroll-event-throttle': scrollEventThrottle = 0,
143
145
  __selectRef
144
146
  } = props
145
147
 
@@ -159,7 +161,6 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
159
161
  visibleLength: 0
160
162
  })
161
163
 
162
- const scrollEventThrottle = 50
163
164
  const hasCallScrollToUpper = useRef(true)
164
165
  const hasCallScrollToLower = useRef(false)
165
166
  const initialTimeout = useRef<ReturnType<typeof setTimeout> | null>(null)
@@ -202,6 +203,8 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
202
203
 
203
204
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout })
204
205
 
206
+ const lastOffset = useRef(0)
207
+
205
208
  if (scrollX && scrollY) {
206
209
  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')
207
210
  }
@@ -263,7 +266,8 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
263
266
  function onStartReached (e: NativeSyntheticEvent<NativeScrollEvent>) {
264
267
  const { bindscrolltoupper } = props
265
268
  const { offset } = scrollOptions.current
266
- if (bindscrolltoupper && (offset <= upperThreshold)) {
269
+ const isScrollingBackward = offset < lastOffset.current
270
+ if (bindscrolltoupper && (offset <= upperThreshold) && isScrollingBackward) {
267
271
  if (!hasCallScrollToUpper.current) {
268
272
  bindscrolltoupper(
269
273
  getCustomEvent('scrolltoupper', e, {
@@ -284,13 +288,15 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
284
288
  const { bindscrolltolower } = props
285
289
  const { contentLength, visibleLength, offset } = scrollOptions.current
286
290
  const distanceFromEnd = contentLength - visibleLength - offset
287
- if (bindscrolltolower && (distanceFromEnd < lowerThreshold)) {
291
+ const isScrollingForward = offset > lastOffset.current
292
+
293
+ if (bindscrolltolower && (distanceFromEnd < lowerThreshold) && isScrollingForward) {
288
294
  if (!hasCallScrollToLower.current) {
289
295
  hasCallScrollToLower.current = true
290
296
  bindscrolltolower(
291
297
  getCustomEvent('scrolltolower', e, {
292
298
  detail: {
293
- direction: scrollX ? 'right' : 'botttom'
299
+ direction: scrollX ? 'right' : 'bottom'
294
300
  },
295
301
  layoutRef
296
302
  }, props)
@@ -345,6 +351,8 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
345
351
  onStartReached(e)
346
352
  onEndReached(e)
347
353
  updateIntersection()
354
+ // 在 onStartReached、onEndReached 执行完后更新 lastOffset
355
+ lastOffset.current = scrollOptions.current.offset
348
356
  }
349
357
 
350
358
  function onScrollEnd (e: NativeSyntheticEvent<NativeScrollEvent>) {
@@ -367,6 +375,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
367
375
  onStartReached(e)
368
376
  onEndReached(e)
369
377
  updateIntersection()
378
+ lastOffset.current = scrollOptions.current.offset
370
379
  }
371
380
  function updateIntersection () {
372
381
  if (enableTriggerIntersectionObserver && intersectionObservers) {