@mpxjs/webpack-plugin 2.10.3-beta.17 → 2.10.3-beta.18

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 (68) hide show
  1. package/lib/runtime/components/react/dist/context.js +5 -1
  2. package/lib/runtime/components/react/dist/event.config.js +0 -1
  3. package/lib/runtime/components/react/dist/getInnerListeners.js +148 -149
  4. package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +145 -0
  5. package/lib/runtime/components/react/dist/mpx-button.jsx +11 -7
  6. package/lib/runtime/components/react/dist/mpx-canvas/Image.js +2 -4
  7. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +23 -21
  8. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +9 -4
  9. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +9 -5
  10. package/lib/runtime/components/react/dist/mpx-form.jsx +2 -2
  11. package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
  12. package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
  13. package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
  14. package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
  15. package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
  16. package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
  17. package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
  18. package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
  19. package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
  20. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +9 -4
  21. package/lib/runtime/components/react/dist/mpx-image.jsx +92 -41
  22. package/lib/runtime/components/react/dist/mpx-inline-text.jsx +11 -0
  23. package/lib/runtime/components/react/dist/mpx-input.jsx +14 -13
  24. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +22 -7
  25. package/lib/runtime/components/react/dist/mpx-label.jsx +9 -5
  26. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +10 -5
  27. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +206 -80
  28. package/lib/runtime/components/react/dist/mpx-navigator.jsx +11 -3
  29. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +194 -68
  30. package/lib/runtime/components/react/dist/mpx-picker/dateData.js +17 -0
  31. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +178 -98
  32. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +79 -139
  33. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +190 -90
  34. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +60 -75
  35. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +100 -228
  36. package/lib/runtime/components/react/dist/{mpx-picker-view.jsx → mpx-picker-view/index.jsx} +16 -15
  37. package/lib/runtime/components/react/dist/{mpx-picker-view-column.jsx → mpx-picker-view-column/index.jsx} +95 -26
  38. package/lib/runtime/components/react/dist/{mpx-picker-view-column-item.jsx → mpx-picker-view-column/pickerViewColumnItem.jsx} +16 -16
  39. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItemLite.jsx +20 -0
  40. package/lib/runtime/components/react/dist/{pickerFaces.js → mpx-picker-view-column/pickerViewFaces.js} +6 -0
  41. package/lib/runtime/components/react/dist/mpx-popup/index.jsx +61 -0
  42. package/lib/runtime/components/react/dist/mpx-popup/popupBase.jsx +92 -0
  43. package/lib/runtime/components/react/dist/mpx-portal/index.jsx +5 -1
  44. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +3 -5
  45. package/lib/runtime/components/react/dist/mpx-progress.jsx +163 -0
  46. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +11 -4
  47. package/lib/runtime/components/react/dist/mpx-radio.jsx +9 -5
  48. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +12 -4
  49. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +317 -89
  50. package/lib/runtime/components/react/dist/mpx-simple-text.jsx +7 -5
  51. package/lib/runtime/components/react/dist/mpx-simple-view.jsx +11 -15
  52. package/lib/runtime/components/react/dist/mpx-slider.jsx +321 -0
  53. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +117 -0
  54. package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
  55. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +15 -14
  56. package/lib/runtime/components/react/dist/mpx-swiper.jsx +245 -121
  57. package/lib/runtime/components/react/dist/mpx-switch.jsx +10 -7
  58. package/lib/runtime/components/react/dist/mpx-text.jsx +43 -13
  59. package/lib/runtime/components/react/dist/mpx-video.jsx +12 -7
  60. package/lib/runtime/components/react/dist/mpx-view.jsx +34 -18
  61. package/lib/runtime/components/react/dist/mpx-web-view.jsx +40 -35
  62. package/lib/runtime/components/react/dist/useAnimationHooks.js +35 -90
  63. package/lib/runtime/components/react/dist/utils.jsx +215 -109
  64. package/lib/runtime/components/web/mpx-titlebar.vue +21 -18
  65. package/package.json +1 -1
  66. /package/lib/runtime/components/react/dist/{pickerVIewContext.js → mpx-picker-view/pickerVIewContext.js} +0 -0
  67. /package/lib/runtime/components/react/dist/{pickerViewIndicator.jsx → mpx-picker-view-column/pickerViewIndicator.jsx} +0 -0
  68. /package/lib/runtime/components/react/dist/{pickerViewMask.jsx → mpx-picker-view-column/pickerViewMask.jsx} +0 -0
@@ -10,7 +10,7 @@
10
10
  * ✔ enable-back-to-top
11
11
  * ✘ enable-passive
12
12
  * ✔ refresher-enabled
13
- * refresher-threshold
13
+ * refresher-threshold(仅自定义下拉节点样式支持)
14
14
  * ✔ refresher-default-style(仅 android 支持)
15
15
  * ✔ refresher-background(仅 android 支持)
16
16
  * ✔ refresher-triggered
@@ -31,22 +31,31 @@
31
31
  * ✔ bindscrolltolower
32
32
  * ✔ bindscroll
33
33
  */
34
- import { ScrollView, RefreshControl } from 'react-native-gesture-handler';
35
- import { useRef, useState, useEffect, forwardRef, useContext, createElement, useMemo } from 'react';
36
- import { useAnimatedRef } from 'react-native-reanimated';
37
- import { warn } from '@mpxjs/utils';
34
+ import { ScrollView, RefreshControl, Gesture, GestureDetector } from 'react-native-gesture-handler';
35
+ import { Animated as RNAnimated } from 'react-native';
36
+ import { isValidElement, Children, useRef, useState, useEffect, forwardRef, useContext, useMemo, createElement } from 'react';
37
+ import Animated, { useSharedValue, withTiming, useAnimatedStyle, runOnJS } from 'react-native-reanimated';
38
+ import { warn, hasOwn } from '@mpxjs/utils';
38
39
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
39
40
  import useNodesRef from './useNodesRef';
40
- import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture } from './utils';
41
+ import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, HIDDEN_STYLE, useRunOnJSCallback } from './utils';
41
42
  import { IntersectionObserverContext, ScrollViewContext } from './context';
43
+ import Portal from './mpx-portal';
44
+ const AnimatedScrollView = RNAnimated.createAnimatedComponent(ScrollView);
42
45
  const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
43
46
  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, 'scroll-event-throttle': scrollEventThrottle = 0, __selectRef } = props;
47
+ 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, 'refresher-threshold': refresherThreshold = 45, '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, 'enable-sticky': enableSticky, 'scroll-event-throttle': scrollEventThrottle = 0, 'scroll-into-view-offset': scrollIntoViewOffset = 0, __selectRef } = props;
48
+ const scrollOffset = useRef(new RNAnimated.Value(0)).current;
45
49
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
46
50
  const waitForHandlers = flatGesture(waitFor);
47
- const [refreshing, setRefreshing] = useState(true);
48
- const snapScrollTop = useRef(0);
49
- const snapScrollLeft = useRef(0);
51
+ const [refreshing, setRefreshing] = useState(false);
52
+ const [enableScroll, setEnableScroll] = useState(true);
53
+ const enableScrollValue = useSharedValue(true);
54
+ const [scrollBounces, setScrollBounces] = useState(false);
55
+ const bouncesValue = useSharedValue(!!false);
56
+ const translateY = useSharedValue(0);
57
+ const isAtTop = useSharedValue(true);
58
+ const refresherHeight = useSharedValue(0);
50
59
  const scrollOptions = useRef({
51
60
  contentLength: 0,
52
61
  offset: 0,
@@ -58,10 +67,34 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
58
67
  const hasCallScrollToLower = useRef(false);
59
68
  const initialTimeout = useRef(null);
60
69
  const intersectionObservers = useContext(IntersectionObserverContext);
61
- const firstScrollIntoViewChange = useRef(false);
62
- const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
70
+ const firstScrollIntoViewChange = useRef(true);
71
+ const refreshColor = {
72
+ black: ['#000'],
73
+ white: ['#fff']
74
+ };
75
+ const isContentSizeChange = useRef(false);
76
+ const { refresherContent, otherContent } = getRefresherContent(props.children);
77
+ const hasRefresher = refresherContent && refresherEnabled;
78
+ const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, hasPositionFixed, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
63
79
  const { textStyle, innerStyle = {} } = splitStyle(normalStyle);
64
- const scrollViewRef = useAnimatedRef();
80
+ const scrollViewRef = useRef(null);
81
+ const propsRef = useRef(props);
82
+ const refresherStateRef = useRef({
83
+ hasRefresher,
84
+ refresherTriggered
85
+ });
86
+ propsRef.current = props;
87
+ refresherStateRef.current = {
88
+ hasRefresher,
89
+ refresherTriggered
90
+ };
91
+ const runOnJSCallbackRef = useRef({
92
+ setEnableScroll,
93
+ setScrollBounces,
94
+ setRefreshing,
95
+ onRefresh
96
+ });
97
+ const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef);
65
98
  useNodesRef(props, ref, scrollViewRef, {
66
99
  style: normalStyle,
67
100
  scrollOffset: scrollOptions,
@@ -72,56 +105,73 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
72
105
  pagingEnabled,
73
106
  fastDeceleration: false,
74
107
  decelerationDisabled: false,
75
- scrollTo
108
+ scrollTo,
109
+ scrollIntoView: handleScrollIntoView
76
110
  },
77
111
  gestureRef: scrollViewRef
78
112
  });
113
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
79
114
  const contextValue = useMemo(() => {
80
115
  return {
81
- gestureRef: scrollViewRef
116
+ gestureRef: scrollViewRef,
117
+ scrollOffset
82
118
  };
83
119
  }, []);
84
- const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
120
+ const hasRefresherLayoutRef = useRef(false);
121
+ // layout 完成前先隐藏,避免安卓闪烁问题
122
+ const refresherLayoutStyle = useMemo(() => { return !hasRefresherLayoutRef.current ? HIDDEN_STYLE : {}; }, [hasRefresherLayoutRef.current]);
85
123
  const lastOffset = useRef(0);
86
124
  if (scrollX && scrollY) {
87
125
  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
126
  }
89
127
  useEffect(() => {
90
- if (snapScrollTop.current !== scrollTop || snapScrollLeft.current !== scrollLeft) {
91
- initialTimeout.current = setTimeout(() => {
92
- scrollToOffset(scrollLeft, scrollTop);
93
- }, 0);
94
- return () => {
95
- initialTimeout.current && clearTimeout(initialTimeout.current);
96
- };
97
- }
128
+ initialTimeout.current = setTimeout(() => {
129
+ scrollToOffset(scrollLeft, scrollTop);
130
+ }, 0);
131
+ return () => {
132
+ initialTimeout.current && clearTimeout(initialTimeout.current);
133
+ };
98
134
  }, [scrollTop, scrollLeft]);
99
- useEffect(() => {
100
- if (refreshing !== refresherTriggered) {
101
- setRefreshing(!!refresherTriggered);
102
- }
103
- }, [refresherTriggered]);
104
135
  useEffect(() => {
105
136
  if (scrollIntoView && __selectRef) {
106
- if (!firstScrollIntoViewChange.current) {
107
- setTimeout(handleScrollIntoView);
137
+ if (firstScrollIntoViewChange.current) {
138
+ setTimeout(() => {
139
+ handleScrollIntoView(scrollIntoView, { offset: scrollIntoViewOffset, animated: scrollWithAnimation });
140
+ });
108
141
  }
109
142
  else {
110
- handleScrollIntoView();
143
+ handleScrollIntoView(scrollIntoView, { offset: scrollIntoViewOffset, animated: scrollWithAnimation });
111
144
  }
112
145
  }
113
- firstScrollIntoViewChange.current = true;
146
+ firstScrollIntoViewChange.current = false;
114
147
  }, [scrollIntoView]);
148
+ useEffect(() => {
149
+ if (refresherEnabled) {
150
+ setRefreshing(!!refresherTriggered);
151
+ if (!refresherContent)
152
+ return;
153
+ if (refresherTriggered) {
154
+ translateY.value = withTiming(refresherHeight.value);
155
+ resetScrollState(false);
156
+ }
157
+ else {
158
+ translateY.value = withTiming(0);
159
+ resetScrollState(true);
160
+ }
161
+ }
162
+ }, [refresherTriggered]);
115
163
  function scrollTo({ top = 0, left = 0, animated = false }) {
116
164
  scrollToOffset(left, top, animated);
117
165
  }
118
- function handleScrollIntoView() {
119
- const refs = __selectRef(`#${scrollIntoView}`, 'node');
166
+ function handleScrollIntoView(selector = '', { offset = 0, animated = true } = {}) {
167
+ const refs = __selectRef(`#${selector}`, 'node');
120
168
  if (!refs)
121
169
  return;
122
170
  const { nodeRef } = refs.getNodeInstance();
123
171
  nodeRef.current?.measureLayout(scrollViewRef.current, (left, top) => {
124
- scrollToOffset(left, top);
172
+ const adjustedLeft = scrollX ? left + offset : left;
173
+ const adjustedTop = scrollY ? top + offset : top;
174
+ scrollToOffset(adjustedLeft, adjustedTop, animated);
125
175
  });
126
176
  }
127
177
  function selectLength(size) {
@@ -170,7 +220,22 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
170
220
  }
171
221
  }
172
222
  function onContentSizeChange(width, height) {
173
- scrollOptions.current.contentLength = selectLength({ height, width });
223
+ isContentSizeChange.current = true;
224
+ const newContentLength = selectLength({ height, width });
225
+ const oldContentLength = scrollOptions.current.contentLength;
226
+ scrollOptions.current.contentLength = newContentLength;
227
+ // 内容高度变化时,Animated.event 的映射可能会有不生效的场景,所以需要手动设置一下 scrollOffset 的值
228
+ if (enableSticky && (__mpx_mode__ === 'android' || __mpx_mode__ === 'ios')) {
229
+ // 当内容变少时,检查当前滚动位置是否超出新的内容范围
230
+ if (newContentLength < oldContentLength) {
231
+ const { visibleLength, offset } = scrollOptions.current;
232
+ const maxOffset = Math.max(0, newContentLength - visibleLength);
233
+ // 如果当前滚动位置超出了新的内容范围,调整滚动offset
234
+ if (offset > maxOffset && scrollY) {
235
+ scrollOffset.setValue(maxOffset);
236
+ }
237
+ }
238
+ }
174
239
  }
175
240
  function onLayout(e) {
176
241
  const layout = e.nativeEvent.layout || {};
@@ -190,8 +255,10 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
190
255
  }
191
256
  function onScroll(e) {
192
257
  const { bindscroll } = props;
193
- const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset;
194
- const { width: scrollWidth, height: scrollHeight } = e.nativeEvent.contentSize;
258
+ const { contentOffset, layoutMeasurement, contentSize } = e.nativeEvent;
259
+ const { x: scrollLeft, y: scrollTop } = contentOffset;
260
+ const { width: scrollWidth, height: scrollHeight } = contentSize;
261
+ isAtTop.value = scrollTop <= 0;
195
262
  bindscroll &&
196
263
  bindscroll(getCustomEvent('scroll', e, {
197
264
  detail: {
@@ -200,7 +267,8 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
200
267
  scrollHeight,
201
268
  scrollWidth,
202
269
  deltaX: scrollLeft - scrollOptions.current.scrollLeft,
203
- deltaY: scrollTop - scrollOptions.current.scrollTop
270
+ deltaY: scrollTop - scrollOptions.current.scrollTop,
271
+ layoutMeasurement
204
272
  },
205
273
  layoutRef
206
274
  }, props));
@@ -213,15 +281,18 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
213
281
  }
214
282
  function onScrollEnd(e) {
215
283
  const { bindscrollend } = props;
216
- const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset;
217
- const { width: scrollWidth, height: scrollHeight } = e.nativeEvent.contentSize;
284
+ const { contentOffset, layoutMeasurement, contentSize } = e.nativeEvent;
285
+ const { x: scrollLeft, y: scrollTop } = contentOffset;
286
+ const { width: scrollWidth, height: scrollHeight } = contentSize;
287
+ isAtTop.value = scrollTop <= 0;
218
288
  bindscrollend &&
219
289
  bindscrollend(getCustomEvent('scrollend', e, {
220
290
  detail: {
221
291
  scrollLeft,
222
292
  scrollTop,
223
293
  scrollHeight,
224
- scrollWidth
294
+ scrollWidth,
295
+ layoutMeasurement
225
296
  },
226
297
  layoutRef
227
298
  }, props));
@@ -241,47 +312,61 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
241
312
  function scrollToOffset(x = 0, y = 0, animated = scrollWithAnimation) {
242
313
  if (scrollViewRef.current) {
243
314
  scrollViewRef.current.scrollTo({ x, y, animated });
244
- scrollOptions.current.scrollLeft = x;
245
- scrollOptions.current.scrollTop = y;
246
- snapScrollLeft.current = x;
247
- snapScrollTop.current = y;
248
315
  }
249
316
  }
250
- function onRefresh() {
251
- const { bindrefresherrefresh } = props;
252
- bindrefresherrefresh &&
253
- bindrefresherrefresh(getCustomEvent('refresherrefresh', {}, { layoutRef }, props));
254
- }
255
- function onScrollTouchStart(e) {
256
- const { bindtouchstart } = props;
257
- bindtouchstart && bindtouchstart(e);
317
+ function onScrollTouchMove(e) {
318
+ bindtouchmove && bindtouchmove(e);
258
319
  if (enhanced) {
259
- binddragstart &&
260
- binddragstart(getCustomEvent('dragstart', e, {
320
+ binddragging &&
321
+ binddragging(getCustomEvent('dragging', e, {
261
322
  detail: {
262
- scrollLeft: scrollOptions.current.scrollLeft,
263
- scrollTop: scrollOptions.current.scrollTop
323
+ scrollLeft: scrollOptions.current.scrollLeft || 0,
324
+ scrollTop: scrollOptions.current.scrollTop || 0
264
325
  },
265
326
  layoutRef
266
327
  }, props));
267
328
  }
268
329
  }
269
- function onScrollTouchMove(e) {
270
- bindtouchmove && bindtouchmove(e);
330
+ function onScrollDrag(e) {
331
+ const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset;
332
+ updateScrollOptions(e, { scrollLeft, scrollTop });
333
+ updateIntersection();
334
+ }
335
+ const scrollHandler = RNAnimated.event([{ nativeEvent: { contentOffset: { y: scrollOffset } } }], {
336
+ useNativeDriver: true,
337
+ listener: (event) => {
338
+ const y = event.nativeEvent.contentOffset.y || 0;
339
+ // 内容高度变化时,鸿蒙中 listener 回调通过scrollOffset.__getValue获取值一直等于event.nativeEvent.contentOffset.y,值是正确的,但是无法触发 sticky 动画执行,所以需要手动再 set 一次
340
+ if (__mpx_mode__ === 'harmony') {
341
+ if (isContentSizeChange.current) {
342
+ scrollOffset.setValue(y);
343
+ setTimeout(() => {
344
+ isContentSizeChange.current = false;
345
+ }, 100);
346
+ }
347
+ }
348
+ onScroll(event);
349
+ }
350
+ });
351
+ function onScrollDragStart(e) {
352
+ hasCallScrollToLower.current = false;
353
+ hasCallScrollToUpper.current = false;
354
+ onScrollDrag(e);
271
355
  if (enhanced) {
272
- binddragging &&
273
- binddragging(getCustomEvent('dragging', e, {
356
+ binddragstart &&
357
+ binddragstart(getCustomEvent('dragstart', e, {
274
358
  detail: {
275
- scrollLeft: scrollOptions.current.scrollLeft || 0,
276
- scrollTop: scrollOptions.current.scrollTop || 0
359
+ scrollLeft: scrollOptions.current.scrollLeft,
360
+ scrollTop: scrollOptions.current.scrollTop
277
361
  },
278
362
  layoutRef
279
363
  }, props));
280
364
  }
281
365
  }
282
- function onScrollTouchEnd(e) {
283
- bindtouchend && bindtouchend(e);
366
+ function onScrollDragEnd(e) {
367
+ onScrollDrag(e);
284
368
  if (enhanced) {
369
+ // 安卓上如果触发了默认的下拉刷新,binddragend可能不触发,只会触发 binddragstart
285
370
  binddragend &&
286
371
  binddragend(getCustomEvent('dragend', e, {
287
372
  detail: {
@@ -292,18 +377,151 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
292
377
  }, props));
293
378
  }
294
379
  }
295
- function onScrollDrag(e) {
296
- const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset;
297
- updateScrollOptions(e, { scrollLeft, scrollTop });
298
- updateIntersection();
380
+ // 处理刷新
381
+ function onRefresh() {
382
+ const { hasRefresher, refresherTriggered } = refresherStateRef.current;
383
+ if (hasRefresher && refresherTriggered === undefined) {
384
+ // 处理使用了自定义刷新组件,又没设置 refresherTriggered 的情况
385
+ setRefreshing(true);
386
+ setTimeout(() => {
387
+ setRefreshing(false);
388
+ translateY.value = withTiming(0);
389
+ if (!enableScrollValue.value) {
390
+ resetScrollState(true);
391
+ }
392
+ }, 500);
393
+ }
394
+ const { bindrefresherrefresh } = propsRef.current;
395
+ bindrefresherrefresh &&
396
+ bindrefresherrefresh(getCustomEvent('refresherrefresh', {}, { layoutRef }, propsRef.current));
299
397
  }
300
- function onScrollDragStart(e) {
301
- hasCallScrollToLower.current = false;
302
- hasCallScrollToUpper.current = false;
303
- onScrollDrag(e);
398
+ function getRefresherContent(children) {
399
+ let refresherContent = null;
400
+ const otherContent = [];
401
+ Children.forEach(children, (child) => {
402
+ if (isValidElement(child) && child.props.slot === 'refresher') {
403
+ refresherContent = child;
404
+ }
405
+ else {
406
+ otherContent.push(child);
407
+ }
408
+ });
409
+ return {
410
+ refresherContent,
411
+ otherContent
412
+ };
413
+ }
414
+ // 刷新控件的动画样式
415
+ const refresherAnimatedStyle = useAnimatedStyle(() => {
416
+ return {
417
+ position: 'absolute',
418
+ left: 0,
419
+ right: 0,
420
+ top: -refresherHeight.value,
421
+ transform: [{ translateY: Math.min(translateY.value, refresherHeight.value) }],
422
+ backgroundColor: refresherBackground || 'transparent'
423
+ };
424
+ });
425
+ // 内容区域的动画样式 - 只有内容区域需要下移
426
+ const contentAnimatedStyle = useAnimatedStyle(() => {
427
+ return {
428
+ transform: [{
429
+ translateY: translateY.value > refresherHeight.value
430
+ ? refresherHeight.value
431
+ : translateY.value
432
+ }]
433
+ };
434
+ });
435
+ function onRefresherLayout(e) {
436
+ const { height } = e.nativeEvent.layout;
437
+ refresherHeight.value = height;
438
+ hasRefresherLayoutRef.current = true;
439
+ }
440
+ function updateScrollState(newValue) {
441
+ 'worklet';
442
+ if (enableScrollValue.value !== newValue) {
443
+ enableScrollValue.value = newValue;
444
+ runOnJS(runOnJSCallback)('setEnableScroll', newValue);
445
+ }
304
446
  }
447
+ const resetScrollState = (value) => {
448
+ enableScrollValue.value = value;
449
+ setEnableScroll(value);
450
+ };
451
+ function updateBouncesState(newValue) {
452
+ 'worklet';
453
+ if (bouncesValue.value !== newValue) {
454
+ bouncesValue.value = newValue;
455
+ runOnJS(runOnJSCallback)('setScrollBounces', newValue);
456
+ }
457
+ }
458
+ // 处理下拉刷新的手势
459
+ const panGesture = Gesture.Pan()
460
+ .onUpdate((event) => {
461
+ 'worklet';
462
+ if (enhanced && !!bounces) {
463
+ if (event.translationY > 0 && bouncesValue.value) {
464
+ updateBouncesState(false);
465
+ }
466
+ else if ((event.translationY < 0) && !bouncesValue.value) {
467
+ updateBouncesState(true);
468
+ }
469
+ }
470
+ if (translateY.value <= 0 && event.translationY < 0) {
471
+ // 滑动到顶再向上开启滚动
472
+ updateScrollState(true);
473
+ }
474
+ else if (event.translationY > 0 && isAtTop.value) {
475
+ // 滚动到顶再向下禁止滚动
476
+ updateScrollState(false);
477
+ }
478
+ // 禁止滚动后切换为滑动
479
+ if (!enableScrollValue.value && isAtTop.value) {
480
+ if (refreshing) {
481
+ // 从完全展开状态(refresherHeight.value)开始计算偏移
482
+ translateY.value = Math.max(0, Math.min(refresherHeight.value, refresherHeight.value + event.translationY));
483
+ }
484
+ else if (event.translationY > 0) {
485
+ // 非刷新状态下的下拉逻辑保持不变
486
+ translateY.value = Math.min(event.translationY * 0.6, refresherHeight.value);
487
+ }
488
+ }
489
+ })
490
+ .onEnd((event) => {
491
+ 'worklet';
492
+ if (enableScrollValue.value)
493
+ return;
494
+ if (refreshing) {
495
+ // 刷新状态下,根据滑动距离决定是否隐藏
496
+ // 如果向下滑动没超过 refresherThreshold,就完全隐藏,如果向上滑动完全隐藏
497
+ if ((event.translationY > 0 && translateY.value < refresherThreshold) || event.translationY < 0) {
498
+ translateY.value = withTiming(0);
499
+ updateScrollState(true);
500
+ runOnJS(runOnJSCallback)('setRefreshing', false);
501
+ }
502
+ else {
503
+ translateY.value = withTiming(refresherHeight.value);
504
+ }
505
+ }
506
+ else if (event.translationY >= refresherHeight.value) {
507
+ // 触发刷新
508
+ translateY.value = withTiming(refresherHeight.value);
509
+ runOnJS(runOnJSCallback)('onRefresh');
510
+ }
511
+ else {
512
+ // 回弹
513
+ translateY.value = withTiming(0);
514
+ updateScrollState(true);
515
+ runOnJS(runOnJSCallback)('setRefreshing', false);
516
+ }
517
+ })
518
+ .simultaneousWithExternalGesture(scrollViewRef);
305
519
  const scrollAdditionalProps = extendObject({
306
- style: extendObject({}, innerStyle, layoutStyle),
520
+ style: extendObject(hasOwn(innerStyle, 'flex') || hasOwn(innerStyle, 'flexGrow')
521
+ ? {}
522
+ : {
523
+ flexGrow: 0
524
+ }, innerStyle, layoutStyle),
307
525
  pinchGestureEnabled: false,
308
526
  alwaysBounceVertical: false,
309
527
  alwaysBounceHorizontal: false,
@@ -312,24 +530,24 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
312
530
  scrollsToTop: enableBackToTop,
313
531
  showsHorizontalScrollIndicator: scrollX && showScrollbar,
314
532
  showsVerticalScrollIndicator: scrollY && showScrollbar,
315
- scrollEnabled: scrollX || scrollY,
533
+ scrollEnabled: !enableScroll ? false : !!(scrollX || scrollY),
534
+ bounces: false,
535
+ overScrollMode: 'never',
316
536
  ref: scrollViewRef,
317
- onScroll: onScroll,
537
+ onScroll: enableSticky ? scrollHandler : onScroll,
318
538
  onContentSizeChange: onContentSizeChange,
319
- bindtouchstart: ((enhanced && binddragstart) || bindtouchstart) && onScrollTouchStart,
320
539
  bindtouchmove: ((enhanced && binddragging) || bindtouchmove) && onScrollTouchMove,
321
- bindtouchend: ((enhanced && binddragend) || bindtouchend) && onScrollTouchEnd,
322
540
  onScrollBeginDrag: onScrollDragStart,
323
- onScrollEndDrag: onScrollDrag,
541
+ onScrollEndDrag: onScrollDragEnd,
324
542
  onMomentumScrollEnd: onScrollEnd
325
543
  }, (simultaneousHandlers ? { simultaneousHandlers } : {}), (waitForHandlers ? { waitFor: waitForHandlers } : {}), layoutProps);
326
544
  if (enhanced) {
327
545
  Object.assign(scrollAdditionalProps, {
328
- bounces,
546
+ bounces: hasRefresher ? scrollBounces : !!bounces,
329
547
  pagingEnabled
330
548
  });
331
549
  }
332
- const innerProps = useInnerProps(props, scrollAdditionalProps, [
550
+ const innerProps = useInnerProps(extendObject({}, props, scrollAdditionalProps), [
333
551
  'id',
334
552
  'scroll-x',
335
553
  'scroll-y',
@@ -356,17 +574,22 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
356
574
  'bindscrolltolower',
357
575
  'bindrefresherrefresh'
358
576
  ], { layoutRef });
359
- const refreshColor = {
360
- black: ['#000'],
361
- white: ['#fff']
362
- };
363
- return createElement(ScrollView, extendObject({}, innerProps, {
577
+ const ScrollViewComponent = enableSticky ? AnimatedScrollView : ScrollView;
578
+ const withRefresherScrollView = createElement(GestureDetector, { gesture: panGesture }, createElement(ScrollViewComponent, innerProps, createElement(Animated.View, { style: [refresherAnimatedStyle, refresherLayoutStyle], onLayout: onRefresherLayout }, refresherContent), createElement(Animated.View, { style: contentAnimatedStyle }, createElement(ScrollViewContext.Provider, { value: contextValue }, wrapChildren(extendObject({}, props, { children: otherContent }), {
579
+ hasVarDec,
580
+ varContext: varContextRef.current,
581
+ textStyle,
582
+ textProps
583
+ })))));
584
+ const commonScrollView = createElement(ScrollViewComponent, extendObject({}, innerProps, {
364
585
  refreshControl: refresherEnabled
365
586
  ? createElement(RefreshControl, extendObject({
366
587
  progressBackgroundColor: refresherBackground,
367
588
  refreshing: refreshing,
368
589
  onRefresh: onRefresh
369
- }, (refresherDefaultStyle && refresherDefaultStyle !== 'none' ? { colors: refreshColor[refresherDefaultStyle] } : null)))
590
+ }, refresherDefaultStyle && refresherDefaultStyle !== 'none'
591
+ ? { colors: refreshColor[refresherDefaultStyle] }
592
+ : {}))
370
593
  : undefined
371
594
  }), createElement(ScrollViewContext.Provider, { value: contextValue }, wrapChildren(props, {
372
595
  hasVarDec,
@@ -374,6 +597,11 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
374
597
  textStyle,
375
598
  textProps
376
599
  })));
600
+ let scrollViewComponent = hasRefresher ? withRefresherScrollView : commonScrollView;
601
+ if (hasPositionFixed) {
602
+ scrollViewComponent = createElement(Portal, null, scrollViewComponent);
603
+ }
604
+ return scrollViewComponent;
377
605
  });
378
606
  _ScrollView.displayName = 'MpxScrollView';
379
607
  export default _ScrollView;
@@ -1,11 +1,13 @@
1
1
  import { Text } from 'react-native';
2
2
  import { createElement } from 'react';
3
+ import useInnerProps from './getInnerListeners';
3
4
  import { extendObject } from './utils';
4
- const _Text2 = (props) => {
5
- const { allowFontScaling = false } = props;
6
- return createElement(Text, extendObject({}, props, {
5
+ const SimpleText = (props) => {
6
+ const { allowFontScaling = false, children } = props;
7
+ const innerProps = useInnerProps(extendObject({}, props, {
7
8
  allowFontScaling
8
9
  }));
10
+ return createElement(Text, innerProps, children);
9
11
  };
10
- _Text2.displayName = 'MpxSimpleText';
11
- export default _Text2;
12
+ SimpleText.displayName = 'MpxSimpleText';
13
+ export default SimpleText;
@@ -1,22 +1,18 @@
1
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);
2
+ import { createElement } from 'react';
3
+ import { splitProps, splitStyle, wrapChildren, extendObject } from './utils';
4
+ import useInnerProps from './getInnerListeners';
5
+ const SimpleView = (simpleViewProps) => {
7
6
  const { textProps, innerProps: props = {} } = splitProps(simpleViewProps);
8
7
  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, {
8
+ const innerProps = useInnerProps(extendObject({}, props, {
9
+ style: innerStyle
10
+ }));
11
+ return createElement(View, innerProps, wrapChildren(props, {
16
12
  hasVarDec: false,
17
13
  textStyle: textStyle,
18
14
  textProps
19
15
  }));
20
- });
21
- _View2.displayName = 'MpxSimpleView';
22
- export default _View2;
16
+ };
17
+ SimpleView.displayName = 'MpxSimpleView';
18
+ export default SimpleView;