@mpxjs/webpack-plugin 2.10.5 → 2.10.6-beta.2

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 (92) hide show
  1. package/lib/dependencies/WriteVfsDependency.js +46 -0
  2. package/lib/index.js +22 -6
  3. package/lib/json-compiler/helper.js +1 -4
  4. package/lib/platform/index.js +4 -2
  5. package/lib/platform/json/wx/index.js +0 -1
  6. package/lib/platform/template/wx/component-config/button.js +1 -1
  7. package/lib/platform/template/wx/component-config/index.js +7 -3
  8. package/lib/platform/template/wx/component-config/input.js +1 -1
  9. package/lib/platform/template/wx/component-config/sticky-header.js +23 -0
  10. package/lib/platform/template/wx/component-config/sticky-section.js +23 -0
  11. package/lib/platform/template/wx/component-config/template.js +26 -1
  12. package/lib/platform/template/wx/index.js +31 -4
  13. package/lib/react/processJSON.js +7 -6
  14. package/lib/resolver/PackageEntryPlugin.js +3 -1
  15. package/lib/runtime/components/react/context.ts +12 -3
  16. package/lib/runtime/components/react/dist/context.js +4 -1
  17. package/lib/runtime/components/react/dist/mpx-button.jsx +9 -4
  18. package/lib/runtime/components/react/dist/mpx-canvas/Image.js +2 -4
  19. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +20 -17
  20. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +7 -2
  21. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +7 -2
  22. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +7 -2
  23. package/lib/runtime/components/react/dist/mpx-image.jsx +9 -2
  24. package/lib/runtime/components/react/dist/mpx-input.jsx +7 -2
  25. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +1 -1
  26. package/lib/runtime/components/react/dist/mpx-label.jsx +7 -2
  27. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +8 -3
  28. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +100 -62
  29. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +11 -13
  30. package/lib/runtime/components/react/dist/mpx-picker-view/index.jsx +8 -7
  31. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.jsx +26 -8
  32. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +9 -2
  33. package/lib/runtime/components/react/dist/mpx-radio.jsx +7 -2
  34. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +7 -2
  35. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +30 -10
  36. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +115 -0
  37. package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
  38. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +11 -9
  39. package/lib/runtime/components/react/dist/mpx-swiper.jsx +82 -36
  40. package/lib/runtime/components/react/dist/mpx-switch.jsx +7 -2
  41. package/lib/runtime/components/react/dist/mpx-text.jsx +7 -2
  42. package/lib/runtime/components/react/dist/mpx-video.jsx +7 -2
  43. package/lib/runtime/components/react/dist/mpx-view.jsx +2 -4
  44. package/lib/runtime/components/react/dist/mpx-web-view.jsx +13 -13
  45. package/lib/runtime/components/react/dist/utils.jsx +14 -3
  46. package/lib/runtime/components/react/mpx-button.tsx +12 -3
  47. package/lib/runtime/components/react/mpx-canvas/Image.ts +4 -4
  48. package/lib/runtime/components/react/mpx-canvas/index.tsx +24 -17
  49. package/lib/runtime/components/react/mpx-checkbox-group.tsx +9 -1
  50. package/lib/runtime/components/react/mpx-checkbox.tsx +9 -1
  51. package/lib/runtime/components/react/mpx-icon/index.tsx +9 -1
  52. package/lib/runtime/components/react/mpx-image.tsx +38 -19
  53. package/lib/runtime/components/react/mpx-input.tsx +10 -1
  54. package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +1 -1
  55. package/lib/runtime/components/react/mpx-label.tsx +9 -1
  56. package/lib/runtime/components/react/mpx-movable-area.tsx +8 -2
  57. package/lib/runtime/components/react/mpx-movable-view.tsx +100 -62
  58. package/lib/runtime/components/react/mpx-picker/index.tsx +18 -16
  59. package/lib/runtime/components/react/mpx-picker-view/index.tsx +22 -8
  60. package/lib/runtime/components/react/mpx-picker-view-column/index.tsx +34 -30
  61. package/lib/runtime/components/react/mpx-radio-group.tsx +20 -9
  62. package/lib/runtime/components/react/mpx-radio.tsx +9 -1
  63. package/lib/runtime/components/react/mpx-rich-text/index.tsx +10 -2
  64. package/lib/runtime/components/react/mpx-scroll-view.tsx +82 -53
  65. package/lib/runtime/components/react/mpx-sticky-header.tsx +179 -0
  66. package/lib/runtime/components/react/mpx-sticky-section.tsx +96 -0
  67. package/lib/runtime/components/react/mpx-swiper-item.tsx +11 -19
  68. package/lib/runtime/components/react/mpx-swiper.tsx +95 -38
  69. package/lib/runtime/components/react/mpx-switch.tsx +10 -2
  70. package/lib/runtime/components/react/mpx-text.tsx +10 -2
  71. package/lib/runtime/components/react/mpx-video.tsx +7 -2
  72. package/lib/runtime/components/react/mpx-view.tsx +8 -4
  73. package/lib/runtime/components/react/mpx-web-view.tsx +12 -12
  74. package/lib/runtime/components/react/utils.tsx +16 -5
  75. package/lib/runtime/components/web/mpx-scroll-view.vue +21 -4
  76. package/lib/runtime/components/web/mpx-sticky-header.vue +91 -0
  77. package/lib/runtime/components/web/mpx-sticky-section.vue +15 -0
  78. package/lib/runtime/components/web/mpx-web-view.vue +1 -1
  79. package/lib/runtime/mpxGlobal.js +1 -0
  80. package/lib/runtime/optionProcessor.d.ts +5 -0
  81. package/lib/runtime/optionProcessor.js +2 -2
  82. package/lib/template-compiler/bind-this.js +8 -7
  83. package/lib/template-compiler/compiler.js +59 -9
  84. package/lib/utils/get-template-content.js +47 -0
  85. package/lib/web/index.js +2 -0
  86. package/lib/web/processScript.js +29 -7
  87. package/lib/web/processTemplate.js +10 -4
  88. package/lib/web/template2vue.js +280 -0
  89. package/lib/web/wxml-template-loader.js +29 -0
  90. package/lib/wxs/pre-loader.js +1 -0
  91. package/package.json +4 -4
  92. package/LICENSE +0 -433
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useRef, useContext, useEffect } from 'react';
1
+ import React, { forwardRef, useRef, useContext, useEffect, createElement } from 'react';
2
2
  import { StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native';
3
3
  import { warn } from '@mpxjs/utils';
4
4
  import PickerSelector from './selector';
@@ -9,7 +9,7 @@ import PickerRegion from './region';
9
9
  import { FormContext, RouteContext } from '../context';
10
10
  import useNodesRef from '../useNodesRef';
11
11
  import useInnerProps, { getCustomEvent } from '../getInnerListeners';
12
- import { extendObject } from '../utils';
12
+ import { extendObject, useLayout } from '../utils';
13
13
  import { createPopupManager } from '../mpx-popup';
14
14
  /**
15
15
  * ✔ mode
@@ -109,17 +109,18 @@ const Picker = forwardRef((props, ref) => {
109
109
  const buttonText = buttonTextMap[global.__mpx?.i18n?.locale || 'zh-CN'];
110
110
  const pickerValue = useRef(value);
111
111
  pickerValue.current = Array.isArray(value) ? value.slice() : value;
112
- const innerLayout = useRef({});
113
112
  const nodeRef = useRef(null);
114
113
  const pickerRef = useRef(null);
115
114
  const { open, show, hide, remove } = useRef(createPopupManager()).current;
116
115
  useNodesRef(props, ref, nodeRef);
116
+ const { layoutRef, layoutProps } = useLayout({
117
+ props,
118
+ hasSelfPercent: false,
119
+ nodeRef
120
+ });
117
121
  const innerProps = useInnerProps(extendObject({}, props, {
118
122
  ref: nodeRef
119
- }), [], { layoutRef: innerLayout });
120
- const getInnerLayout = (layout) => {
121
- innerLayout.current = layout.current;
122
- };
123
+ }, layoutProps), [], { layoutRef });
123
124
  useEffect(() => {
124
125
  if (range && pickerRef.current && mode === "multiSelector" /* PickerMode.MULTI_SELECTOR */) {
125
126
  pickerRef.current.updateRange?.(range);
@@ -162,7 +163,7 @@ const Picker = forwardRef((props, ref) => {
162
163
  if (mode !== "multiSelector" /* PickerMode.MULTI_SELECTOR */) {
163
164
  return;
164
165
  }
165
- const eventData = getCustomEvent('columnchange', {}, { detail: { column: columnIndex, value }, layoutRef: innerLayout });
166
+ const eventData = getCustomEvent('columnchange', {}, { detail: { column: columnIndex, value }, layoutRef });
166
167
  props.bindcolumnchange?.(eventData);
167
168
  };
168
169
  const onCancel = () => {
@@ -170,7 +171,7 @@ const Picker = forwardRef((props, ref) => {
170
171
  hide();
171
172
  };
172
173
  const onConfirm = () => {
173
- const eventData = getCustomEvent('change', {}, { detail: { value: pickerValue.current }, layoutRef: innerLayout });
174
+ const eventData = getCustomEvent('change', {}, { detail: { value: pickerValue.current }, layoutRef });
174
175
  bindchange?.(eventData);
175
176
  hide();
176
177
  };
@@ -179,7 +180,6 @@ const Picker = forwardRef((props, ref) => {
179
180
  children,
180
181
  bindchange: onChange,
181
182
  bindcolumnchange: onColumnChange,
182
- getInnerLayout,
183
183
  getRange: () => range
184
184
  });
185
185
  const renderPickerContent = () => {
@@ -215,9 +215,7 @@ const Picker = forwardRef((props, ref) => {
215
215
  remove();
216
216
  };
217
217
  }, []);
218
- return (<TouchableWithoutFeedback onPress={show}>
219
- {children}
220
- </TouchableWithoutFeedback>);
218
+ return createElement(TouchableWithoutFeedback, { onPress: show }, createElement(View, innerProps, children));
221
219
  });
222
220
  Picker.displayName = 'MpxPicker';
223
221
  export default Picker;
@@ -1,9 +1,10 @@
1
1
  import { View } from 'react-native';
2
- import React, { forwardRef, useRef } from 'react';
2
+ import React, { createElement, forwardRef, useRef } from 'react';
3
3
  import useInnerProps, { getCustomEvent } from '../getInnerListeners';
4
4
  import useNodesRef from '../useNodesRef';
5
5
  import { useLayout, splitProps, splitStyle, wrapChildren, useTransformStyle, extendObject } from '../utils';
6
6
  import { PickerViewStyleContext } from './pickerVIewContext';
7
+ import Portal from '../mpx-portal';
7
8
  const styles = {
8
9
  wrapper: {
9
10
  display: 'flex',
@@ -23,7 +24,7 @@ const _PickerView = forwardRef((props, ref) => {
23
24
  const activeValueRef = useRef(value);
24
25
  activeValueRef.current = value.slice();
25
26
  const snapActiveValueRef = useRef(null);
26
- const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext });
27
+ const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight, hasPositionFixed } = useTransformStyle(style, { enableVar, externalVarContext });
27
28
  useNodesRef(props, ref, nodeRef, {
28
29
  style: normalStyle
29
30
  });
@@ -114,11 +115,11 @@ const _PickerView = forwardRef((props, ref) => {
114
115
  onInitialChange(isInvalid, validValue);
115
116
  return renderColumns;
116
117
  };
117
- return (<PickerViewStyleContext.Provider value={textStyle}>
118
- <View {...innerProps}>
119
- <View style={[styles.wrapper]}>{renderPickerColumns()}</View>
120
- </View>
121
- </PickerViewStyleContext.Provider>);
118
+ const finalComponent = createElement(PickerViewStyleContext.Provider, { value: textStyle }, createElement(View, innerProps, createElement(View, { style: [styles.wrapper] }, renderPickerColumns())));
119
+ if (hasPositionFixed) {
120
+ return createElement(Portal, null, finalComponent);
121
+ }
122
+ return finalComponent;
122
123
  });
123
124
  _PickerView.displayName = 'MpxPickerView';
124
125
  export default _PickerView;
@@ -1,7 +1,7 @@
1
- import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react';
1
+ import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback, createElement } from 'react';
2
2
  import { StyleSheet, View } from 'react-native';
3
3
  import Reanimated, { useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated';
4
- import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, isHarmony } from '../utils';
4
+ import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, isHarmony, extendObject } from '../utils';
5
5
  import useNodesRef from '../useNodesRef';
6
6
  import PickerIndicator from './pickerViewIndicator';
7
7
  import PickerMask from './pickerViewMask';
@@ -220,11 +220,28 @@ const _PickerViewColumn = forwardRef((props, ref) => {
220
220
  return (<MpxPickerVIewColumnItem key={index} item={item} index={index} itemHeight={itemHeight} textStyle={textStyle} textProps={textProps} visibleCount={visibleCount} onItemLayout={onItemLayout}/>);
221
221
  });
222
222
  const renderScollView = () => {
223
- return (<PickerViewColumnAnimationContext.Provider value={offsetYShared}>
224
- <Reanimated.ScrollView ref={scrollViewRef} bounces={true} horizontal={false} nestedScrollEnabled={true} removeClippedSubviews={false} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} scrollEventThrottle={16} {...layoutProps} onTouchEnd={onClickOnceItem} style={[{ width: '100%' }]} decelerationRate="fast" snapToOffsets={snapToOffsets} onScroll={onScroll} onScrollBeginDrag={onScrollBeginDrag} onScrollEndDrag={onScrollEndDrag} onMomentumScrollBegin={onMomentumScrollBegin} onMomentumScrollEnd={onMomentumScrollEnd} onContentSizeChange={onContentSizeChange} contentContainerStyle={contentContainerStyle}>
225
- {renderInnerchild()}
226
- </Reanimated.ScrollView>
227
- </PickerViewColumnAnimationContext.Provider>);
223
+ const innerProps = extendObject({}, layoutProps, {
224
+ ref: scrollViewRef,
225
+ bounces: true,
226
+ horizontal: false,
227
+ nestedScrollEnabled: true,
228
+ removeClippedSubviews: false,
229
+ showsVerticalScrollIndicator: false,
230
+ showsHorizontalScrollIndicator: false,
231
+ scrollEventThrottle: 16,
232
+ style: styles.scrollView,
233
+ decelerationRate: 'fast',
234
+ snapToOffsets: snapToOffsets,
235
+ onTouchEnd: onClickOnceItem,
236
+ onScroll,
237
+ onScrollBeginDrag,
238
+ onScrollEndDrag,
239
+ onMomentumScrollBegin,
240
+ onMomentumScrollEnd,
241
+ onContentSizeChange,
242
+ contentContainerStyle
243
+ });
244
+ return createElement(PickerViewColumnAnimationContext.Provider, { value: offsetYShared }, createElement(Reanimated.ScrollView, innerProps, renderInnerchild()));
228
245
  };
229
246
  const renderIndicator = () => (<PickerIndicator itemHeight={itemHeight} indicatorItemStyle={pickerIndicatorStyle}/>);
230
247
  const renderMask = () => (<PickerMask itemHeight={itemHeight} maskContainerStyle={pickerMaskStyle}/>);
@@ -235,7 +252,8 @@ const _PickerViewColumn = forwardRef((props, ref) => {
235
252
  </View>);
236
253
  });
237
254
  const styles = StyleSheet.create({
238
- wrapper: { display: 'flex', flex: 1 }
255
+ wrapper: { display: 'flex', flex: 1 },
256
+ scrollView: { width: '100%' }
239
257
  });
240
258
  _PickerViewColumn.displayName = 'MpxPickerViewColumn';
241
259
  export default _PickerViewColumn;
@@ -8,6 +8,7 @@ import { FormContext, RadioGroupContext } from './context';
8
8
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
9
9
  import useNodesRef from './useNodesRef';
10
10
  import { useLayout, useTransformStyle, wrapChildren, extendObject } from './utils';
11
+ import Portal from './mpx-portal';
11
12
  const radioGroup = forwardRef((props, ref) => {
12
13
  const { style = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
13
14
  const propsRef = useRef({});
@@ -23,7 +24,7 @@ const radioGroup = forwardRef((props, ref) => {
23
24
  flexWrap: 'wrap'
24
25
  };
25
26
  const styleObj = extendObject({}, defaultStyle, style);
26
- const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
27
+ const { hasPositionFixed, hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
27
28
  const nodeRef = useRef(null);
28
29
  useNodesRef(props, ref, nodeRef, { style: normalStyle });
29
30
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef });
@@ -77,10 +78,16 @@ const radioGroup = forwardRef((props, ref) => {
77
78
  }), ['name'], {
78
79
  layoutRef
79
80
  });
80
- return createElement(View, innerProps, createElement(RadioGroupContext.Provider, { value: contextValue }, wrapChildren(props, {
81
+ const finalComponent = createElement(View, innerProps, createElement(RadioGroupContext.Provider, {
82
+ value: contextValue
83
+ }, wrapChildren(props, {
81
84
  hasVarDec,
82
85
  varContext: varContextRef.current
83
86
  })));
87
+ if (hasPositionFixed) {
88
+ return createElement(Portal, null, finalComponent);
89
+ }
90
+ return finalComponent;
84
91
  });
85
92
  radioGroup.displayName = 'MpxRadioGroup';
86
93
  export default radioGroup;
@@ -12,6 +12,7 @@ import useInnerProps, { getCustomEvent } from './getInnerListeners';
12
12
  import useNodesRef from './useNodesRef';
13
13
  import { splitProps, splitStyle, useLayout, useTransformStyle, wrapChildren, extendObject } from './utils';
14
14
  import Icon from './mpx-icon';
15
+ import Portal from './mpx-portal';
15
16
  const styles = StyleSheet.create({
16
17
  container: {
17
18
  flexDirection: 'row',
@@ -73,7 +74,7 @@ const Radio = forwardRef((radioProps, ref) => {
73
74
  bindtap && bindtap(getCustomEvent('tap', evt, { layoutRef }, props));
74
75
  onChange(evt);
75
76
  };
76
- const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
77
+ const { hasPositionFixed, hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
77
78
  const { textStyle, backgroundStyle, innerStyle = {} } = splitStyle(normalStyle);
78
79
  if (backgroundStyle) {
79
80
  warn('Radio does not support background image-related styles!');
@@ -123,7 +124,7 @@ const Radio = forwardRef((radioProps, ref) => {
123
124
  }
124
125
  }
125
126
  }, [checked]);
126
- return createElement(View, innerProps, createElement(View, { style: defaultStyle }, createElement(Icon, {
127
+ const finalComponent = createElement(View, innerProps, createElement(View, { style: defaultStyle }, createElement(Icon, {
127
128
  type: 'success',
128
129
  size: 24,
129
130
  color: disabled ? '#E1E1E1' : color,
@@ -134,6 +135,10 @@ const Radio = forwardRef((radioProps, ref) => {
134
135
  textStyle,
135
136
  textProps
136
137
  }));
138
+ if (hasPositionFixed) {
139
+ return createElement(Portal, null, finalComponent);
140
+ }
141
+ return finalComponent;
137
142
  });
138
143
  Radio.displayName = 'MpxRadio';
139
144
  export default Radio;
@@ -8,6 +8,7 @@ import useNodesRef from '../useNodesRef'; // 引入辅助函数
8
8
  import { useTransformStyle, useLayout, extendObject } from '../utils';
9
9
  import { WebView } from 'react-native-webview';
10
10
  import { generateHTML } from './html';
11
+ import Portal from '../mpx-portal';
11
12
  function jsonToHtmlStr(elements) {
12
13
  let htmlStr = '';
13
14
  for (const element of elements) {
@@ -30,7 +31,7 @@ const _RichText = forwardRef((props, ref) => {
30
31
  const { style = {}, nodes, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
31
32
  const nodeRef = useRef(null);
32
33
  const [webViewHeight, setWebViewHeight] = useState(0);
33
- const { normalStyle, hasSelfPercent, setWidth, setHeight } = useTransformStyle(Object.assign({
34
+ const { normalStyle, hasSelfPercent, setWidth, setHeight, hasPositionFixed } = useTransformStyle(Object.assign({
34
35
  width: '100%',
35
36
  height: webViewHeight
36
37
  }, style), {
@@ -51,12 +52,16 @@ const _RichText = forwardRef((props, ref) => {
51
52
  layoutRef
52
53
  });
53
54
  const html = typeof nodes === 'string' ? nodes : jsonToHtmlStr(nodes);
54
- return createElement(View, innerProps, createElement(WebView, {
55
+ let finalComponent = createElement(View, innerProps, createElement(WebView, {
55
56
  source: { html: generateHTML(html) },
56
57
  onMessage: (event) => {
57
58
  setWebViewHeight(+event.nativeEvent.data);
58
59
  }
59
60
  }));
61
+ if (hasPositionFixed) {
62
+ finalComponent = createElement(Portal, null, finalComponent);
63
+ }
64
+ return finalComponent;
60
65
  });
61
66
  _RichText.displayName = 'mpx-rich-text';
62
67
  export default _RichText;
@@ -32,16 +32,20 @@
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
- import { warn } from '@mpxjs/utils';
38
+ import { warn, hasOwn } from '@mpxjs/utils';
38
39
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
39
40
  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';
43
+ import Portal from './mpx-portal';
44
+ const AnimatedScrollView = RNAnimated.createAnimatedComponent(ScrollView);
42
45
  const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
43
46
  const { textProps, innerProps: props = {} } = splitProps(scrollViewProps);
44
- const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, '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, __selectRef } = props;
48
+ const scrollOffset = useRef(new RNAnimated.Value(0)).current;
45
49
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
46
50
  const waitForHandlers = flatGesture(waitFor);
47
51
  const snapScrollTop = useRef(0);
@@ -72,7 +76,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
72
76
  };
73
77
  const { refresherContent, otherContent } = getRefresherContent(props.children);
74
78
  const hasRefresher = refresherContent && refresherEnabled;
75
- const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
79
+ const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, hasPositionFixed, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
76
80
  const { textStyle, innerStyle = {} } = splitStyle(normalStyle);
77
81
  const scrollViewRef = useAnimatedRef();
78
82
  useNodesRef(props, ref, scrollViewRef, {
@@ -89,12 +93,13 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
89
93
  },
90
94
  gestureRef: scrollViewRef
91
95
  });
96
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
92
97
  const contextValue = useMemo(() => {
93
98
  return {
94
- gestureRef: scrollViewRef
99
+ gestureRef: scrollViewRef,
100
+ scrollOffset
95
101
  };
96
102
  }, []);
97
- const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
98
103
  const hasRefresherLayoutRef = useRef(false);
99
104
  // layout 完成前先隐藏,避免安卓闪烁问题
100
105
  const refresherLayoutStyle = useMemo(() => { return !hasRefresherLayoutRef.current ? HIDDEN_STYLE : {}; }, [hasRefresherLayoutRef.current]);
@@ -320,6 +325,12 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
320
325
  updateScrollOptions(e, { scrollLeft, scrollTop });
321
326
  updateIntersection();
322
327
  }
328
+ const scrollHandler = RNAnimated.event([{ nativeEvent: { contentOffset: { y: scrollOffset } } }], {
329
+ useNativeDriver: true,
330
+ listener: (event) => {
331
+ onScroll(event);
332
+ }
333
+ });
323
334
  function onScrollDragStart(e) {
324
335
  hasCallScrollToLower.current = false;
325
336
  hasCallScrollToUpper.current = false;
@@ -464,7 +475,11 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
464
475
  })
465
476
  .simultaneousWithExternalGesture(scrollViewRef);
466
477
  const scrollAdditionalProps = extendObject({
467
- style: extendObject({}, innerStyle, layoutStyle),
478
+ style: extendObject(hasOwn(innerStyle, 'flex') || hasOwn(innerStyle, 'flexGrow')
479
+ ? {}
480
+ : {
481
+ flexGrow: 0
482
+ }, innerStyle, layoutStyle),
468
483
  pinchGestureEnabled: false,
469
484
  alwaysBounceVertical: false,
470
485
  alwaysBounceHorizontal: false,
@@ -476,7 +491,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
476
491
  scrollEnabled: !enableScroll ? false : !!(scrollX || scrollY),
477
492
  bounces: false,
478
493
  ref: scrollViewRef,
479
- onScroll: onScroll,
494
+ onScroll: enableSticky ? scrollHandler : onScroll,
480
495
  onContentSizeChange: onContentSizeChange,
481
496
  bindtouchstart: ((enhanced && binddragstart) || bindtouchstart) && onScrollTouchStart,
482
497
  bindtouchmove: ((enhanced && binddragging) || bindtouchmove) && onScrollTouchMove,
@@ -518,13 +533,14 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
518
533
  'bindscrolltolower',
519
534
  'bindrefresherrefresh'
520
535
  ], { layoutRef });
521
- const withRefresherScrollView = createElement(GestureDetector, { gesture: panGesture }, createElement(ScrollView, 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 }), {
536
+ const ScrollViewComponent = enableSticky ? AnimatedScrollView : ScrollView;
537
+ 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 }), {
522
538
  hasVarDec,
523
539
  varContext: varContextRef.current,
524
540
  textStyle,
525
541
  textProps
526
542
  })))));
527
- const commonScrollView = createElement(ScrollView, extendObject(innerProps, {
543
+ const commonScrollView = createElement(ScrollViewComponent, extendObject({}, innerProps, {
528
544
  refreshControl: refresherEnabled
529
545
  ? createElement(RefreshControl, extendObject({
530
546
  progressBackgroundColor: refresherBackground,
@@ -540,7 +556,11 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
540
556
  textStyle,
541
557
  textProps
542
558
  })));
543
- return hasRefresher ? withRefresherScrollView : commonScrollView;
559
+ let scrollViewComponent = hasRefresher ? withRefresherScrollView : commonScrollView;
560
+ if (hasPositionFixed) {
561
+ scrollViewComponent = createElement(Portal, null, scrollViewComponent);
562
+ }
563
+ return scrollViewComponent;
544
564
  });
545
565
  _ScrollView.displayName = 'MpxScrollView';
546
566
  export default _ScrollView;
@@ -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;
@@ -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;
@@ -1,5 +1,5 @@
1
1
  import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated';
2
- import { forwardRef, useRef, useContext } from 'react';
2
+ import { forwardRef, useRef, useContext, createElement } from 'react';
3
3
  import useInnerProps from './getInnerListeners';
4
4
  import useNodesRef from './useNodesRef'; // 引入辅助函数
5
5
  import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout, extendObject } from './utils';
@@ -45,14 +45,16 @@ const _SwiperItem = forwardRef((props, ref) => {
45
45
  transform: transformStyle
46
46
  });
47
47
  });
48
- return (<Animated.View {...innerProps} style={[innerStyle, layoutStyle, itemAnimatedStyle, customStyle]} data-itemId={props['item-id']}>
49
- {wrapChildren(props, {
50
- hasVarDec,
51
- varContext: varContextRef.current,
52
- textStyle,
53
- textProps
54
- })}
55
- </Animated.View>);
48
+ const mergeProps = extendObject({}, innerProps, {
49
+ style: [innerStyle, layoutStyle, itemAnimatedStyle, customStyle],
50
+ 'data-itemId': props['item-id']
51
+ });
52
+ return createElement(Animated.View, mergeProps, wrapChildren(props, {
53
+ hasVarDec,
54
+ varContext: varContextRef.current,
55
+ textStyle,
56
+ textProps
57
+ }));
56
58
  });
57
59
  _SwiperItem.displayName = 'MpxSwiperItem';
58
60
  export default _SwiperItem;