@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.
- package/lib/runtime/components/react/dist/context.js +5 -1
- package/lib/runtime/components/react/dist/event.config.js +0 -1
- package/lib/runtime/components/react/dist/getInnerListeners.js +148 -149
- package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +145 -0
- package/lib/runtime/components/react/dist/mpx-button.jsx +11 -7
- package/lib/runtime/components/react/dist/mpx-canvas/Image.js +2 -4
- package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +23 -21
- package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +9 -4
- package/lib/runtime/components/react/dist/mpx-checkbox.jsx +9 -5
- package/lib/runtime/components/react/dist/mpx-form.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/index.jsx +9 -4
- package/lib/runtime/components/react/dist/mpx-image.jsx +92 -41
- package/lib/runtime/components/react/dist/mpx-inline-text.jsx +11 -0
- package/lib/runtime/components/react/dist/mpx-input.jsx +14 -13
- package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +22 -7
- package/lib/runtime/components/react/dist/mpx-label.jsx +9 -5
- package/lib/runtime/components/react/dist/mpx-movable-area.jsx +10 -5
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +206 -80
- package/lib/runtime/components/react/dist/mpx-navigator.jsx +11 -3
- package/lib/runtime/components/react/dist/mpx-picker/date.jsx +194 -68
- package/lib/runtime/components/react/dist/mpx-picker/dateData.js +17 -0
- package/lib/runtime/components/react/dist/mpx-picker/index.jsx +178 -98
- package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +79 -139
- package/lib/runtime/components/react/dist/mpx-picker/region.jsx +190 -90
- package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +60 -75
- package/lib/runtime/components/react/dist/mpx-picker/time.jsx +100 -228
- package/lib/runtime/components/react/dist/{mpx-picker-view.jsx → mpx-picker-view/index.jsx} +16 -15
- package/lib/runtime/components/react/dist/{mpx-picker-view-column.jsx → mpx-picker-view-column/index.jsx} +95 -26
- package/lib/runtime/components/react/dist/{mpx-picker-view-column-item.jsx → mpx-picker-view-column/pickerViewColumnItem.jsx} +16 -16
- package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItemLite.jsx +20 -0
- package/lib/runtime/components/react/dist/{pickerFaces.js → mpx-picker-view-column/pickerViewFaces.js} +6 -0
- package/lib/runtime/components/react/dist/mpx-popup/index.jsx +61 -0
- package/lib/runtime/components/react/dist/mpx-popup/popupBase.jsx +92 -0
- package/lib/runtime/components/react/dist/mpx-portal/index.jsx +5 -1
- package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +3 -5
- package/lib/runtime/components/react/dist/mpx-progress.jsx +163 -0
- package/lib/runtime/components/react/dist/mpx-radio-group.jsx +11 -4
- package/lib/runtime/components/react/dist/mpx-radio.jsx +9 -5
- package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +12 -4
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +317 -89
- package/lib/runtime/components/react/dist/mpx-simple-text.jsx +7 -5
- package/lib/runtime/components/react/dist/mpx-simple-view.jsx +11 -15
- package/lib/runtime/components/react/dist/mpx-slider.jsx +321 -0
- package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +117 -0
- package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
- package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +15 -14
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +245 -121
- package/lib/runtime/components/react/dist/mpx-switch.jsx +10 -7
- package/lib/runtime/components/react/dist/mpx-text.jsx +43 -13
- package/lib/runtime/components/react/dist/mpx-video.jsx +12 -7
- package/lib/runtime/components/react/dist/mpx-view.jsx +34 -18
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +40 -35
- package/lib/runtime/components/react/dist/useAnimationHooks.js +35 -90
- package/lib/runtime/components/react/dist/utils.jsx +215 -109
- package/lib/runtime/components/web/mpx-titlebar.vue +21 -18
- package/package.json +1 -1
- /package/lib/runtime/components/react/dist/{pickerVIewContext.js → mpx-picker-view/pickerVIewContext.js} +0 -0
- /package/lib/runtime/components/react/dist/{pickerViewIndicator.jsx → mpx-picker-view-column/pickerViewIndicator.jsx} +0 -0
- /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
|
-
*
|
|
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 {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
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(
|
|
48
|
-
const
|
|
49
|
-
const
|
|
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(
|
|
62
|
-
const
|
|
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 =
|
|
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
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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 (
|
|
107
|
-
setTimeout(
|
|
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 =
|
|
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(`#${
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
194
|
-
const {
|
|
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 {
|
|
217
|
-
const {
|
|
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
|
|
251
|
-
|
|
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
|
-
|
|
260
|
-
|
|
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
|
|
270
|
-
|
|
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
|
-
|
|
273
|
-
|
|
356
|
+
binddragstart &&
|
|
357
|
+
binddragstart(getCustomEvent('dragstart', e, {
|
|
274
358
|
detail: {
|
|
275
|
-
scrollLeft: scrollOptions.current.scrollLeft
|
|
276
|
-
scrollTop: scrollOptions.current.scrollTop
|
|
359
|
+
scrollLeft: scrollOptions.current.scrollLeft,
|
|
360
|
+
scrollTop: scrollOptions.current.scrollTop
|
|
277
361
|
},
|
|
278
362
|
layoutRef
|
|
279
363
|
}, props));
|
|
280
364
|
}
|
|
281
365
|
}
|
|
282
|
-
function
|
|
283
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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(
|
|
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:
|
|
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
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
},
|
|
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
|
|
5
|
-
const { allowFontScaling = false } = props;
|
|
6
|
-
|
|
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
|
-
|
|
11
|
-
export default
|
|
12
|
+
SimpleText.displayName = 'MpxSimpleText';
|
|
13
|
+
export default SimpleText;
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { View } from 'react-native';
|
|
2
|
-
import { createElement
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
const
|
|
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
|
-
|
|
10
|
-
style: innerStyle
|
|
11
|
-
});
|
|
12
|
-
return createElement(View,
|
|
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
|
-
|
|
22
|
-
export default
|
|
16
|
+
};
|
|
17
|
+
SimpleView.displayName = 'MpxSimpleView';
|
|
18
|
+
export default SimpleView;
|