@mpxjs/webpack-plugin 2.10.3-beta.6 → 2.10.4-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/index.js +1 -1
- package/lib/platform/json/wx/index.js +0 -1
- package/lib/platform/style/wx/index.js +22 -21
- 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 +2 -2
- 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/platform/template/wx/component-config/text.js +18 -3
- package/lib/platform/template/wx/component-config/view.js +0 -2
- package/lib/platform/template/wx/index.js +41 -93
- package/lib/react/processJSON.js +7 -6
- package/lib/react/processScript.js +1 -18
- package/lib/runtime/components/react/context.ts +12 -3
- package/lib/runtime/components/react/dist/context.js +4 -1
- package/lib/runtime/components/react/dist/event.config.js +0 -1
- package/lib/runtime/components/react/dist/getInnerListeners.js +127 -142
- package/lib/runtime/components/react/dist/mpx-button.jsx +4 -5
- package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +4 -5
- package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-checkbox.jsx +2 -3
- package/lib/runtime/components/react/dist/mpx-form.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-icon/index.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-image.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-inline-text.jsx +11 -0
- package/lib/runtime/components/react/dist/mpx-input.jsx +6 -10
- package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-label.jsx +2 -3
- package/lib/runtime/components/react/dist/mpx-movable-area.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +1 -1
- 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 +180 -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} +5 -6
- package/lib/runtime/components/react/dist/{mpx-picker-view-column.jsx → mpx-picker-view-column/index.jsx} +64 -16
- package/lib/runtime/components/react/dist/{mpx-picker-view-column-item.jsx → mpx-picker-view-column/pickerViewColumnItem.jsx} +8 -5
- 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-radio-group.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-radio.jsx +2 -3
- package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +207 -29
- 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-sticky-header.jsx +115 -0
- package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
- package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +4 -5
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +11 -11
- package/lib/runtime/components/react/dist/mpx-switch.jsx +3 -5
- package/lib/runtime/components/react/dist/mpx-text.jsx +4 -7
- package/lib/runtime/components/react/dist/mpx-video.jsx +5 -5
- package/lib/runtime/components/react/dist/mpx-view.jsx +23 -9
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +17 -17
- package/lib/runtime/components/react/dist/useAnimationHooks.js +26 -4
- package/lib/runtime/components/react/dist/utils.jsx +13 -2
- package/lib/runtime/components/react/event.config.ts +1 -6
- package/lib/runtime/components/react/getInnerListeners.ts +148 -191
- package/lib/runtime/components/react/mpx-button.tsx +7 -7
- package/lib/runtime/components/react/mpx-canvas/index.tsx +25 -17
- package/lib/runtime/components/react/mpx-checkbox-group.tsx +4 -3
- package/lib/runtime/components/react/mpx-checkbox.tsx +8 -9
- package/lib/runtime/components/react/mpx-form.tsx +25 -19
- package/lib/runtime/components/react/mpx-icon/index.tsx +4 -3
- package/lib/runtime/components/react/mpx-image.tsx +4 -3
- package/lib/runtime/components/react/mpx-inline-text.tsx +18 -0
- package/lib/runtime/components/react/mpx-input.tsx +8 -14
- package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +2 -2
- package/lib/runtime/components/react/mpx-label.tsx +4 -5
- package/lib/runtime/components/react/mpx-movable-area.tsx +22 -13
- package/lib/runtime/components/react/mpx-movable-view.tsx +47 -40
- package/lib/runtime/components/react/mpx-navigator.tsx +4 -6
- package/lib/runtime/components/react/mpx-picker/date.tsx +226 -69
- package/lib/runtime/components/react/mpx-picker/dateData.ts +22 -0
- package/lib/runtime/components/react/mpx-picker/index.tsx +242 -118
- package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +96 -139
- package/lib/runtime/components/react/mpx-picker/region.tsx +217 -89
- package/lib/runtime/components/react/mpx-picker/selector.tsx +75 -80
- package/lib/runtime/components/react/mpx-picker/time.tsx +119 -236
- package/lib/runtime/components/react/mpx-picker/type.ts +85 -71
- package/lib/runtime/components/react/{mpx-picker-view.tsx → mpx-picker-view/index.tsx} +24 -21
- package/lib/runtime/components/react/{mpx-picker-view-column.tsx → mpx-picker-view-column/index.tsx} +70 -19
- package/lib/runtime/components/react/{mpx-picker-view-column-item.tsx → mpx-picker-view-column/pickerViewColumnItem.tsx} +8 -5
- package/lib/runtime/components/react/{pickerFaces.ts → mpx-picker-view-column/pickerViewFaces.ts} +7 -0
- package/lib/runtime/components/react/mpx-popup/index.tsx +86 -0
- package/lib/runtime/components/react/mpx-popup/popupBase.tsx +130 -0
- package/lib/runtime/components/react/mpx-radio-group.tsx +4 -3
- package/lib/runtime/components/react/mpx-radio.tsx +8 -9
- package/lib/runtime/components/react/mpx-rich-text/index.tsx +15 -6
- package/lib/runtime/components/react/mpx-scroll-view.tsx +326 -96
- package/lib/runtime/components/react/mpx-simple-text.tsx +17 -8
- package/lib/runtime/components/react/mpx-simple-view.tsx +17 -16
- package/lib/runtime/components/react/mpx-sticky-header.tsx +179 -0
- package/lib/runtime/components/react/mpx-sticky-section.tsx +96 -0
- package/lib/runtime/components/react/mpx-swiper-item.tsx +31 -24
- package/lib/runtime/components/react/mpx-swiper.tsx +69 -65
- package/lib/runtime/components/react/mpx-switch.tsx +19 -14
- package/lib/runtime/components/react/mpx-text.tsx +16 -13
- package/lib/runtime/components/react/mpx-video.tsx +36 -35
- package/lib/runtime/components/react/mpx-view.tsx +41 -17
- package/lib/runtime/components/react/mpx-web-view.tsx +16 -16
- package/lib/runtime/components/react/types/getInnerListeners.d.ts +69 -35
- package/lib/runtime/components/react/useAnimationHooks.ts +29 -9
- package/lib/runtime/components/react/utils.tsx +13 -2
- package/lib/runtime/components/web/mpx-input.vue +1 -1
- package/lib/runtime/components/web/mpx-scroll-view.vue +21 -4
- package/lib/runtime/components/web/mpx-sticky-header.vue +91 -0
- package/lib/runtime/components/web/mpx-sticky-section.vue +15 -0
- package/lib/runtime/stringify.wxs +2 -2
- package/lib/template-compiler/compiler.js +7 -7
- package/package.json +3 -4
- /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
- /package/lib/runtime/components/react/{pickerVIewContext.ts → mpx-picker-view/pickerVIewContext.ts} +0 -0
- /package/lib/runtime/components/react/{pickerViewIndicator.tsx → mpx-picker-view-column/pickerViewIndicator.tsx} +0 -0
- /package/lib/runtime/components/react/{pickerViewMask.tsx → mpx-picker-view-column/pickerViewMask.tsx} +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
|
|
3
|
+
import { getWindowInfo } from '@mpxjs/api-proxy';
|
|
4
|
+
import { useUpdateEffect } from '../utils';
|
|
5
|
+
const windowInfo = getWindowInfo();
|
|
6
|
+
const bottom = windowInfo.screenHeight - windowInfo.safeArea.bottom;
|
|
7
|
+
const styles = StyleSheet.create({
|
|
8
|
+
mask: {
|
|
9
|
+
left: 0,
|
|
10
|
+
top: 0,
|
|
11
|
+
bottom: 0,
|
|
12
|
+
right: 0,
|
|
13
|
+
backgroundColor: 'rgba(0,0,0,0.6)',
|
|
14
|
+
position: 'absolute',
|
|
15
|
+
zIndex: 1000
|
|
16
|
+
},
|
|
17
|
+
content: {
|
|
18
|
+
backgroundColor: '#ffffff',
|
|
19
|
+
borderTopLeftRadius: 10,
|
|
20
|
+
borderTopRightRadius: 10,
|
|
21
|
+
position: 'absolute',
|
|
22
|
+
bottom: 0,
|
|
23
|
+
left: 0,
|
|
24
|
+
right: 0,
|
|
25
|
+
paddingBottom: bottom
|
|
26
|
+
},
|
|
27
|
+
buttonStyle: {
|
|
28
|
+
fontSize: 18,
|
|
29
|
+
paddingTop: 10,
|
|
30
|
+
paddingBottom: 10
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
const MASK_ON = 1;
|
|
34
|
+
const MASK_OFF = 0;
|
|
35
|
+
const MOVEOUT_HEIGHT = 330;
|
|
36
|
+
/**
|
|
37
|
+
* 类似微信 picker 弹窗的动画效果都可以复用此类容器
|
|
38
|
+
* 其他特定类型的弹窗容器组件可以在此基础上封装,或者扩展实现
|
|
39
|
+
*/
|
|
40
|
+
const PopupBase = (props = {}) => {
|
|
41
|
+
const { children, hide = () => null, contentHeight = MOVEOUT_HEIGHT, visible = false } = props;
|
|
42
|
+
const fade = useSharedValue(MASK_OFF);
|
|
43
|
+
const slide = useSharedValue(contentHeight);
|
|
44
|
+
const animatedStylesMask = useAnimatedStyle(() => ({
|
|
45
|
+
opacity: fade.value
|
|
46
|
+
}));
|
|
47
|
+
const animatedStylesContent = useAnimatedStyle(() => ({
|
|
48
|
+
transform: [{ translateY: slide.value }]
|
|
49
|
+
}));
|
|
50
|
+
const showAimation = () => {
|
|
51
|
+
fade.value = withTiming(MASK_ON, {
|
|
52
|
+
easing: Easing.inOut(Easing.poly(3)),
|
|
53
|
+
duration: 300
|
|
54
|
+
});
|
|
55
|
+
slide.value = withTiming(0, {
|
|
56
|
+
easing: Easing.out(Easing.poly(3)),
|
|
57
|
+
duration: 300
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
const hideAnimation = () => {
|
|
61
|
+
fade.value = withTiming(MASK_OFF, {
|
|
62
|
+
easing: Easing.inOut(Easing.poly(3)),
|
|
63
|
+
duration: 300
|
|
64
|
+
});
|
|
65
|
+
slide.value = withTiming(contentHeight, {
|
|
66
|
+
easing: Easing.inOut(Easing.poly(3)),
|
|
67
|
+
duration: 300
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
useUpdateEffect(() => {
|
|
71
|
+
if (visible) {
|
|
72
|
+
showAimation();
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
hideAnimation();
|
|
76
|
+
}
|
|
77
|
+
}, [visible]);
|
|
78
|
+
const preventMaskClick = (e) => {
|
|
79
|
+
e.stopPropagation();
|
|
80
|
+
};
|
|
81
|
+
return (<Animated.View onTouchEnd={hide} style={[
|
|
82
|
+
styles.mask,
|
|
83
|
+
animatedStylesMask,
|
|
84
|
+
{ pointerEvents: visible ? 'auto' : 'none' }
|
|
85
|
+
]}>
|
|
86
|
+
<Animated.View style={[styles.content, animatedStylesContent]} onTouchEnd={preventMaskClick}>
|
|
87
|
+
{children}
|
|
88
|
+
</Animated.View>
|
|
89
|
+
</Animated.View>);
|
|
90
|
+
};
|
|
91
|
+
PopupBase.displayName = 'MpxPopupBase';
|
|
92
|
+
export default PopupBase;
|
|
@@ -71,10 +71,10 @@ const radioGroup = forwardRef((props, ref) => {
|
|
|
71
71
|
notifyChange
|
|
72
72
|
};
|
|
73
73
|
}, []);
|
|
74
|
-
const innerProps = useInnerProps(props,
|
|
74
|
+
const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
|
|
75
75
|
ref: nodeRef,
|
|
76
76
|
style: extendObject({}, normalStyle, layoutStyle)
|
|
77
|
-
}
|
|
77
|
+
}), ['name'], {
|
|
78
78
|
layoutRef
|
|
79
79
|
});
|
|
80
80
|
return createElement(View, innerProps, createElement(RadioGroupContext.Provider, { value: contextValue }, wrapChildren(props, {
|
|
@@ -91,10 +91,9 @@ const Radio = forwardRef((radioProps, ref) => {
|
|
|
91
91
|
if (labelContext) {
|
|
92
92
|
labelContext.current.triggerChange = onChange;
|
|
93
93
|
}
|
|
94
|
-
const innerProps = useInnerProps(props,
|
|
94
|
+
const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
|
|
95
95
|
ref: nodeRef,
|
|
96
|
-
style: extendObject({}, innerStyle, layoutStyle)
|
|
97
|
-
}, layoutProps, {
|
|
96
|
+
style: extendObject({}, innerStyle, layoutStyle),
|
|
98
97
|
bindtap: !disabled && onTap
|
|
99
98
|
}), [
|
|
100
99
|
'value',
|
|
@@ -44,10 +44,10 @@ const _RichText = forwardRef((props, ref) => {
|
|
|
44
44
|
useNodesRef(props, ref, nodeRef, {
|
|
45
45
|
layoutRef
|
|
46
46
|
});
|
|
47
|
-
const innerProps = useInnerProps(props,
|
|
47
|
+
const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
|
|
48
48
|
ref: nodeRef,
|
|
49
49
|
style: extendObject(normalStyle, layoutStyle)
|
|
50
|
-
}
|
|
50
|
+
}), [], {
|
|
51
51
|
layoutRef
|
|
52
52
|
});
|
|
53
53
|
const html = typeof nodes === 'string' ? nodes : jsonToHtmlStr(nodes);
|
|
@@ -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,32 @@
|
|
|
31
31
|
* ✔ bindscrolltolower
|
|
32
32
|
* ✔ bindscroll
|
|
33
33
|
*/
|
|
34
|
-
import { ScrollView, RefreshControl } from 'react-native-gesture-handler';
|
|
35
|
-
import {
|
|
36
|
-
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, { useAnimatedRef, useSharedValue, withTiming, useAnimatedStyle, runOnJS } from 'react-native-reanimated';
|
|
37
38
|
import { warn } 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 } from './utils';
|
|
41
42
|
import { IntersectionObserverContext, ScrollViewContext } from './context';
|
|
43
|
+
const AnimatedScrollView = RNAnimated.createAnimatedComponent(ScrollView);
|
|
42
44
|
const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
43
45
|
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;
|
|
46
|
+
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, __selectRef } = props;
|
|
47
|
+
const scrollOffset = useRef(new RNAnimated.Value(0)).current;
|
|
45
48
|
const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
|
|
46
49
|
const waitForHandlers = flatGesture(waitFor);
|
|
47
|
-
const [refreshing, setRefreshing] = useState(true);
|
|
48
50
|
const snapScrollTop = useRef(0);
|
|
49
51
|
const snapScrollLeft = useRef(0);
|
|
52
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
53
|
+
const [enableScroll, setEnableScroll] = useState(true);
|
|
54
|
+
const enableScrollValue = useSharedValue(true);
|
|
55
|
+
const [scrollBounces, setScrollBounces] = useState(false);
|
|
56
|
+
const bouncesValue = useSharedValue(!!false);
|
|
57
|
+
const translateY = useSharedValue(0);
|
|
58
|
+
const isAtTop = useSharedValue(true);
|
|
59
|
+
const refresherHeight = useSharedValue(0);
|
|
50
60
|
const scrollOptions = useRef({
|
|
51
61
|
contentLength: 0,
|
|
52
62
|
offset: 0,
|
|
@@ -59,6 +69,12 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
59
69
|
const initialTimeout = useRef(null);
|
|
60
70
|
const intersectionObservers = useContext(IntersectionObserverContext);
|
|
61
71
|
const firstScrollIntoViewChange = useRef(false);
|
|
72
|
+
const refreshColor = {
|
|
73
|
+
black: ['#000'],
|
|
74
|
+
white: ['#fff']
|
|
75
|
+
};
|
|
76
|
+
const { refresherContent, otherContent } = getRefresherContent(props.children);
|
|
77
|
+
const hasRefresher = refresherContent && refresherEnabled;
|
|
62
78
|
const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
63
79
|
const { textStyle, innerStyle = {} } = splitStyle(normalStyle);
|
|
64
80
|
const scrollViewRef = useAnimatedRef();
|
|
@@ -76,12 +92,16 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
76
92
|
},
|
|
77
93
|
gestureRef: scrollViewRef
|
|
78
94
|
});
|
|
95
|
+
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
|
|
79
96
|
const contextValue = useMemo(() => {
|
|
80
97
|
return {
|
|
81
|
-
gestureRef: scrollViewRef
|
|
98
|
+
gestureRef: scrollViewRef,
|
|
99
|
+
scrollOffset
|
|
82
100
|
};
|
|
83
101
|
}, []);
|
|
84
|
-
const
|
|
102
|
+
const hasRefresherLayoutRef = useRef(false);
|
|
103
|
+
// layout 完成前先隐藏,避免安卓闪烁问题
|
|
104
|
+
const refresherLayoutStyle = useMemo(() => { return !hasRefresherLayoutRef.current ? HIDDEN_STYLE : {}; }, [hasRefresherLayoutRef.current]);
|
|
85
105
|
const lastOffset = useRef(0);
|
|
86
106
|
if (scrollX && scrollY) {
|
|
87
107
|
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');
|
|
@@ -96,11 +116,6 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
96
116
|
};
|
|
97
117
|
}
|
|
98
118
|
}, [scrollTop, scrollLeft]);
|
|
99
|
-
useEffect(() => {
|
|
100
|
-
if (refreshing !== refresherTriggered) {
|
|
101
|
-
setRefreshing(!!refresherTriggered);
|
|
102
|
-
}
|
|
103
|
-
}, [refresherTriggered]);
|
|
104
119
|
useEffect(() => {
|
|
105
120
|
if (scrollIntoView && __selectRef) {
|
|
106
121
|
if (!firstScrollIntoViewChange.current) {
|
|
@@ -112,6 +127,21 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
112
127
|
}
|
|
113
128
|
firstScrollIntoViewChange.current = true;
|
|
114
129
|
}, [scrollIntoView]);
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (refresherEnabled) {
|
|
132
|
+
setRefreshing(!!refresherTriggered);
|
|
133
|
+
if (!refresherContent)
|
|
134
|
+
return;
|
|
135
|
+
if (refresherTriggered) {
|
|
136
|
+
translateY.value = withTiming(refresherHeight.value);
|
|
137
|
+
resetScrollState(false);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
translateY.value = withTiming(0);
|
|
141
|
+
resetScrollState(true);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}, [refresherTriggered]);
|
|
115
145
|
function scrollTo({ top = 0, left = 0, animated = false }) {
|
|
116
146
|
scrollToOffset(left, top, animated);
|
|
117
147
|
}
|
|
@@ -192,6 +222,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
192
222
|
const { bindscroll } = props;
|
|
193
223
|
const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset;
|
|
194
224
|
const { width: scrollWidth, height: scrollHeight } = e.nativeEvent.contentSize;
|
|
225
|
+
isAtTop.value = scrollTop <= 0;
|
|
195
226
|
bindscroll &&
|
|
196
227
|
bindscroll(getCustomEvent('scroll', e, {
|
|
197
228
|
detail: {
|
|
@@ -215,6 +246,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
215
246
|
const { bindscrollend } = props;
|
|
216
247
|
const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset;
|
|
217
248
|
const { width: scrollWidth, height: scrollHeight } = e.nativeEvent.contentSize;
|
|
249
|
+
isAtTop.value = scrollTop <= 0;
|
|
218
250
|
bindscrollend &&
|
|
219
251
|
bindscrollend(getCustomEvent('scrollend', e, {
|
|
220
252
|
detail: {
|
|
@@ -247,11 +279,6 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
247
279
|
snapScrollTop.current = y;
|
|
248
280
|
}
|
|
249
281
|
}
|
|
250
|
-
function onRefresh() {
|
|
251
|
-
const { bindrefresherrefresh } = props;
|
|
252
|
-
bindrefresherrefresh &&
|
|
253
|
-
bindrefresherrefresh(getCustomEvent('refresherrefresh', {}, { layoutRef }, props));
|
|
254
|
-
}
|
|
255
282
|
function onScrollTouchStart(e) {
|
|
256
283
|
const { bindtouchstart } = props;
|
|
257
284
|
bindtouchstart && bindtouchstart(e);
|
|
@@ -297,11 +324,155 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
297
324
|
updateScrollOptions(e, { scrollLeft, scrollTop });
|
|
298
325
|
updateIntersection();
|
|
299
326
|
}
|
|
327
|
+
const scrollHandler = RNAnimated.event([{ nativeEvent: { contentOffset: { y: scrollOffset } } }], {
|
|
328
|
+
useNativeDriver: true,
|
|
329
|
+
listener: (event) => {
|
|
330
|
+
onScroll(event);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
300
333
|
function onScrollDragStart(e) {
|
|
301
334
|
hasCallScrollToLower.current = false;
|
|
302
335
|
hasCallScrollToUpper.current = false;
|
|
303
336
|
onScrollDrag(e);
|
|
304
337
|
}
|
|
338
|
+
// 处理刷新
|
|
339
|
+
function onRefresh() {
|
|
340
|
+
if (hasRefresher && refresherTriggered === undefined) {
|
|
341
|
+
// 处理使用了自定义刷新组件,又没设置 refresherTriggered 的情况
|
|
342
|
+
setRefreshing(true);
|
|
343
|
+
setTimeout(() => {
|
|
344
|
+
setRefreshing(false);
|
|
345
|
+
translateY.value = withTiming(0);
|
|
346
|
+
if (!enableScrollValue.value) {
|
|
347
|
+
resetScrollState(true);
|
|
348
|
+
}
|
|
349
|
+
}, 500);
|
|
350
|
+
}
|
|
351
|
+
const { bindrefresherrefresh } = props;
|
|
352
|
+
bindrefresherrefresh &&
|
|
353
|
+
bindrefresherrefresh(getCustomEvent('refresherrefresh', {}, { layoutRef }, props));
|
|
354
|
+
}
|
|
355
|
+
function getRefresherContent(children) {
|
|
356
|
+
let refresherContent = null;
|
|
357
|
+
const otherContent = [];
|
|
358
|
+
Children.forEach(children, (child) => {
|
|
359
|
+
if (isValidElement(child) && child.props.slot === 'refresher') {
|
|
360
|
+
refresherContent = child;
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
otherContent.push(child);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
return {
|
|
367
|
+
refresherContent,
|
|
368
|
+
otherContent
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
// 刷新控件的动画样式
|
|
372
|
+
const refresherAnimatedStyle = useAnimatedStyle(() => {
|
|
373
|
+
return {
|
|
374
|
+
position: 'absolute',
|
|
375
|
+
left: 0,
|
|
376
|
+
right: 0,
|
|
377
|
+
top: -refresherHeight.value,
|
|
378
|
+
transform: [{ translateY: Math.min(translateY.value, refresherHeight.value) }],
|
|
379
|
+
backgroundColor: refresherBackground || 'transparent'
|
|
380
|
+
};
|
|
381
|
+
});
|
|
382
|
+
// 内容区域的动画样式 - 只有内容区域需要下移
|
|
383
|
+
const contentAnimatedStyle = useAnimatedStyle(() => {
|
|
384
|
+
return {
|
|
385
|
+
transform: [{
|
|
386
|
+
translateY: translateY.value > refresherHeight.value
|
|
387
|
+
? refresherHeight.value
|
|
388
|
+
: translateY.value
|
|
389
|
+
}]
|
|
390
|
+
};
|
|
391
|
+
});
|
|
392
|
+
function onRefresherLayout(e) {
|
|
393
|
+
const { height } = e.nativeEvent.layout;
|
|
394
|
+
refresherHeight.value = height;
|
|
395
|
+
hasRefresherLayoutRef.current = true;
|
|
396
|
+
}
|
|
397
|
+
function updateScrollState(newValue) {
|
|
398
|
+
'worklet';
|
|
399
|
+
if (enableScrollValue.value !== newValue) {
|
|
400
|
+
enableScrollValue.value = newValue;
|
|
401
|
+
runOnJS(setEnableScroll)(newValue);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const resetScrollState = (value) => {
|
|
405
|
+
enableScrollValue.value = value;
|
|
406
|
+
setEnableScroll(value);
|
|
407
|
+
};
|
|
408
|
+
function updateBouncesState(newValue) {
|
|
409
|
+
'worklet';
|
|
410
|
+
if (bouncesValue.value !== newValue) {
|
|
411
|
+
bouncesValue.value = newValue;
|
|
412
|
+
runOnJS(setScrollBounces)(newValue);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// 处理下拉刷新的手势
|
|
416
|
+
const panGesture = Gesture.Pan()
|
|
417
|
+
.onUpdate((event) => {
|
|
418
|
+
'worklet';
|
|
419
|
+
if (enhanced && !!bounces) {
|
|
420
|
+
if (event.translationY > 0 && bouncesValue.value) {
|
|
421
|
+
updateBouncesState(false);
|
|
422
|
+
}
|
|
423
|
+
else if ((event.translationY < 0) && !bouncesValue.value) {
|
|
424
|
+
updateBouncesState(true);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (translateY.value <= 0 && event.translationY < 0) {
|
|
428
|
+
// 滑动到顶再向上开启滚动
|
|
429
|
+
updateScrollState(true);
|
|
430
|
+
}
|
|
431
|
+
else if (event.translationY > 0 && isAtTop.value) {
|
|
432
|
+
// 滚动到顶再向下禁止滚动
|
|
433
|
+
updateScrollState(false);
|
|
434
|
+
}
|
|
435
|
+
// 禁止滚动后切换为滑动
|
|
436
|
+
if (!enableScrollValue.value && isAtTop.value) {
|
|
437
|
+
if (refreshing) {
|
|
438
|
+
// 从完全展开状态(refresherHeight.value)开始计算偏移
|
|
439
|
+
translateY.value = Math.max(0, Math.min(refresherHeight.value, refresherHeight.value + event.translationY));
|
|
440
|
+
}
|
|
441
|
+
else if (event.translationY > 0) {
|
|
442
|
+
// 非刷新状态下的下拉逻辑保持不变
|
|
443
|
+
translateY.value = Math.min(event.translationY * 0.6, refresherHeight.value);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
})
|
|
447
|
+
.onEnd((event) => {
|
|
448
|
+
'worklet';
|
|
449
|
+
if (enableScrollValue.value)
|
|
450
|
+
return;
|
|
451
|
+
if (refreshing) {
|
|
452
|
+
// 刷新状态下,根据滑动距离决定是否隐藏
|
|
453
|
+
// 如果向下滑动没超过 refresherThreshold,就完全隐藏,如果向上滑动完全隐藏
|
|
454
|
+
if ((event.translationY > 0 && translateY.value < refresherThreshold) || event.translationY < 0) {
|
|
455
|
+
translateY.value = withTiming(0);
|
|
456
|
+
updateScrollState(true);
|
|
457
|
+
runOnJS(setRefreshing)(false);
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
translateY.value = withTiming(refresherHeight.value);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
else if (event.translationY >= refresherHeight.value) {
|
|
464
|
+
// 触发刷新
|
|
465
|
+
translateY.value = withTiming(refresherHeight.value);
|
|
466
|
+
runOnJS(onRefresh)();
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
// 回弹
|
|
470
|
+
translateY.value = withTiming(0);
|
|
471
|
+
updateScrollState(true);
|
|
472
|
+
runOnJS(setRefreshing)(false);
|
|
473
|
+
}
|
|
474
|
+
})
|
|
475
|
+
.simultaneousWithExternalGesture(scrollViewRef);
|
|
305
476
|
const scrollAdditionalProps = extendObject({
|
|
306
477
|
style: extendObject({}, innerStyle, layoutStyle),
|
|
307
478
|
pinchGestureEnabled: false,
|
|
@@ -312,9 +483,10 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
312
483
|
scrollsToTop: enableBackToTop,
|
|
313
484
|
showsHorizontalScrollIndicator: scrollX && showScrollbar,
|
|
314
485
|
showsVerticalScrollIndicator: scrollY && showScrollbar,
|
|
315
|
-
scrollEnabled: scrollX || scrollY,
|
|
486
|
+
scrollEnabled: !enableScroll ? false : !!(scrollX || scrollY),
|
|
487
|
+
bounces: false,
|
|
316
488
|
ref: scrollViewRef,
|
|
317
|
-
onScroll: onScroll,
|
|
489
|
+
onScroll: enableSticky ? scrollHandler : onScroll,
|
|
318
490
|
onContentSizeChange: onContentSizeChange,
|
|
319
491
|
bindtouchstart: ((enhanced && binddragstart) || bindtouchstart) && onScrollTouchStart,
|
|
320
492
|
bindtouchmove: ((enhanced && binddragging) || bindtouchmove) && onScrollTouchMove,
|
|
@@ -325,11 +497,11 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
325
497
|
}, (simultaneousHandlers ? { simultaneousHandlers } : {}), (waitForHandlers ? { waitFor: waitForHandlers } : {}), layoutProps);
|
|
326
498
|
if (enhanced) {
|
|
327
499
|
Object.assign(scrollAdditionalProps, {
|
|
328
|
-
bounces,
|
|
500
|
+
bounces: hasRefresher ? scrollBounces : !!bounces,
|
|
329
501
|
pagingEnabled
|
|
330
502
|
});
|
|
331
503
|
}
|
|
332
|
-
const innerProps = useInnerProps(props, scrollAdditionalProps, [
|
|
504
|
+
const innerProps = useInnerProps(extendObject({}, props, scrollAdditionalProps), [
|
|
333
505
|
'id',
|
|
334
506
|
'scroll-x',
|
|
335
507
|
'scroll-y',
|
|
@@ -356,17 +528,22 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
356
528
|
'bindscrolltolower',
|
|
357
529
|
'bindrefresherrefresh'
|
|
358
530
|
], { layoutRef });
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
531
|
+
const ScrollViewComponent = enableSticky ? AnimatedScrollView : ScrollView;
|
|
532
|
+
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 }), {
|
|
533
|
+
hasVarDec,
|
|
534
|
+
varContext: varContextRef.current,
|
|
535
|
+
textStyle,
|
|
536
|
+
textProps
|
|
537
|
+
})))));
|
|
538
|
+
const commonScrollView = createElement(ScrollViewComponent, extendObject({}, innerProps, {
|
|
364
539
|
refreshControl: refresherEnabled
|
|
365
540
|
? createElement(RefreshControl, extendObject({
|
|
366
541
|
progressBackgroundColor: refresherBackground,
|
|
367
542
|
refreshing: refreshing,
|
|
368
543
|
onRefresh: onRefresh
|
|
369
|
-
},
|
|
544
|
+
}, refresherDefaultStyle && refresherDefaultStyle !== 'none'
|
|
545
|
+
? { colors: refreshColor[refresherDefaultStyle] }
|
|
546
|
+
: {}))
|
|
370
547
|
: undefined
|
|
371
548
|
}), createElement(ScrollViewContext.Provider, { value: contextValue }, wrapChildren(props, {
|
|
372
549
|
hasVarDec,
|
|
@@ -374,6 +551,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
374
551
|
textStyle,
|
|
375
552
|
textProps
|
|
376
553
|
})));
|
|
554
|
+
return hasRefresher ? withRefresherScrollView : commonScrollView;
|
|
377
555
|
});
|
|
378
556
|
_ScrollView.displayName = 'MpxScrollView';
|
|
379
557
|
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;
|
|
@@ -0,0 +1,115 @@
|
|
|
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
|
+
}
|
|
113
|
+
});
|
|
114
|
+
_StickyHeader.displayName = 'MpxStickyHeader';
|
|
115
|
+
export default _StickyHeader;
|