@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.
Files changed (122) hide show
  1. package/lib/index.js +1 -1
  2. package/lib/platform/json/wx/index.js +0 -1
  3. package/lib/platform/style/wx/index.js +22 -21
  4. package/lib/platform/template/wx/component-config/button.js +1 -1
  5. package/lib/platform/template/wx/component-config/index.js +5 -1
  6. package/lib/platform/template/wx/component-config/input.js +2 -2
  7. package/lib/platform/template/wx/component-config/sticky-header.js +23 -0
  8. package/lib/platform/template/wx/component-config/sticky-section.js +23 -0
  9. package/lib/platform/template/wx/component-config/text.js +18 -3
  10. package/lib/platform/template/wx/component-config/view.js +0 -2
  11. package/lib/platform/template/wx/index.js +41 -93
  12. package/lib/react/processJSON.js +7 -6
  13. package/lib/react/processScript.js +1 -18
  14. package/lib/runtime/components/react/context.ts +12 -3
  15. package/lib/runtime/components/react/dist/context.js +4 -1
  16. package/lib/runtime/components/react/dist/event.config.js +0 -1
  17. package/lib/runtime/components/react/dist/getInnerListeners.js +127 -142
  18. package/lib/runtime/components/react/dist/mpx-button.jsx +4 -5
  19. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +4 -5
  20. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +2 -2
  21. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +2 -3
  22. package/lib/runtime/components/react/dist/mpx-form.jsx +2 -2
  23. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +2 -2
  24. package/lib/runtime/components/react/dist/mpx-image.jsx +2 -2
  25. package/lib/runtime/components/react/dist/mpx-inline-text.jsx +11 -0
  26. package/lib/runtime/components/react/dist/mpx-input.jsx +6 -10
  27. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +2 -2
  28. package/lib/runtime/components/react/dist/mpx-label.jsx +2 -3
  29. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +2 -2
  30. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +1 -1
  31. package/lib/runtime/components/react/dist/mpx-navigator.jsx +11 -3
  32. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +194 -68
  33. package/lib/runtime/components/react/dist/mpx-picker/dateData.js +17 -0
  34. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +180 -98
  35. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +79 -139
  36. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +190 -90
  37. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +60 -75
  38. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +100 -228
  39. package/lib/runtime/components/react/dist/{mpx-picker-view.jsx → mpx-picker-view/index.jsx} +5 -6
  40. package/lib/runtime/components/react/dist/{mpx-picker-view-column.jsx → mpx-picker-view-column/index.jsx} +64 -16
  41. package/lib/runtime/components/react/dist/{mpx-picker-view-column-item.jsx → mpx-picker-view-column/pickerViewColumnItem.jsx} +8 -5
  42. package/lib/runtime/components/react/dist/{pickerFaces.js → mpx-picker-view-column/pickerViewFaces.js} +6 -0
  43. package/lib/runtime/components/react/dist/mpx-popup/index.jsx +61 -0
  44. package/lib/runtime/components/react/dist/mpx-popup/popupBase.jsx +92 -0
  45. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +2 -2
  46. package/lib/runtime/components/react/dist/mpx-radio.jsx +2 -3
  47. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +2 -2
  48. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +207 -29
  49. package/lib/runtime/components/react/dist/mpx-simple-text.jsx +7 -5
  50. package/lib/runtime/components/react/dist/mpx-simple-view.jsx +11 -15
  51. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +115 -0
  52. package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
  53. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +4 -5
  54. package/lib/runtime/components/react/dist/mpx-swiper.jsx +11 -11
  55. package/lib/runtime/components/react/dist/mpx-switch.jsx +3 -5
  56. package/lib/runtime/components/react/dist/mpx-text.jsx +4 -7
  57. package/lib/runtime/components/react/dist/mpx-video.jsx +5 -5
  58. package/lib/runtime/components/react/dist/mpx-view.jsx +23 -9
  59. package/lib/runtime/components/react/dist/mpx-web-view.jsx +17 -17
  60. package/lib/runtime/components/react/dist/useAnimationHooks.js +26 -4
  61. package/lib/runtime/components/react/dist/utils.jsx +13 -2
  62. package/lib/runtime/components/react/event.config.ts +1 -6
  63. package/lib/runtime/components/react/getInnerListeners.ts +148 -191
  64. package/lib/runtime/components/react/mpx-button.tsx +7 -7
  65. package/lib/runtime/components/react/mpx-canvas/index.tsx +25 -17
  66. package/lib/runtime/components/react/mpx-checkbox-group.tsx +4 -3
  67. package/lib/runtime/components/react/mpx-checkbox.tsx +8 -9
  68. package/lib/runtime/components/react/mpx-form.tsx +25 -19
  69. package/lib/runtime/components/react/mpx-icon/index.tsx +4 -3
  70. package/lib/runtime/components/react/mpx-image.tsx +4 -3
  71. package/lib/runtime/components/react/mpx-inline-text.tsx +18 -0
  72. package/lib/runtime/components/react/mpx-input.tsx +8 -14
  73. package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +2 -2
  74. package/lib/runtime/components/react/mpx-label.tsx +4 -5
  75. package/lib/runtime/components/react/mpx-movable-area.tsx +22 -13
  76. package/lib/runtime/components/react/mpx-movable-view.tsx +47 -40
  77. package/lib/runtime/components/react/mpx-navigator.tsx +4 -6
  78. package/lib/runtime/components/react/mpx-picker/date.tsx +226 -69
  79. package/lib/runtime/components/react/mpx-picker/dateData.ts +22 -0
  80. package/lib/runtime/components/react/mpx-picker/index.tsx +242 -118
  81. package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +96 -139
  82. package/lib/runtime/components/react/mpx-picker/region.tsx +217 -89
  83. package/lib/runtime/components/react/mpx-picker/selector.tsx +75 -80
  84. package/lib/runtime/components/react/mpx-picker/time.tsx +119 -236
  85. package/lib/runtime/components/react/mpx-picker/type.ts +85 -71
  86. package/lib/runtime/components/react/{mpx-picker-view.tsx → mpx-picker-view/index.tsx} +24 -21
  87. package/lib/runtime/components/react/{mpx-picker-view-column.tsx → mpx-picker-view-column/index.tsx} +70 -19
  88. package/lib/runtime/components/react/{mpx-picker-view-column-item.tsx → mpx-picker-view-column/pickerViewColumnItem.tsx} +8 -5
  89. package/lib/runtime/components/react/{pickerFaces.ts → mpx-picker-view-column/pickerViewFaces.ts} +7 -0
  90. package/lib/runtime/components/react/mpx-popup/index.tsx +86 -0
  91. package/lib/runtime/components/react/mpx-popup/popupBase.tsx +130 -0
  92. package/lib/runtime/components/react/mpx-radio-group.tsx +4 -3
  93. package/lib/runtime/components/react/mpx-radio.tsx +8 -9
  94. package/lib/runtime/components/react/mpx-rich-text/index.tsx +15 -6
  95. package/lib/runtime/components/react/mpx-scroll-view.tsx +326 -96
  96. package/lib/runtime/components/react/mpx-simple-text.tsx +17 -8
  97. package/lib/runtime/components/react/mpx-simple-view.tsx +17 -16
  98. package/lib/runtime/components/react/mpx-sticky-header.tsx +179 -0
  99. package/lib/runtime/components/react/mpx-sticky-section.tsx +96 -0
  100. package/lib/runtime/components/react/mpx-swiper-item.tsx +31 -24
  101. package/lib/runtime/components/react/mpx-swiper.tsx +69 -65
  102. package/lib/runtime/components/react/mpx-switch.tsx +19 -14
  103. package/lib/runtime/components/react/mpx-text.tsx +16 -13
  104. package/lib/runtime/components/react/mpx-video.tsx +36 -35
  105. package/lib/runtime/components/react/mpx-view.tsx +41 -17
  106. package/lib/runtime/components/react/mpx-web-view.tsx +16 -16
  107. package/lib/runtime/components/react/types/getInnerListeners.d.ts +69 -35
  108. package/lib/runtime/components/react/useAnimationHooks.ts +29 -9
  109. package/lib/runtime/components/react/utils.tsx +13 -2
  110. package/lib/runtime/components/web/mpx-input.vue +1 -1
  111. package/lib/runtime/components/web/mpx-scroll-view.vue +21 -4
  112. package/lib/runtime/components/web/mpx-sticky-header.vue +91 -0
  113. package/lib/runtime/components/web/mpx-sticky-section.vue +15 -0
  114. package/lib/runtime/stringify.wxs +2 -2
  115. package/lib/template-compiler/compiler.js +7 -7
  116. package/package.json +3 -4
  117. /package/lib/runtime/components/react/dist/{pickerVIewContext.js → mpx-picker-view/pickerVIewContext.js} +0 -0
  118. /package/lib/runtime/components/react/dist/{pickerViewIndicator.jsx → mpx-picker-view-column/pickerViewIndicator.jsx} +0 -0
  119. /package/lib/runtime/components/react/dist/{pickerViewMask.jsx → mpx-picker-view-column/pickerViewMask.jsx} +0 -0
  120. /package/lib/runtime/components/react/{pickerVIewContext.ts → mpx-picker-view/pickerVIewContext.ts} +0 -0
  121. /package/lib/runtime/components/react/{pickerViewIndicator.tsx → mpx-picker-view-column/pickerViewIndicator.tsx} +0 -0
  122. /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, extendObject({
74
+ const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
75
75
  ref: nodeRef,
76
76
  style: extendObject({}, normalStyle, layoutStyle)
77
- }, layoutProps), ['name'], {
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, extendObject({
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, extendObject({
47
+ const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
48
48
  ref: nodeRef,
49
49
  style: extendObject(normalStyle, layoutStyle)
50
- }, layoutProps), [], {
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
- * refresher-threshold
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 { useRef, useState, useEffect, forwardRef, useContext, createElement, useMemo } from 'react';
36
- import { useAnimatedRef } from 'react-native-reanimated';
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 { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
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 refreshColor = {
360
- black: ['#000'],
361
- white: ['#fff']
362
- };
363
- return createElement(ScrollView, extendObject({}, innerProps, {
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
- }, (refresherDefaultStyle && refresherDefaultStyle !== 'none' ? { colors: refreshColor[refresherDefaultStyle] } : null)))
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 _Text2 = (props) => {
5
- const { allowFontScaling = false } = props;
6
- return createElement(Text, extendObject({}, props, {
5
+ const SimpleText = (props) => {
6
+ const { allowFontScaling = false, children } = props;
7
+ const innerProps = useInnerProps(extendObject({}, props, {
7
8
  allowFontScaling
8
9
  }));
10
+ return createElement(Text, innerProps, children);
9
11
  };
10
- _Text2.displayName = 'MpxSimpleText';
11
- export default _Text2;
12
+ SimpleText.displayName = 'MpxSimpleText';
13
+ export default SimpleText;
@@ -1,22 +1,18 @@
1
1
  import { View } from 'react-native';
2
- import { createElement, forwardRef, useRef } from 'react';
3
- import useNodesRef from './useNodesRef';
4
- import { extendObject, splitProps, splitStyle, wrapChildren } from './utils';
5
- const _View2 = forwardRef((simpleViewProps, ref) => {
6
- const nodeRef = useRef(null);
2
+ import { createElement } from 'react';
3
+ import { splitProps, splitStyle, wrapChildren, extendObject } from './utils';
4
+ import useInnerProps from './getInnerListeners';
5
+ const SimpleView = (simpleViewProps) => {
7
6
  const { textProps, innerProps: props = {} } = splitProps(simpleViewProps);
8
7
  const { textStyle, innerStyle = {} } = splitStyle(props.style || {});
9
- useNodesRef(props, ref, nodeRef, {
10
- style: innerStyle || {}
11
- });
12
- return createElement(View, extendObject({}, props, {
13
- style: innerStyle,
14
- ref: nodeRef
15
- }), wrapChildren(props, {
8
+ const innerProps = useInnerProps(extendObject({}, props, {
9
+ style: innerStyle
10
+ }));
11
+ return createElement(View, innerProps, wrapChildren(props, {
16
12
  hasVarDec: false,
17
13
  textStyle: textStyle,
18
14
  textProps
19
15
  }));
20
- });
21
- _View2.displayName = 'MpxSimpleView';
22
- export default _View2;
16
+ };
17
+ SimpleView.displayName = 'MpxSimpleView';
18
+ export default SimpleView;
@@ -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;