@mpxjs/webpack-plugin 2.10.6 → 2.10.7-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/dependencies/RecordPageConfigsMapDependency.js +1 -1
- package/lib/index.js +71 -51
- package/lib/parser.js +1 -1
- package/lib/platform/json/wx/index.js +0 -1
- package/lib/platform/style/wx/index.js +7 -0
- package/lib/platform/template/wx/component-config/button.js +1 -1
- package/lib/platform/template/wx/component-config/index.js +5 -1
- package/lib/platform/template/wx/component-config/input.js +1 -1
- package/lib/platform/template/wx/component-config/movable-view.js +1 -10
- package/lib/platform/template/wx/component-config/sticky-header.js +23 -0
- package/lib/platform/template/wx/component-config/sticky-section.js +23 -0
- package/lib/react/processJSON.js +2 -1
- package/lib/runtime/components/react/AsyncContainer.tsx +189 -0
- package/lib/runtime/components/react/context.ts +23 -4
- package/lib/runtime/components/react/dist/AsyncContainer.jsx +141 -0
- package/lib/runtime/components/react/dist/context.js +5 -2
- package/lib/runtime/components/react/dist/mpx-button.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-input.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-movable-area.jsx +64 -10
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +358 -98
- package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +3 -0
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +31 -15
- 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 +2 -2
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +53 -27
- package/lib/runtime/components/react/dist/mpx-view.jsx +21 -7
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +13 -13
- package/lib/runtime/components/react/dist/utils.jsx +94 -1
- package/lib/runtime/components/react/mpx-button.tsx +3 -2
- package/lib/runtime/components/react/mpx-input.tsx +1 -1
- package/lib/runtime/components/react/mpx-movable-area.tsx +99 -12
- package/lib/runtime/components/react/mpx-movable-view.tsx +413 -100
- package/lib/runtime/components/react/mpx-rich-text/index.tsx +3 -0
- package/lib/runtime/components/react/mpx-scroll-view.tsx +84 -59
- package/lib/runtime/components/react/mpx-sticky-header.tsx +181 -0
- package/lib/runtime/components/react/mpx-sticky-section.tsx +96 -0
- package/lib/runtime/components/react/mpx-swiper-item.tsx +2 -2
- package/lib/runtime/components/react/mpx-swiper.tsx +53 -25
- package/lib/runtime/components/react/mpx-view.tsx +20 -7
- package/lib/runtime/components/react/mpx-web-view.tsx +12 -12
- package/lib/runtime/components/react/utils.tsx +93 -1
- package/lib/runtime/components/web/mpx-scroll-view.vue +18 -4
- package/lib/runtime/components/web/mpx-sticky-header.vue +99 -0
- package/lib/runtime/components/web/mpx-sticky-section.vue +15 -0
- package/lib/runtime/optionProcessor.js +0 -2
- package/lib/script-setup-compiler/index.js +27 -5
- package/lib/template-compiler/bind-this.js +2 -1
- package/lib/template-compiler/compiler.js +4 -3
- package/package.json +4 -4
- package/LICENSE +0 -433
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
* ✔ bindscroll
|
|
33
33
|
*/
|
|
34
34
|
import { ScrollView, RefreshControl, Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
35
|
+
import { Animated as RNAnimated } from 'react-native';
|
|
35
36
|
import { isValidElement, Children, useRef, useState, useEffect, forwardRef, useContext, useMemo, createElement } from 'react';
|
|
36
37
|
import Animated, { useAnimatedRef, useSharedValue, withTiming, useAnimatedStyle, runOnJS } from 'react-native-reanimated';
|
|
37
38
|
import { warn, hasOwn } from '@mpxjs/utils';
|
|
@@ -40,9 +41,11 @@ import useNodesRef from './useNodesRef';
|
|
|
40
41
|
import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, HIDDEN_STYLE } from './utils';
|
|
41
42
|
import { IntersectionObserverContext, ScrollViewContext } from './context';
|
|
42
43
|
import Portal from './mpx-portal';
|
|
44
|
+
const AnimatedScrollView = RNAnimated.createAnimatedComponent(ScrollView);
|
|
43
45
|
const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
44
46
|
const { textProps, innerProps: props = {} } = splitProps(scrollViewProps);
|
|
45
|
-
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, '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;
|
|
46
49
|
const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
|
|
47
50
|
const waitForHandlers = flatGesture(waitFor);
|
|
48
51
|
const snapScrollTop = useRef(0);
|
|
@@ -66,7 +69,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
66
69
|
const hasCallScrollToLower = useRef(false);
|
|
67
70
|
const initialTimeout = useRef(null);
|
|
68
71
|
const intersectionObservers = useContext(IntersectionObserverContext);
|
|
69
|
-
const firstScrollIntoViewChange = useRef(
|
|
72
|
+
const firstScrollIntoViewChange = useRef(true);
|
|
70
73
|
const refreshColor = {
|
|
71
74
|
black: ['#000'],
|
|
72
75
|
white: ['#fff']
|
|
@@ -86,16 +89,18 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
86
89
|
pagingEnabled,
|
|
87
90
|
fastDeceleration: false,
|
|
88
91
|
decelerationDisabled: false,
|
|
89
|
-
scrollTo
|
|
92
|
+
scrollTo,
|
|
93
|
+
scrollIntoView: handleScrollIntoView
|
|
90
94
|
},
|
|
91
95
|
gestureRef: scrollViewRef
|
|
92
96
|
});
|
|
97
|
+
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
|
|
93
98
|
const contextValue = useMemo(() => {
|
|
94
99
|
return {
|
|
95
|
-
gestureRef: scrollViewRef
|
|
100
|
+
gestureRef: scrollViewRef,
|
|
101
|
+
scrollOffset
|
|
96
102
|
};
|
|
97
103
|
}, []);
|
|
98
|
-
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
|
|
99
104
|
const hasRefresherLayoutRef = useRef(false);
|
|
100
105
|
// layout 完成前先隐藏,避免安卓闪烁问题
|
|
101
106
|
const refresherLayoutStyle = useMemo(() => { return !hasRefresherLayoutRef.current ? HIDDEN_STYLE : {}; }, [hasRefresherLayoutRef.current]);
|
|
@@ -115,14 +120,16 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
115
120
|
}, [scrollTop, scrollLeft]);
|
|
116
121
|
useEffect(() => {
|
|
117
122
|
if (scrollIntoView && __selectRef) {
|
|
118
|
-
if (
|
|
119
|
-
setTimeout(
|
|
123
|
+
if (firstScrollIntoViewChange.current) {
|
|
124
|
+
setTimeout(() => {
|
|
125
|
+
handleScrollIntoView(scrollIntoView, { offset: scrollIntoViewOffset, animated: scrollWithAnimation });
|
|
126
|
+
});
|
|
120
127
|
}
|
|
121
128
|
else {
|
|
122
|
-
handleScrollIntoView();
|
|
129
|
+
handleScrollIntoView(scrollIntoView, { offset: scrollIntoViewOffset, animated: scrollWithAnimation });
|
|
123
130
|
}
|
|
124
131
|
}
|
|
125
|
-
firstScrollIntoViewChange.current =
|
|
132
|
+
firstScrollIntoViewChange.current = false;
|
|
126
133
|
}, [scrollIntoView]);
|
|
127
134
|
useEffect(() => {
|
|
128
135
|
if (refresherEnabled) {
|
|
@@ -142,13 +149,15 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
142
149
|
function scrollTo({ top = 0, left = 0, animated = false }) {
|
|
143
150
|
scrollToOffset(left, top, animated);
|
|
144
151
|
}
|
|
145
|
-
function handleScrollIntoView() {
|
|
146
|
-
const refs = __selectRef(`#${
|
|
152
|
+
function handleScrollIntoView(selector = '', { offset = 0, animated = true } = {}) {
|
|
153
|
+
const refs = __selectRef(`#${selector}`, 'node');
|
|
147
154
|
if (!refs)
|
|
148
155
|
return;
|
|
149
156
|
const { nodeRef } = refs.getNodeInstance();
|
|
150
157
|
nodeRef.current?.measureLayout(scrollViewRef.current, (left, top) => {
|
|
151
|
-
|
|
158
|
+
const adjustedLeft = scrollX ? left + offset : left;
|
|
159
|
+
const adjustedTop = scrollY ? top + offset : top;
|
|
160
|
+
scrollToOffset(adjustedLeft, adjustedTop, animated);
|
|
152
161
|
});
|
|
153
162
|
}
|
|
154
163
|
function selectLength(size) {
|
|
@@ -321,6 +330,12 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
321
330
|
updateScrollOptions(e, { scrollLeft, scrollTop });
|
|
322
331
|
updateIntersection();
|
|
323
332
|
}
|
|
333
|
+
const scrollHandler = RNAnimated.event([{ nativeEvent: { contentOffset: { y: scrollOffset } } }], {
|
|
334
|
+
useNativeDriver: true,
|
|
335
|
+
listener: (event) => {
|
|
336
|
+
onScroll(event);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
324
339
|
function onScrollDragStart(e) {
|
|
325
340
|
hasCallScrollToLower.current = false;
|
|
326
341
|
hasCallScrollToUpper.current = false;
|
|
@@ -481,7 +496,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
481
496
|
scrollEnabled: !enableScroll ? false : !!(scrollX || scrollY),
|
|
482
497
|
bounces: false,
|
|
483
498
|
ref: scrollViewRef,
|
|
484
|
-
onScroll: onScroll,
|
|
499
|
+
onScroll: enableSticky ? scrollHandler : onScroll,
|
|
485
500
|
onContentSizeChange: onContentSizeChange,
|
|
486
501
|
bindtouchstart: ((enhanced && binddragstart) || bindtouchstart) && onScrollTouchStart,
|
|
487
502
|
bindtouchmove: ((enhanced && binddragging) || bindtouchmove) && onScrollTouchMove,
|
|
@@ -523,13 +538,14 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
523
538
|
'bindscrolltolower',
|
|
524
539
|
'bindrefresherrefresh'
|
|
525
540
|
], { layoutRef });
|
|
526
|
-
const
|
|
541
|
+
const ScrollViewComponent = enableSticky ? AnimatedScrollView : ScrollView;
|
|
542
|
+
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 }), {
|
|
527
543
|
hasVarDec,
|
|
528
544
|
varContext: varContextRef.current,
|
|
529
545
|
textStyle,
|
|
530
546
|
textProps
|
|
531
547
|
})))));
|
|
532
|
-
const commonScrollView = createElement(
|
|
548
|
+
const commonScrollView = createElement(ScrollViewComponent, extendObject({}, innerProps, {
|
|
533
549
|
refreshControl: refresherEnabled
|
|
534
550
|
? createElement(RefreshControl, extendObject({
|
|
535
551
|
progressBackgroundColor: refresherBackground,
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { useEffect, useRef, useContext, forwardRef, useMemo, createElement, useId } from 'react';
|
|
2
|
+
import { Animated, StyleSheet, useAnimatedValue } from 'react-native';
|
|
3
|
+
import { ScrollViewContext, StickyContext } from './context';
|
|
4
|
+
import useNodesRef from './useNodesRef';
|
|
5
|
+
import { splitProps, splitStyle, useTransformStyle, wrapChildren, useLayout, extendObject } from './utils';
|
|
6
|
+
import { error } from '@mpxjs/utils';
|
|
7
|
+
import useInnerProps, { getCustomEvent } from './getInnerListeners';
|
|
8
|
+
const _StickyHeader = forwardRef((stickyHeaderProps = {}, ref) => {
|
|
9
|
+
const { textProps, innerProps: props = {} } = splitProps(stickyHeaderProps);
|
|
10
|
+
const { style, bindstickontopchange, padding = [0, 0, 0, 0], 'offset-top': offsetTop = 0, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
|
|
11
|
+
const scrollViewContext = useContext(ScrollViewContext);
|
|
12
|
+
const stickyContext = useContext(StickyContext);
|
|
13
|
+
const { scrollOffset } = scrollViewContext;
|
|
14
|
+
const { registerStickyHeader, unregisterStickyHeader } = stickyContext;
|
|
15
|
+
const headerRef = useRef(null);
|
|
16
|
+
const isStickOnTopRef = useRef(false);
|
|
17
|
+
const id = useId();
|
|
18
|
+
const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
19
|
+
const { layoutRef, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: headerRef, onLayout });
|
|
20
|
+
const { textStyle, innerStyle = {} } = splitStyle(normalStyle);
|
|
21
|
+
const headerTopAnimated = useAnimatedValue(0);
|
|
22
|
+
// harmony animatedValue 不支持通过 _value 访问
|
|
23
|
+
const headerTopRef = useRef(0);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
registerStickyHeader({ key: id, updatePosition });
|
|
26
|
+
return () => {
|
|
27
|
+
unregisterStickyHeader(id);
|
|
28
|
+
};
|
|
29
|
+
}, []);
|
|
30
|
+
function updatePosition() {
|
|
31
|
+
if (headerRef.current) {
|
|
32
|
+
const scrollViewRef = scrollViewContext.gestureRef;
|
|
33
|
+
if (scrollViewRef && scrollViewRef.current) {
|
|
34
|
+
headerRef.current.measureLayout(scrollViewRef.current, (left, top) => {
|
|
35
|
+
Animated.timing(headerTopAnimated, {
|
|
36
|
+
toValue: top,
|
|
37
|
+
duration: 0,
|
|
38
|
+
useNativeDriver: true
|
|
39
|
+
}).start();
|
|
40
|
+
headerTopRef.current = top;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
error('StickyHeader measureLayout error: scrollViewRef is not a valid native component reference');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function onLayout(e) {
|
|
49
|
+
updatePosition();
|
|
50
|
+
}
|
|
51
|
+
useNodesRef(props, ref, headerRef, {
|
|
52
|
+
style: normalStyle
|
|
53
|
+
});
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (!bindstickontopchange)
|
|
56
|
+
return;
|
|
57
|
+
const listener = scrollOffset.addListener((state) => {
|
|
58
|
+
const currentScrollValue = state.value;
|
|
59
|
+
const newIsStickOnTop = currentScrollValue > headerTopRef.current;
|
|
60
|
+
if (newIsStickOnTop !== isStickOnTopRef.current) {
|
|
61
|
+
isStickOnTopRef.current = newIsStickOnTop;
|
|
62
|
+
bindstickontopchange(getCustomEvent('stickontopchange', {}, {
|
|
63
|
+
detail: {
|
|
64
|
+
isStickOnTop: newIsStickOnTop
|
|
65
|
+
},
|
|
66
|
+
layoutRef
|
|
67
|
+
}, props));
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return () => {
|
|
71
|
+
scrollOffset.removeListener(listener);
|
|
72
|
+
};
|
|
73
|
+
}, []);
|
|
74
|
+
const animatedStyle = useMemo(() => {
|
|
75
|
+
const translateY = Animated.subtract(scrollOffset, headerTopAnimated).interpolate({
|
|
76
|
+
inputRange: [0, 1],
|
|
77
|
+
outputRange: [0, 1],
|
|
78
|
+
extrapolateLeft: 'clamp',
|
|
79
|
+
extrapolateRight: 'extend'
|
|
80
|
+
});
|
|
81
|
+
const finalTranslateY = offsetTop === 0
|
|
82
|
+
? translateY
|
|
83
|
+
: Animated.add(translateY, Animated.subtract(scrollOffset, headerTopAnimated).interpolate({
|
|
84
|
+
inputRange: [0, 1],
|
|
85
|
+
outputRange: [0, offsetTop],
|
|
86
|
+
extrapolate: 'clamp'
|
|
87
|
+
}));
|
|
88
|
+
return {
|
|
89
|
+
transform: [{ translateY: finalTranslateY }]
|
|
90
|
+
};
|
|
91
|
+
}, [scrollOffset, headerTopAnimated, offsetTop]);
|
|
92
|
+
const innerProps = useInnerProps(extendObject({}, props, {
|
|
93
|
+
ref: headerRef,
|
|
94
|
+
style: extendObject({}, styles.content, innerStyle, animatedStyle, {
|
|
95
|
+
paddingTop: padding[0] || 0,
|
|
96
|
+
paddingRight: padding[1] || 0,
|
|
97
|
+
paddingBottom: padding[2] || 0,
|
|
98
|
+
paddingLeft: padding[3] || 0
|
|
99
|
+
})
|
|
100
|
+
}, layoutProps), [], { layoutRef });
|
|
101
|
+
return (createElement(Animated.View, innerProps, wrapChildren(props, {
|
|
102
|
+
hasVarDec,
|
|
103
|
+
varContext: varContextRef.current,
|
|
104
|
+
textStyle,
|
|
105
|
+
textProps
|
|
106
|
+
})));
|
|
107
|
+
});
|
|
108
|
+
const styles = StyleSheet.create({
|
|
109
|
+
content: {
|
|
110
|
+
width: '100%',
|
|
111
|
+
zIndex: 10,
|
|
112
|
+
// harmony 需要手动设置 relative, zIndex 才生效
|
|
113
|
+
position: 'relative'
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
_StickyHeader.displayName = 'MpxStickyHeader';
|
|
117
|
+
export default _StickyHeader;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useRef, forwardRef, createElement, useCallback, useMemo } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import useNodesRef from './useNodesRef';
|
|
4
|
+
import { splitProps, splitStyle, useTransformStyle, wrapChildren, useLayout, extendObject } from './utils';
|
|
5
|
+
import { StickyContext } from './context';
|
|
6
|
+
import useInnerProps from './getInnerListeners';
|
|
7
|
+
const _StickySection = forwardRef((stickySectionProps = {}, ref) => {
|
|
8
|
+
const { textProps, innerProps: props = {} } = splitProps(stickySectionProps);
|
|
9
|
+
const { style, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
|
|
10
|
+
const sectionRef = useRef(null);
|
|
11
|
+
const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
12
|
+
const { layoutRef, layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: sectionRef, onLayout });
|
|
13
|
+
const { textStyle, innerStyle = {} } = splitStyle(normalStyle);
|
|
14
|
+
const stickyHeaders = useRef(new Map());
|
|
15
|
+
const registerStickyHeader = useCallback((item) => {
|
|
16
|
+
stickyHeaders.current.set(item.id, item);
|
|
17
|
+
}, []);
|
|
18
|
+
const unregisterStickyHeader = useCallback((id) => {
|
|
19
|
+
stickyHeaders.current.delete(id);
|
|
20
|
+
}, []);
|
|
21
|
+
const contextValue = useMemo(() => ({
|
|
22
|
+
registerStickyHeader,
|
|
23
|
+
unregisterStickyHeader
|
|
24
|
+
}), []);
|
|
25
|
+
useNodesRef(props, ref, sectionRef, {
|
|
26
|
+
style: normalStyle
|
|
27
|
+
});
|
|
28
|
+
function onLayout() {
|
|
29
|
+
stickyHeaders.current.forEach(item => {
|
|
30
|
+
item.updatePosition();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const innerProps = useInnerProps(extendObject({}, props, {
|
|
34
|
+
style: extendObject(innerStyle, layoutStyle),
|
|
35
|
+
ref: sectionRef
|
|
36
|
+
}, layoutProps), [], { layoutRef });
|
|
37
|
+
return (createElement(View, innerProps, createElement(StickyContext.Provider, { value: contextValue }, wrapChildren(props, {
|
|
38
|
+
hasVarDec,
|
|
39
|
+
varContext: varContextRef.current,
|
|
40
|
+
textStyle,
|
|
41
|
+
textProps
|
|
42
|
+
}))));
|
|
43
|
+
});
|
|
44
|
+
_StickySection.displayName = 'MpxStickySection';
|
|
45
|
+
export default _StickySection;
|
|
@@ -2,7 +2,7 @@ import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated
|
|
|
2
2
|
import { forwardRef, useRef, useContext, createElement } from 'react';
|
|
3
3
|
import useInnerProps from './getInnerListeners';
|
|
4
4
|
import useNodesRef from './useNodesRef'; // 引入辅助函数
|
|
5
|
-
import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout, extendObject } from './utils';
|
|
5
|
+
import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout, extendObject, isHarmony } from './utils';
|
|
6
6
|
import { SwiperContext } from './context';
|
|
7
7
|
const _SwiperItem = forwardRef((props, ref) => {
|
|
8
8
|
const { 'enable-var': enableVar, 'external-var-context': externalVarContext, style, customStyle, itemIndex } = props;
|
|
@@ -29,7 +29,7 @@ const _SwiperItem = forwardRef((props, ref) => {
|
|
|
29
29
|
'style'
|
|
30
30
|
], { layoutRef });
|
|
31
31
|
const itemAnimatedStyle = useAnimatedStyle(() => {
|
|
32
|
-
if (!step.value)
|
|
32
|
+
if (!step.value && !isHarmony)
|
|
33
33
|
return {};
|
|
34
34
|
const inputRange = [step.value, 0];
|
|
35
35
|
const outputRange = [0.7, 1];
|
|
@@ -125,6 +125,10 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
125
125
|
const moveTranstion = useSharedValue(0);
|
|
126
126
|
// 记录从onBegin 到 onTouchesUp 的时间
|
|
127
127
|
const moveTime = useSharedValue(0);
|
|
128
|
+
// 记录从onBegin 到 onTouchesCancelled 另外一个方向移动的距离
|
|
129
|
+
const anotherDirectionMove = useSharedValue(0);
|
|
130
|
+
// 另一个方向的
|
|
131
|
+
const anotherAbso = 'absolute' + (dir === 'x' ? 'y' : 'x').toUpperCase();
|
|
128
132
|
const timerId = useRef(0);
|
|
129
133
|
const intervalTimer = props.interval || 500;
|
|
130
134
|
const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
|
|
@@ -405,7 +409,11 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
405
409
|
}
|
|
406
410
|
}, [children.length]);
|
|
407
411
|
useEffect(() => {
|
|
408
|
-
|
|
412
|
+
// 1. 如果用户在touch的过程中, 外部更新了current以外部为准(小程序表现)
|
|
413
|
+
// 2. 手指滑动过程中更新索引,外部会把current再穿进来,导致offset直接更新了
|
|
414
|
+
if (props.current !== currentIndex.value) {
|
|
415
|
+
updateCurrent(props.current || 0, step.value);
|
|
416
|
+
}
|
|
409
417
|
}, [props.current]);
|
|
410
418
|
useEffect(() => {
|
|
411
419
|
autoplayShared.value = autoplay;
|
|
@@ -468,20 +476,26 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
468
476
|
targetOffset: -moveToTargetPos
|
|
469
477
|
};
|
|
470
478
|
}
|
|
471
|
-
function
|
|
479
|
+
function checkUnCircular(eventData) {
|
|
472
480
|
'worklet';
|
|
473
481
|
const { translation } = eventData;
|
|
474
482
|
const currentOffset = Math.abs(offset.value);
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
483
|
+
// 向右滑动swiper
|
|
484
|
+
if (translation < 0) {
|
|
485
|
+
const boundaryOffset = step.value * (childrenLength.value - 1);
|
|
486
|
+
const gestureMovePos = Math.abs(translation) + currentOffset;
|
|
487
|
+
return {
|
|
488
|
+
// 防止快速连续向右滑动时,手势移动的距离 加 当前的offset超出边界
|
|
489
|
+
targetOffset: gestureMovePos > boundaryOffset ? -boundaryOffset : offset.value + translation,
|
|
490
|
+
canMove: currentOffset < boundaryOffset
|
|
491
|
+
};
|
|
482
492
|
}
|
|
483
493
|
else {
|
|
484
|
-
|
|
494
|
+
const gestureMovePos = currentOffset - translation;
|
|
495
|
+
return {
|
|
496
|
+
targetOffset: gestureMovePos < 0 ? 0 : offset.value + translation,
|
|
497
|
+
canMove: currentOffset > 0
|
|
498
|
+
};
|
|
485
499
|
}
|
|
486
500
|
}
|
|
487
501
|
function handleEnd(eventData) {
|
|
@@ -541,7 +555,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
541
555
|
}
|
|
542
556
|
});
|
|
543
557
|
}
|
|
544
|
-
function
|
|
558
|
+
function computeHalf() {
|
|
545
559
|
'worklet';
|
|
546
560
|
const currentOffset = Math.abs(offset.value);
|
|
547
561
|
let preOffset = (currentIndex.value + patchElmNumShared.value) * step.value;
|
|
@@ -551,6 +565,14 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
551
565
|
// 正常事件中拿到的transition值(正向滑动<0,倒着滑>0)
|
|
552
566
|
const diffOffset = preOffset - currentOffset;
|
|
553
567
|
const half = Math.abs(diffOffset) > step.value / 2;
|
|
568
|
+
return {
|
|
569
|
+
diffOffset,
|
|
570
|
+
half
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function handleLongPress() {
|
|
574
|
+
'worklet';
|
|
575
|
+
const { diffOffset, half } = computeHalf();
|
|
554
576
|
if (+diffOffset === 0) {
|
|
555
577
|
runOnJS(resumeLoop)();
|
|
556
578
|
}
|
|
@@ -610,19 +632,30 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
610
632
|
runOnJS(pauseLoop)();
|
|
611
633
|
preAbsolutePos.value = e[strAbso];
|
|
612
634
|
moveTranstion.value = e[strAbso];
|
|
635
|
+
anotherDirectionMove.value = e[anotherAbso];
|
|
613
636
|
moveTime.value = new Date().getTime();
|
|
614
637
|
})
|
|
615
|
-
.
|
|
638
|
+
.onUpdate((e) => {
|
|
616
639
|
'worklet';
|
|
617
640
|
if (touchfinish.value)
|
|
618
641
|
return;
|
|
619
|
-
const
|
|
620
|
-
const moveDistance = touchEventData[strAbso] - preAbsolutePos.value;
|
|
642
|
+
const moveDistance = e[strAbso] - preAbsolutePos.value;
|
|
621
643
|
const eventData = {
|
|
622
644
|
translation: moveDistance
|
|
623
645
|
};
|
|
624
|
-
//
|
|
625
|
-
|
|
646
|
+
// 1. 在Move过程中,如果手指一直没抬起来,超过一半的话也会更新索引
|
|
647
|
+
const { half } = computeHalf();
|
|
648
|
+
if (half) {
|
|
649
|
+
const { selectedIndex } = getTargetPosition(eventData);
|
|
650
|
+
currentIndex.value = selectedIndex;
|
|
651
|
+
}
|
|
652
|
+
// 2. 处理用户一直拖拽到临界点的场景, 不会执行onEnd
|
|
653
|
+
const { canMove, targetOffset } = checkUnCircular(eventData);
|
|
654
|
+
if (!circularShared.value) {
|
|
655
|
+
if (canMove) {
|
|
656
|
+
offset.value = targetOffset;
|
|
657
|
+
preAbsolutePos.value = e[strAbso];
|
|
658
|
+
}
|
|
626
659
|
return;
|
|
627
660
|
}
|
|
628
661
|
const { isBoundary, resetOffset } = reachBoundary(eventData);
|
|
@@ -632,30 +665,23 @@ const SwiperWrapper = forwardRef((props, ref) => {
|
|
|
632
665
|
else {
|
|
633
666
|
offset.value = moveDistance + offset.value;
|
|
634
667
|
}
|
|
635
|
-
preAbsolutePos.value =
|
|
668
|
+
preAbsolutePos.value = e[strAbso];
|
|
636
669
|
})
|
|
637
|
-
.
|
|
670
|
+
.onFinalize((e) => {
|
|
638
671
|
'worklet';
|
|
639
672
|
if (touchfinish.value)
|
|
640
673
|
return;
|
|
641
|
-
const
|
|
642
|
-
const moveDistance = touchEventData[strAbso] - moveTranstion.value;
|
|
674
|
+
const moveDistance = e[strAbso] - moveTranstion.value;
|
|
643
675
|
touchfinish.value = true;
|
|
644
676
|
const eventData = {
|
|
645
677
|
translation: moveDistance
|
|
646
678
|
};
|
|
647
|
-
if (childrenLength.value === 1) {
|
|
648
|
-
return handleBackInit();
|
|
649
|
-
}
|
|
650
|
-
// 用户手指按下起来, 需要计算正确的位置, 比如在滑动过程中突然按下然后起来,需要计算到正确的位置
|
|
651
|
-
if (!circularShared.value && !canMove(eventData)) {
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
679
|
const strVelocity = moveDistance / (new Date().getTime() - moveTime.value) * 1000;
|
|
655
680
|
if (Math.abs(strVelocity) < longPressRatio) {
|
|
656
681
|
handleLongPress();
|
|
657
682
|
}
|
|
658
683
|
else {
|
|
684
|
+
// 如果触发了onTouchesCancelled,不会触发onUpdate不会更新offset值, 索引不会变更
|
|
659
685
|
handleEnd(eventData);
|
|
660
686
|
}
|
|
661
687
|
})
|
|
@@ -57,7 +57,7 @@ const normalizeStyle = (style = {}) => {
|
|
|
57
57
|
const isPercent = (val) => typeof val === 'string' && PERCENT_REGEX.test(val);
|
|
58
58
|
const isBackgroundSizeKeyword = (val) => typeof val === 'string' && /^cover|contain$/.test(val);
|
|
59
59
|
const isNeedLayout = (preImageInfo) => {
|
|
60
|
-
const { sizeList, backgroundPosition, linearInfo } = preImageInfo;
|
|
60
|
+
const { sizeList, backgroundPosition, linearInfo, type } = preImageInfo;
|
|
61
61
|
const [width, height] = sizeList;
|
|
62
62
|
const bp = backgroundPosition;
|
|
63
63
|
// 含有百分号,center 需计算布局
|
|
@@ -66,7 +66,8 @@ const isNeedLayout = (preImageInfo) => {
|
|
|
66
66
|
(isPercent(width) && height === 'auto') ||
|
|
67
67
|
isPercent(bp[1]) ||
|
|
68
68
|
isPercent(bp[3]) ||
|
|
69
|
-
isDiagonalAngle(linearInfo)
|
|
69
|
+
isDiagonalAngle(linearInfo) ||
|
|
70
|
+
(type === 'linear' && (isPercent(height) || isPercent(width)));
|
|
70
71
|
};
|
|
71
72
|
const checkNeedLayout = (preImageInfo) => {
|
|
72
73
|
const { sizeList } = preImageInfo;
|
|
@@ -155,7 +156,7 @@ function backgroundPosition(imageProps, preImageInfo, imageSize, layoutInfo) {
|
|
|
155
156
|
}
|
|
156
157
|
// background-size 转换
|
|
157
158
|
function backgroundSize(imageProps, preImageInfo, imageSize, layoutInfo) {
|
|
158
|
-
const sizeList = preImageInfo
|
|
159
|
+
const { sizeList, type } = preImageInfo;
|
|
159
160
|
if (!sizeList)
|
|
160
161
|
return;
|
|
161
162
|
const { width: layoutWidth, height: layoutHeight } = layoutInfo || {};
|
|
@@ -202,10 +203,23 @@ function backgroundSize(imageProps, preImageInfo, imageSize, layoutInfo) {
|
|
|
202
203
|
else { // 数值类型 ImageStyle
|
|
203
204
|
// 数值类型设置为 stretch
|
|
204
205
|
imageProps.resizeMode = 'stretch';
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
206
|
+
if (type === 'linear') {
|
|
207
|
+
const dimensionWidth = calcPercent(width, layoutWidth) || 0;
|
|
208
|
+
const dimensionHeight = calcPercent(height, layoutHeight) || 0;
|
|
209
|
+
// ios 上 linear 组件只要重新触发渲染,在渲染过程中 width 或者 height 被设置为 0,即使后面再更新为正常宽高,也会渲染不出来
|
|
210
|
+
if (dimensionWidth && dimensionHeight) {
|
|
211
|
+
dimensions = {
|
|
212
|
+
width: dimensionWidth,
|
|
213
|
+
height: dimensionHeight
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
dimensions = {
|
|
219
|
+
width: isPercent(width) ? width : +width,
|
|
220
|
+
height: isPercent(height) ? height : +height
|
|
221
|
+
};
|
|
222
|
+
}
|
|
209
223
|
}
|
|
210
224
|
}
|
|
211
225
|
// 样式合并
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { forwardRef, useRef, useContext, useMemo, useState
|
|
1
|
+
import { forwardRef, useRef, useContext, useMemo, useState } from 'react';
|
|
2
2
|
import { warn, isFunction } from '@mpxjs/utils';
|
|
3
3
|
import Portal from './mpx-portal/index';
|
|
4
4
|
import { getCustomEvent } from './getInnerListeners';
|
|
@@ -74,17 +74,17 @@ const _WebView = forwardRef((props, ref) => {
|
|
|
74
74
|
isNavigateBack.current = false;
|
|
75
75
|
};
|
|
76
76
|
const navigation = useNavigation();
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}, [])
|
|
77
|
+
// useEffect(() => {
|
|
78
|
+
// let beforeRemoveSubscription:any
|
|
79
|
+
// if (__mpx_mode__ !== 'ios') {
|
|
80
|
+
// beforeRemoveSubscription = navigation?.addListener?.('beforeRemove', beforeRemoveHandle)
|
|
81
|
+
// }
|
|
82
|
+
// return () => {
|
|
83
|
+
// if (isFunction(beforeRemoveSubscription)) {
|
|
84
|
+
// beforeRemoveSubscription()
|
|
85
|
+
// }
|
|
86
|
+
// }
|
|
87
|
+
// }, [])
|
|
88
88
|
useNodesRef(props, ref, webViewRef, {
|
|
89
89
|
style: defaultWebViewStyle
|
|
90
90
|
});
|
|
@@ -160,7 +160,7 @@ const _WebView = forwardRef((props, ref) => {
|
|
|
160
160
|
{ // case下不允许直接声明,包个块解决该问题
|
|
161
161
|
const title = postData._documentTitle?.trim();
|
|
162
162
|
if (title !== undefined) {
|
|
163
|
-
navigation && navigation.
|
|
163
|
+
navigation && navigation.setPageConfig({ navigationBarTitleText: title });
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
break;
|