@mpxjs/webpack-plugin 2.10.4-beta.18 → 2.10.4-beta.19-input

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 (36) hide show
  1. package/lib/runtime/components/react/dist/getInnerListeners.js +36 -22
  2. package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +145 -0
  3. package/lib/runtime/components/react/dist/mpx-button.jsx +7 -2
  4. package/lib/runtime/components/react/dist/mpx-canvas/Image.js +2 -4
  5. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +20 -17
  6. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +7 -2
  7. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +7 -2
  8. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +7 -2
  9. package/lib/runtime/components/react/dist/mpx-image.jsx +33 -20
  10. package/lib/runtime/components/react/dist/mpx-input.jsx +7 -2
  11. package/lib/runtime/components/react/dist/mpx-label.jsx +7 -2
  12. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +8 -3
  13. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +205 -79
  14. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +11 -13
  15. package/lib/runtime/components/react/dist/mpx-picker-view/index.jsx +8 -7
  16. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.jsx +29 -11
  17. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +3 -5
  18. package/lib/runtime/components/react/dist/mpx-progress.jsx +163 -0
  19. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +9 -2
  20. package/lib/runtime/components/react/dist/mpx-radio.jsx +7 -2
  21. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +10 -2
  22. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +104 -51
  23. package/lib/runtime/components/react/dist/mpx-slider.jsx +321 -0
  24. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +3 -1
  25. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +11 -9
  26. package/lib/runtime/components/react/dist/mpx-swiper.jsx +203 -141
  27. package/lib/runtime/components/react/dist/mpx-switch.jsx +7 -2
  28. package/lib/runtime/components/react/dist/mpx-text.jsx +7 -2
  29. package/lib/runtime/components/react/dist/mpx-video.jsx +7 -2
  30. package/lib/runtime/components/react/dist/mpx-view.jsx +28 -26
  31. package/lib/runtime/components/react/dist/mpx-web-view.jsx +34 -29
  32. package/lib/runtime/components/react/dist/useAnimationHooks.js +12 -89
  33. package/lib/runtime/components/react/dist/utils.jsx +199 -114
  34. package/lib/runtime/components/react/mpx-input.tsx +6 -6
  35. package/lib/template-compiler/compiler.js +1 -1
  36. package/package.json +1 -1
@@ -7,9 +7,10 @@ import useNodesRef from './useNodesRef';
7
7
  import useInnerProps from './getInnerListeners';
8
8
  import { MovableAreaContext } from './context';
9
9
  import { useTransformStyle, wrapChildren, useLayout, extendObject } from './utils';
10
+ import Portal from './mpx-portal';
10
11
  const _MovableArea = forwardRef((props, ref) => {
11
12
  const { style = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
12
- const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
13
+ const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, hasPositionFixed, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
13
14
  const movableViewRef = useRef(null);
14
15
  useNodesRef(props, ref, movableViewRef, {
15
16
  style: normalStyle
@@ -20,13 +21,17 @@ const _MovableArea = forwardRef((props, ref) => {
20
21
  }), [normalStyle.width, normalStyle.height]);
21
22
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: movableViewRef });
22
23
  const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
23
- style: extendObject({ height: contextValue.height, width: contextValue.width, overflow: 'hidden' }, normalStyle, layoutStyle),
24
+ style: extendObject({ height: contextValue.height, width: contextValue.width }, normalStyle, layoutStyle),
24
25
  ref: movableViewRef
25
26
  }), [], { layoutRef });
26
- return createElement(MovableAreaContext.Provider, { value: contextValue }, createElement(View, innerProps, wrapChildren(props, {
27
+ let movableComponent = createElement(MovableAreaContext.Provider, { value: contextValue }, createElement(View, innerProps, wrapChildren(props, {
27
28
  hasVarDec,
28
29
  varContext: varContextRef.current
29
30
  })));
31
+ if (hasPositionFixed) {
32
+ movableComponent = createElement(Portal, null, movableComponent);
33
+ }
34
+ return movableComponent;
30
35
  });
31
36
  _MovableArea.displayName = 'MpxMovableArea';
32
37
  export default _MovableArea;
@@ -4,8 +4,8 @@
4
4
  * ✔ out-of-bounds
5
5
  * ✔ x
6
6
  * ✔ y
7
- * damping
8
- * friction
7
+ * damping
8
+ * friction
9
9
  * ✔ disabled
10
10
  * ✘ scale
11
11
  * ✘ scale-min
@@ -22,10 +22,98 @@ import { StyleSheet } from 'react-native';
22
22
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
23
23
  import useNodesRef from './useNodesRef';
24
24
  import { MovableAreaContext } from './context';
25
- import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, flatGesture, extendObject, omit, useNavigation } from './utils';
25
+ import { useTransformStyle, splitProps, splitStyle, HIDDEN_STYLE, wrapChildren, flatGesture, extendObject, omit, useNavigation, useRunOnJSCallback } from './utils';
26
26
  import { GestureDetector, Gesture } from 'react-native-gesture-handler';
27
- import Animated, { useSharedValue, useAnimatedStyle, withDecay, runOnJS, runOnUI, useAnimatedReaction, withSpring } from 'react-native-reanimated';
27
+ import Animated, { useSharedValue, useAnimatedStyle, runOnJS, runOnUI, withTiming, Easing } from 'react-native-reanimated';
28
28
  import { collectDataset, noop } from '@mpxjs/utils';
29
+ // 超出边界处理函数,参考微信小程序的超出边界衰减效果
30
+ const applyBoundaryDecline = (newValue, range) => {
31
+ 'worklet';
32
+ const decline = (distance) => {
33
+ 'worklet';
34
+ return Math.sqrt(Math.abs(distance));
35
+ };
36
+ if (newValue < range[0]) {
37
+ const overDistance = range[0] - newValue;
38
+ return range[0] - decline(overDistance);
39
+ }
40
+ else if (newValue > range[1]) {
41
+ const overDistance = newValue - range[1];
42
+ return range[1] + decline(overDistance);
43
+ }
44
+ return newValue;
45
+ };
46
+ // 参考微信小程序的弹簧阻尼系统实现
47
+ const withWechatSpring = (toValue, dampingParam = 20, callback) => {
48
+ 'worklet';
49
+ // 弹簧参数计算
50
+ const m = 1; // 质量
51
+ const k = 9 * Math.pow(dampingParam, 2) / 40; // 弹簧系数
52
+ const c = dampingParam; // 阻尼系数
53
+ // 判别式:r = c² - 4mk
54
+ const discriminant = c * c - 4 * m * k;
55
+ // 计算动画持续时间和缓动函数
56
+ let duration;
57
+ let easingFunction;
58
+ if (Math.abs(discriminant) < 0.01) {
59
+ // 临界阻尼 (discriminant ≈ 0)
60
+ // 使用cubic-out模拟临界阻尼的平滑过渡
61
+ duration = Math.max(350, Math.min(800, 2000 / dampingParam));
62
+ easingFunction = Easing.out(Easing.cubic);
63
+ }
64
+ else if (discriminant > 0) {
65
+ // 过阻尼 (discriminant > 0)
66
+ // 使用指数缓动模拟过阻尼的缓慢收敛
67
+ duration = Math.max(450, Math.min(1000, 2500 / dampingParam));
68
+ easingFunction = Easing.out(Easing.exp);
69
+ }
70
+ else {
71
+ // 欠阻尼 (discriminant < 0) - 会产生振荡
72
+ // 计算振荡频率和衰减率
73
+ const dampingRatio = c / (2 * Math.sqrt(m * k)); // 阻尼比
74
+ // 根据阻尼比调整动画参数
75
+ if (dampingRatio < 0.7) {
76
+ // 明显振荡
77
+ duration = Math.max(600, Math.min(1200, 3000 / dampingParam));
78
+ // 创建带振荡的贝塞尔曲线
79
+ easingFunction = Easing.bezier(0.175, 0.885, 0.32, 1.275);
80
+ }
81
+ else {
82
+ // 轻微振荡
83
+ duration = Math.max(400, Math.min(800, 2000 / dampingParam));
84
+ easingFunction = Easing.bezier(0.25, 0.46, 0.45, 0.94);
85
+ }
86
+ }
87
+ return withTiming(toValue, {
88
+ duration,
89
+ easing: easingFunction
90
+ }, callback);
91
+ };
92
+ // 参考微信小程序friction的惯性动画
93
+ const withWechatDecay = (velocity, currentPosition, clampRange, frictionValue = 2, callback) => {
94
+ 'worklet';
95
+ // 微信小程序friction算法: delta = -1.5 * v² / a, 其中 a = -f * v / |v|
96
+ // 如果friction小于等于0,设置为默认值2
97
+ const validFriction = frictionValue <= 0 ? 2 : frictionValue;
98
+ const f = 1000 * validFriction;
99
+ const acceleration = velocity !== 0 ? -f * velocity / Math.abs(velocity) : 0;
100
+ const delta = acceleration !== 0 ? (-1.5 * velocity * velocity) / acceleration : 0;
101
+ let finalPosition = currentPosition + delta;
102
+ // 边界限制
103
+ if (finalPosition < clampRange[0]) {
104
+ finalPosition = clampRange[0];
105
+ }
106
+ else if (finalPosition > clampRange[1]) {
107
+ finalPosition = clampRange[1];
108
+ }
109
+ // 计算动画时长
110
+ const distance = Math.abs(finalPosition - currentPosition);
111
+ const duration = Math.min(1500, Math.max(200, distance * 8));
112
+ return withTiming(finalPosition, {
113
+ duration,
114
+ easing: Easing.out(Easing.cubic)
115
+ }, callback);
116
+ };
29
117
  const styles = StyleSheet.create({
30
118
  container: {
31
119
  position: 'absolute',
@@ -41,7 +129,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
41
129
  const hasLayoutRef = useRef(false);
42
130
  const propsRef = useRef({});
43
131
  propsRef.current = (props || {});
44
- const { x = 0, y = 0, inertia = false, disabled = false, animation = true, 'out-of-bounds': outOfBounds = false, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, direction = 'none', 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, bindtouchstart, catchtouchstart, bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove, bindtouchend, catchtouchend } = props;
132
+ const { x = 0, y = 0, inertia = false, disabled = false, animation = true, damping = 20, friction = 2, 'out-of-bounds': outOfBounds = false, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, direction = 'none', 'disable-event-passthrough': disableEventPassthrough = false, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, changeThrottleTime = 60, bindtouchstart, catchtouchstart, bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove, bindtouchend, catchtouchend, bindchange } = props;
45
133
  const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(Object.assign({}, style, styles.container), { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
46
134
  const navigation = useNavigation();
47
135
  const prevSimultaneousHandlersRef = useRef(originSimultaneousHandlers || []);
@@ -61,6 +149,8 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
61
149
  const yInertialMotion = useSharedValue(false);
62
150
  const isFirstTouch = useSharedValue(true);
63
151
  const touchEvent = useSharedValue('');
152
+ const initialViewPosition = useSharedValue({ x: x || 0, y: y || 0 });
153
+ const lastChangeTime = useSharedValue(0);
64
154
  const MovableAreaLayout = useContext(MovableAreaContext);
65
155
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
66
156
  const waitForHandlers = flatGesture(waitFor);
@@ -104,25 +194,21 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
104
194
  const { x: newX, y: newY } = checkBoundaryPosition({ positionX: Number(x), positionY: Number(y) });
105
195
  if (direction === 'horizontal' || direction === 'all') {
106
196
  offsetX.value = animation
107
- ? withSpring(newX, {
108
- duration: 1500,
109
- dampingRatio: 0.8
110
- })
197
+ ? withWechatSpring(newX, damping)
111
198
  : newX;
112
199
  }
113
200
  if (direction === 'vertical' || direction === 'all') {
114
201
  offsetY.value = animation
115
- ? withSpring(newY, {
116
- duration: 1500,
117
- dampingRatio: 0.8
118
- })
202
+ ? withWechatSpring(newY, damping)
119
203
  : newY;
120
204
  }
121
- runOnJS(handleTriggerChange)({
122
- x: newX,
123
- y: newY,
124
- type: 'setData'
125
- });
205
+ if (bindchange) {
206
+ runOnJS(runOnJSCallback)('handleTriggerChange', {
207
+ x: newX,
208
+ y: newY,
209
+ type: 'setData'
210
+ });
211
+ }
126
212
  }
127
213
  })();
128
214
  }, [x, y]);
@@ -132,16 +218,6 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
132
218
  resetBoundaryAndCheck({ width, height });
133
219
  }
134
220
  }, [MovableAreaLayout.height, MovableAreaLayout.width]);
135
- useAnimatedReaction(() => ({
136
- offsetX: offsetX.value,
137
- offsetY: offsetY.value
138
- }), (currentValue) => {
139
- const { offsetX, offsetY } = currentValue;
140
- runOnJS(handleTriggerChange)({
141
- x: offsetX,
142
- y: offsetY
143
- });
144
- });
145
221
  const getTouchSource = useCallback((offsetX, offsetY) => {
146
222
  const hasOverBoundary = offsetX < draggableXRange.value[0] || offsetX > draggableXRange.value[1] ||
147
223
  offsetY < draggableYRange.value[0] || offsetY > draggableYRange.value[1];
@@ -230,14 +306,14 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
230
306
  setHeight(height || 0);
231
307
  }
232
308
  nodeRef.current?.measure((x, y, width, height) => {
233
- const { y: navigationY = 0 } = navigation?.layout || {};
309
+ const { top: navigationY = 0 } = navigation?.layout || {};
234
310
  layoutRef.current = { x, y: y - navigationY, width, height, offsetLeft: 0, offsetTop: 0 };
235
311
  resetBoundaryAndCheck({ width, height });
236
312
  });
237
313
  props.onLayout && props.onLayout(e);
238
314
  };
239
315
  const extendEvent = useCallback((e, type) => {
240
- const { y: navigationY = 0 } = navigation?.layout || {};
316
+ const { top: navigationY = 0 } = navigation?.layout || {};
241
317
  const touchArr = [e.changedTouches, e.allTouches];
242
318
  touchArr.forEach(touches => {
243
319
  touches && touches.forEach((item) => {
@@ -259,11 +335,13 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
259
335
  });
260
336
  }, []);
261
337
  const triggerStartOnJS = ({ e }) => {
338
+ const { bindtouchstart, catchtouchstart } = propsRef.current;
262
339
  extendEvent(e, 'start');
263
340
  bindtouchstart && bindtouchstart(e);
264
341
  catchtouchstart && catchtouchstart(e);
265
342
  };
266
343
  const triggerMoveOnJS = ({ e, hasTouchmove, hasCatchTouchmove, touchEvent }) => {
344
+ const { bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove } = propsRef.current;
267
345
  extendEvent(e, 'move');
268
346
  if (hasTouchmove) {
269
347
  if (touchEvent === 'htouchmove') {
@@ -285,17 +363,34 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
285
363
  }
286
364
  };
287
365
  const triggerEndOnJS = ({ e }) => {
366
+ const { bindtouchend, catchtouchend } = propsRef.current;
288
367
  extendEvent(e, 'end');
289
368
  bindtouchend && bindtouchend(e);
290
369
  catchtouchend && catchtouchend(e);
291
370
  };
371
+ const runOnJSCallbackRef = useRef({
372
+ handleTriggerChange,
373
+ triggerStartOnJS,
374
+ triggerMoveOnJS,
375
+ triggerEndOnJS
376
+ });
377
+ const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef);
378
+ // 节流版本的change事件触发
379
+ const handleTriggerChangeThrottled = useCallback(({ x, y, type }) => {
380
+ 'worklet';
381
+ const now = Date.now();
382
+ if (now - lastChangeTime.value >= changeThrottleTime) {
383
+ lastChangeTime.value = now;
384
+ runOnJS(runOnJSCallback)('handleTriggerChange', { x, y, type });
385
+ }
386
+ }, [changeThrottleTime]);
292
387
  const gesture = useMemo(() => {
293
388
  const handleTriggerMove = (e) => {
294
389
  'worklet';
295
390
  const hasTouchmove = !!bindhtouchmove || !!bindvtouchmove || !!bindtouchmove;
296
391
  const hasCatchTouchmove = !!catchhtouchmove || !!catchvtouchmove || !!catchtouchmove;
297
392
  if (hasTouchmove || hasCatchTouchmove) {
298
- runOnJS(triggerMoveOnJS)({
393
+ runOnJS(runOnJSCallback)('triggerMoveOnJS', {
299
394
  e,
300
395
  touchEvent: touchEvent.value,
301
396
  hasTouchmove,
@@ -313,8 +408,15 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
313
408
  y: changedTouches.y
314
409
  };
315
410
  if (bindtouchstart || catchtouchstart) {
316
- runOnJS(triggerStartOnJS)({ e });
411
+ runOnJS(runOnJSCallback)('triggerStartOnJS', { e });
317
412
  }
413
+ })
414
+ .onStart(() => {
415
+ 'worklet';
416
+ initialViewPosition.value = {
417
+ x: offsetX.value,
418
+ y: offsetY.value
419
+ };
318
420
  })
319
421
  .onTouchesMove((e) => {
320
422
  'worklet';
@@ -325,87 +427,111 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
325
427
  isFirstTouch.value = false;
326
428
  }
327
429
  handleTriggerMove(e);
430
+ })
431
+ .onUpdate((e) => {
432
+ 'worklet';
328
433
  if (disabled)
329
434
  return;
330
- const changeX = changedTouches.x - startPosition.value.x;
331
- const changeY = changedTouches.y - startPosition.value.y;
332
435
  if (direction === 'horizontal' || direction === 'all') {
333
- const newX = offsetX.value + changeX;
436
+ const newX = initialViewPosition.value.x + e.translationX;
334
437
  if (!outOfBounds) {
335
438
  const { x } = checkBoundaryPosition({ positionX: newX, positionY: offsetY.value });
336
439
  offsetX.value = x;
337
440
  }
338
441
  else {
339
- offsetX.value = newX;
442
+ offsetX.value = applyBoundaryDecline(newX, draggableXRange.value);
340
443
  }
341
444
  }
342
445
  if (direction === 'vertical' || direction === 'all') {
343
- const newY = offsetY.value + changeY;
446
+ const newY = initialViewPosition.value.y + e.translationY;
344
447
  if (!outOfBounds) {
345
448
  const { y } = checkBoundaryPosition({ positionX: offsetX.value, positionY: newY });
346
449
  offsetY.value = y;
347
450
  }
348
451
  else {
349
- offsetY.value = newY;
452
+ offsetY.value = applyBoundaryDecline(newY, draggableYRange.value);
350
453
  }
351
454
  }
455
+ if (bindchange) {
456
+ // 使用节流版本减少 runOnJS 调用
457
+ handleTriggerChangeThrottled({
458
+ x: offsetX.value,
459
+ y: offsetY.value
460
+ });
461
+ }
352
462
  })
353
463
  .onTouchesUp((e) => {
354
464
  'worklet';
355
465
  isFirstTouch.value = true;
356
466
  isMoving.value = false;
357
467
  if (bindtouchend || catchtouchend) {
358
- runOnJS(triggerEndOnJS)({ e });
468
+ runOnJS(runOnJSCallback)('triggerEndOnJS', { e });
359
469
  }
470
+ })
471
+ .onEnd((e) => {
472
+ 'worklet';
473
+ isMoving.value = false;
360
474
  if (disabled)
361
475
  return;
362
- if (!inertia) {
476
+ // 处理没有惯性且超出边界的回弹
477
+ if (!inertia && outOfBounds) {
363
478
  const { x, y } = checkBoundaryPosition({ positionX: offsetX.value, positionY: offsetY.value });
364
- if (x !== offsetX.value) {
365
- offsetX.value = animation
366
- ? withSpring(x, {
367
- duration: 1500,
368
- dampingRatio: 0.8
369
- })
370
- : x;
479
+ if (x !== offsetX.value || y !== offsetY.value) {
480
+ if (x !== offsetX.value) {
481
+ offsetX.value = animation
482
+ ? withWechatSpring(x, damping)
483
+ : x;
484
+ }
485
+ if (y !== offsetY.value) {
486
+ offsetY.value = animation
487
+ ? withWechatSpring(y, damping)
488
+ : y;
489
+ }
490
+ if (bindchange) {
491
+ runOnJS(runOnJSCallback)('handleTriggerChange', {
492
+ x,
493
+ y
494
+ });
495
+ }
371
496
  }
372
- if (y !== offsetY.value) {
373
- offsetY.value = animation
374
- ? withSpring(y, {
375
- duration: 1500,
376
- dampingRatio: 0.8
377
- })
378
- : y;
497
+ }
498
+ else if (inertia) {
499
+ // 惯性处理 - 使用微信小程序friction算法
500
+ if (direction === 'horizontal' || direction === 'all') {
501
+ xInertialMotion.value = true;
502
+ offsetX.value = withWechatDecay(e.velocityX / 10, offsetX.value, draggableXRange.value, friction, () => {
503
+ xInertialMotion.value = false;
504
+ if (bindchange) {
505
+ runOnJS(runOnJSCallback)('handleTriggerChange', {
506
+ x: offsetX.value,
507
+ y: offsetY.value
508
+ });
509
+ }
510
+ });
511
+ }
512
+ if (direction === 'vertical' || direction === 'all') {
513
+ yInertialMotion.value = true;
514
+ offsetY.value = withWechatDecay(e.velocityY / 10, offsetY.value, draggableYRange.value, friction, () => {
515
+ yInertialMotion.value = false;
516
+ if (bindchange) {
517
+ runOnJS(runOnJSCallback)('handleTriggerChange', {
518
+ x: offsetX.value,
519
+ y: offsetY.value
520
+ });
521
+ }
522
+ });
379
523
  }
380
524
  }
381
525
  })
382
- .onFinalize((e) => {
383
- 'worklet';
384
- isMoving.value = false;
385
- if (!inertia || disabled || !animation)
386
- return;
387
- if (direction === 'horizontal' || direction === 'all') {
388
- xInertialMotion.value = true;
389
- offsetX.value = withDecay({
390
- velocity: e.velocityX / 10,
391
- rubberBandEffect: outOfBounds,
392
- clamp: draggableXRange.value
393
- }, () => {
394
- xInertialMotion.value = false;
395
- });
526
+ .withRef(movableGestureRef);
527
+ if (!disableEventPassthrough) {
528
+ if (direction === 'horizontal') {
529
+ gesturePan.activeOffsetX([-5, 5]).failOffsetY([-5, 5]);
396
530
  }
397
- if (direction === 'vertical' || direction === 'all') {
398
- yInertialMotion.value = true;
399
- offsetY.value = withDecay({
400
- velocity: e.velocityY / 10,
401
- rubberBandEffect: outOfBounds,
402
- clamp: draggableYRange.value
403
- }, () => {
404
- yInertialMotion.value = false;
405
- });
531
+ else if (direction === 'vertical') {
532
+ gesturePan.activeOffsetY([-5, 5]).failOffsetX([-5, 5]);
406
533
  }
407
- })
408
- .withRef(movableGestureRef);
534
+ }
409
535
  if (simultaneousHandlers && simultaneousHandlers.length) {
410
536
  gesturePan.simultaneousWithExternalGesture(...simultaneousHandlers);
411
537
  }
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useRef, useContext, useEffect } from 'react';
1
+ import React, { forwardRef, useRef, useContext, useEffect, createElement } from 'react';
2
2
  import { StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native';
3
3
  import { warn } from '@mpxjs/utils';
4
4
  import PickerSelector from './selector';
@@ -9,7 +9,7 @@ import PickerRegion from './region';
9
9
  import { FormContext, RouteContext } from '../context';
10
10
  import useNodesRef from '../useNodesRef';
11
11
  import useInnerProps, { getCustomEvent } from '../getInnerListeners';
12
- import { extendObject } from '../utils';
12
+ import { extendObject, useLayout } from '../utils';
13
13
  import { createPopupManager } from '../mpx-popup';
14
14
  /**
15
15
  * ✔ mode
@@ -109,17 +109,18 @@ const Picker = forwardRef((props, ref) => {
109
109
  const buttonText = buttonTextMap[global.__mpx?.i18n?.locale || 'zh-CN'];
110
110
  const pickerValue = useRef(value);
111
111
  pickerValue.current = Array.isArray(value) ? value.slice() : value;
112
- const innerLayout = useRef({});
113
112
  const nodeRef = useRef(null);
114
113
  const pickerRef = useRef(null);
115
114
  const { open, show, hide, remove } = useRef(createPopupManager()).current;
116
115
  useNodesRef(props, ref, nodeRef);
116
+ const { layoutRef, layoutProps } = useLayout({
117
+ props,
118
+ hasSelfPercent: false,
119
+ nodeRef
120
+ });
117
121
  const innerProps = useInnerProps(extendObject({}, props, {
118
122
  ref: nodeRef
119
- }), [], { layoutRef: innerLayout });
120
- const getInnerLayout = (layout) => {
121
- innerLayout.current = layout.current;
122
- };
123
+ }, layoutProps), [], { layoutRef });
123
124
  useEffect(() => {
124
125
  if (range && pickerRef.current && mode === "multiSelector" /* PickerMode.MULTI_SELECTOR */) {
125
126
  pickerRef.current.updateRange?.(range);
@@ -162,7 +163,7 @@ const Picker = forwardRef((props, ref) => {
162
163
  if (mode !== "multiSelector" /* PickerMode.MULTI_SELECTOR */) {
163
164
  return;
164
165
  }
165
- const eventData = getCustomEvent('columnchange', {}, { detail: { column: columnIndex, value }, layoutRef: innerLayout });
166
+ const eventData = getCustomEvent('columnchange', {}, { detail: { column: columnIndex, value }, layoutRef });
166
167
  props.bindcolumnchange?.(eventData);
167
168
  };
168
169
  const onCancel = () => {
@@ -170,7 +171,7 @@ const Picker = forwardRef((props, ref) => {
170
171
  hide();
171
172
  };
172
173
  const onConfirm = () => {
173
- const eventData = getCustomEvent('change', {}, { detail: { value: pickerValue.current }, layoutRef: innerLayout });
174
+ const eventData = getCustomEvent('change', {}, { detail: { value: pickerValue.current }, layoutRef });
174
175
  bindchange?.(eventData);
175
176
  hide();
176
177
  };
@@ -179,7 +180,6 @@ const Picker = forwardRef((props, ref) => {
179
180
  children,
180
181
  bindchange: onChange,
181
182
  bindcolumnchange: onColumnChange,
182
- getInnerLayout,
183
183
  getRange: () => range
184
184
  });
185
185
  const renderPickerContent = () => {
@@ -215,9 +215,7 @@ const Picker = forwardRef((props, ref) => {
215
215
  remove();
216
216
  };
217
217
  }, []);
218
- return (<TouchableWithoutFeedback onPress={show}>
219
- {children}
220
- </TouchableWithoutFeedback>);
218
+ return createElement(TouchableWithoutFeedback, { onPress: show }, createElement(View, innerProps, children));
221
219
  });
222
220
  Picker.displayName = 'MpxPicker';
223
221
  export default Picker;
@@ -1,9 +1,10 @@
1
1
  import { View } from 'react-native';
2
- import React, { forwardRef, useRef } from 'react';
2
+ import React, { createElement, forwardRef, useRef } from 'react';
3
3
  import useInnerProps, { getCustomEvent } from '../getInnerListeners';
4
4
  import useNodesRef from '../useNodesRef';
5
5
  import { useLayout, splitProps, splitStyle, wrapChildren, useTransformStyle, extendObject } from '../utils';
6
6
  import { PickerViewStyleContext } from './pickerVIewContext';
7
+ import Portal from '../mpx-portal';
7
8
  const styles = {
8
9
  wrapper: {
9
10
  display: 'flex',
@@ -23,7 +24,7 @@ const _PickerView = forwardRef((props, ref) => {
23
24
  const activeValueRef = useRef(value);
24
25
  activeValueRef.current = value.slice();
25
26
  const snapActiveValueRef = useRef(null);
26
- const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext });
27
+ const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight, hasPositionFixed } = useTransformStyle(style, { enableVar, externalVarContext });
27
28
  useNodesRef(props, ref, nodeRef, {
28
29
  style: normalStyle
29
30
  });
@@ -114,11 +115,11 @@ const _PickerView = forwardRef((props, ref) => {
114
115
  onInitialChange(isInvalid, validValue);
115
116
  return renderColumns;
116
117
  };
117
- return (<PickerViewStyleContext.Provider value={textStyle}>
118
- <View {...innerProps}>
119
- <View style={[styles.wrapper]}>{renderPickerColumns()}</View>
120
- </View>
121
- </PickerViewStyleContext.Provider>);
118
+ const finalComponent = createElement(PickerViewStyleContext.Provider, { value: textStyle }, createElement(View, innerProps, createElement(View, { style: [styles.wrapper] }, renderPickerColumns())));
119
+ if (hasPositionFixed) {
120
+ return createElement(Portal, null, finalComponent);
121
+ }
122
+ return finalComponent;
122
123
  });
123
124
  _PickerView.displayName = 'MpxPickerView';
124
125
  export default _PickerView;
@@ -1,7 +1,7 @@
1
- import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react';
1
+ import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback, createElement } 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, isHarmony } from '../utils';
4
+ import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, isHarmony, extendObject } from '../utils';
5
5
  import useNodesRef from '../useNodesRef';
6
6
  import PickerIndicator from './pickerViewIndicator';
7
7
  import PickerMask from './pickerViewMask';
@@ -157,8 +157,8 @@ const _PickerViewColumn = forwardRef((props, ref) => {
157
157
  }, [itemRawH, maxIndex, snapToOffsets, onMomentumScrollEnd, resetScrollPosition]);
158
158
  const onScroll = useCallback((e) => {
159
159
  // 全局注册的振动触感 hook
160
- const pickerVibrate = global.__mpx?.config?.rnConfig?.pickerVibrate;
161
- if (typeof pickerVibrate !== 'function') {
160
+ const onPickerVibrate = global.__mpx?.config?.rnConfig?.onPickerVibrate;
161
+ if (typeof onPickerVibrate !== 'function') {
162
162
  return;
163
163
  }
164
164
  const { y } = e.nativeEvent.contentOffset;
@@ -172,7 +172,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
172
172
  y: currentId * itemRawH
173
173
  };
174
174
  // vibrateShort({ type: 'selection' })
175
- pickerVibrate();
175
+ onPickerVibrate();
176
176
  }
177
177
  }
178
178
  }
@@ -220,11 +220,28 @@ const _PickerViewColumn = forwardRef((props, ref) => {
220
220
  return (<MpxPickerVIewColumnItem key={index} item={item} index={index} itemHeight={itemHeight} textStyle={textStyle} textProps={textProps} visibleCount={visibleCount} onItemLayout={onItemLayout}/>);
221
221
  });
222
222
  const renderScollView = () => {
223
- return (<PickerViewColumnAnimationContext.Provider value={offsetYShared}>
224
- <Reanimated.ScrollView ref={scrollViewRef} bounces={true} horizontal={false} nestedScrollEnabled={true} removeClippedSubviews={false} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} scrollEventThrottle={16} {...layoutProps} onTouchEnd={onClickOnceItem} style={[{ width: '100%' }]} decelerationRate="fast" snapToOffsets={snapToOffsets} onScroll={onScroll} onScrollBeginDrag={onScrollBeginDrag} onScrollEndDrag={onScrollEndDrag} onMomentumScrollBegin={onMomentumScrollBegin} onMomentumScrollEnd={onMomentumScrollEnd} onContentSizeChange={onContentSizeChange} contentContainerStyle={contentContainerStyle}>
225
- {renderInnerchild()}
226
- </Reanimated.ScrollView>
227
- </PickerViewColumnAnimationContext.Provider>);
223
+ const innerProps = extendObject({}, layoutProps, {
224
+ ref: scrollViewRef,
225
+ bounces: true,
226
+ horizontal: false,
227
+ nestedScrollEnabled: true,
228
+ removeClippedSubviews: false,
229
+ showsVerticalScrollIndicator: false,
230
+ showsHorizontalScrollIndicator: false,
231
+ scrollEventThrottle: 16,
232
+ style: styles.scrollView,
233
+ decelerationRate: 'fast',
234
+ snapToOffsets: snapToOffsets,
235
+ onTouchEnd: onClickOnceItem,
236
+ onScroll,
237
+ onScrollBeginDrag,
238
+ onScrollEndDrag,
239
+ onMomentumScrollBegin,
240
+ onMomentumScrollEnd,
241
+ onContentSizeChange,
242
+ contentContainerStyle
243
+ });
244
+ return createElement(PickerViewColumnAnimationContext.Provider, { value: offsetYShared }, createElement(Reanimated.ScrollView, innerProps, renderInnerchild()));
228
245
  };
229
246
  const renderIndicator = () => (<PickerIndicator itemHeight={itemHeight} indicatorItemStyle={pickerIndicatorStyle}/>);
230
247
  const renderMask = () => (<PickerMask itemHeight={itemHeight} maskContainerStyle={pickerMaskStyle}/>);
@@ -235,7 +252,8 @@ const _PickerViewColumn = forwardRef((props, ref) => {
235
252
  </View>);
236
253
  });
237
254
  const styles = StyleSheet.create({
238
- wrapper: { display: 'flex', flex: 1 }
255
+ wrapper: { display: 'flex', flex: 1 },
256
+ scrollView: { width: '100%' }
239
257
  });
240
258
  _PickerViewColumn.displayName = 'MpxPickerViewColumn';
241
259
  export default _PickerViewColumn;
@@ -1,5 +1,4 @@
1
- import { useState, useCallback, forwardRef, useImperativeHandle } from 'react';
2
- import { View, StyleSheet } from 'react-native';
1
+ import { useState, useCallback, forwardRef, useImperativeHandle, Fragment } from 'react';
3
2
  const _PortalManager = forwardRef((props, ref) => {
4
3
  const [state, setState] = useState({
5
4
  portals: []
@@ -31,10 +30,9 @@ const _PortalManager = forwardRef((props, ref) => {
31
30
  portals: state.portals
32
31
  }));
33
32
  return (<>
34
- {state.portals.map(({ key, children }, i) => (<View key={key} collapsable={false} // Need collapsable=false here to clip the elevations
35
- style={[StyleSheet.absoluteFill, { zIndex: 1000 + i, pointerEvents: 'box-none' }]}>
33
+ {state.portals.map(({ key, children }) => (<Fragment key={key}>
36
34
  {children}
37
- </View>))}
35
+ </Fragment>))}
38
36
  </>);
39
37
  });
40
38
  export default _PortalManager;