@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,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 { GestureResponderEvent, LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, ScrollView, StyleSheet, View } from 'react-native'
3
3
  import Reanimated, { AnimatedRef, 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, { HandlerRef } from '../useNodesRef'
6
6
  import PickerIndicator from './pickerViewIndicator'
7
7
  import PickerMask from './pickerViewMask'
@@ -302,33 +302,36 @@ const _PickerViewColumn = forwardRef<HandlerRef<ScrollView & View, ColumnProps>,
302
302
  })
303
303
 
304
304
  const renderScollView = () => {
305
- return (
306
- <PickerViewColumnAnimationContext.Provider value={offsetYShared}>
307
- <Reanimated.ScrollView
308
- ref={scrollViewRef}
309
- bounces={true}
310
- horizontal={false}
311
- nestedScrollEnabled={true}
312
- removeClippedSubviews={false}
313
- showsVerticalScrollIndicator={false}
314
- showsHorizontalScrollIndicator={false}
315
- scrollEventThrottle={16}
316
- {...layoutProps}
317
- onTouchEnd={onClickOnceItem}
318
- style={[{ width: '100%' }]}
319
- decelerationRate="fast"
320
- snapToOffsets={snapToOffsets}
321
- onScroll={onScroll}
322
- onScrollBeginDrag={onScrollBeginDrag}
323
- onScrollEndDrag={onScrollEndDrag}
324
- onMomentumScrollBegin={onMomentumScrollBegin}
325
- onMomentumScrollEnd={onMomentumScrollEnd}
326
- onContentSizeChange={onContentSizeChange}
327
- contentContainerStyle={contentContainerStyle}
328
- >
329
- {renderInnerchild()}
330
- </Reanimated.ScrollView>
331
- </PickerViewColumnAnimationContext.Provider>
305
+ const innerProps = extendObject({}, layoutProps, {
306
+ ref: scrollViewRef,
307
+ bounces: true,
308
+ horizontal: false,
309
+ nestedScrollEnabled: true,
310
+ removeClippedSubviews: false,
311
+ showsVerticalScrollIndicator: false,
312
+ showsHorizontalScrollIndicator: false,
313
+ scrollEventThrottle: 16,
314
+ style: styles.scrollView,
315
+ decelerationRate: 'fast',
316
+ snapToOffsets: snapToOffsets,
317
+ onTouchEnd: onClickOnceItem,
318
+ onScroll,
319
+ onScrollBeginDrag,
320
+ onScrollEndDrag,
321
+ onMomentumScrollBegin,
322
+ onMomentumScrollEnd,
323
+ onContentSizeChange,
324
+ contentContainerStyle
325
+ }) as React.ComponentProps<typeof Reanimated.ScrollView>
326
+
327
+ return createElement(
328
+ PickerViewColumnAnimationContext.Provider,
329
+ { value: offsetYShared },
330
+ createElement(
331
+ Reanimated.ScrollView,
332
+ innerProps,
333
+ renderInnerchild()
334
+ )
332
335
  )
333
336
  }
334
337
 
@@ -356,7 +359,8 @@ const _PickerViewColumn = forwardRef<HandlerRef<ScrollView & View, ColumnProps>,
356
359
  })
357
360
 
358
361
  const styles = StyleSheet.create({
359
- wrapper: { display: 'flex', flex: 1 }
362
+ wrapper: { display: 'flex', flex: 1 },
363
+ scrollView: { width: '100%' }
360
364
  })
361
365
 
362
366
  _PickerViewColumn.displayName = 'MpxPickerViewColumn'
@@ -22,6 +22,7 @@ import { FormContext, FormFieldValue, RadioGroupContext, GroupValue } from './co
22
22
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
23
23
  import useNodesRef, { HandlerRef } from './useNodesRef'
24
24
  import { useLayout, useTransformStyle, wrapChildren, extendObject } from './utils'
25
+ import Portal from './mpx-portal'
25
26
 
26
27
  export interface RadioGroupProps {
27
28
  name: string
@@ -71,6 +72,7 @@ const radioGroup = forwardRef<
71
72
  const styleObj = extendObject({}, defaultStyle, style)
72
73
 
73
74
  const {
75
+ hasPositionFixed,
74
76
  hasSelfPercent,
75
77
  normalStyle,
76
78
  hasVarDec,
@@ -156,18 +158,27 @@ const radioGroup = forwardRef<
156
158
  }
157
159
  )
158
160
 
159
- return createElement(View, innerProps, createElement(
160
- RadioGroupContext.Provider,
161
- { value: contextValue },
162
- wrapChildren(
163
- props,
161
+ const finalComponent = createElement(View, innerProps,
162
+ createElement(
163
+ RadioGroupContext.Provider,
164
164
  {
165
- hasVarDec,
166
- varContext: varContextRef.current
167
- }
165
+ value: contextValue
166
+ },
167
+ wrapChildren(
168
+ props,
169
+ {
170
+ hasVarDec,
171
+ varContext: varContextRef.current
172
+ }
173
+ )
168
174
  )
169
175
  )
170
- )
176
+
177
+ if (hasPositionFixed) {
178
+ return createElement(Portal, null, finalComponent)
179
+ }
180
+
181
+ return finalComponent
171
182
  })
172
183
 
173
184
  radioGroup.displayName = 'MpxRadioGroup'
@@ -12,6 +12,7 @@ import useInnerProps, { getCustomEvent } from './getInnerListeners'
12
12
  import useNodesRef, { HandlerRef } 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
 
16
17
  export interface RadioProps {
17
18
  value?: string
@@ -117,6 +118,7 @@ const Radio = forwardRef<HandlerRef<View, RadioProps>, RadioProps>(
117
118
  }
118
119
 
119
120
  const {
121
+ hasPositionFixed,
120
122
  hasSelfPercent,
121
123
  normalStyle,
122
124
  hasVarDec,
@@ -192,7 +194,7 @@ const Radio = forwardRef<HandlerRef<View, RadioProps>, RadioProps>(
192
194
  }
193
195
  }, [checked])
194
196
 
195
- return createElement(View, innerProps,
197
+ const finalComponent = createElement(View, innerProps,
196
198
  createElement(
197
199
  View,
198
200
  { style: defaultStyle },
@@ -213,6 +215,12 @@ const Radio = forwardRef<HandlerRef<View, RadioProps>, RadioProps>(
213
215
  }
214
216
  )
215
217
  )
218
+
219
+ if (hasPositionFixed) {
220
+ return createElement(Portal, null, finalComponent)
221
+ }
222
+
223
+ return finalComponent
216
224
  }
217
225
  )
218
226
 
@@ -9,6 +9,7 @@ import useNodesRef, { HandlerRef } from '../useNodesRef' // 引入辅助函数
9
9
  import { useTransformStyle, useLayout, extendObject } from '../utils'
10
10
  import { WebView, WebViewMessageEvent } from 'react-native-webview'
11
11
  import { generateHTML } from './html'
12
+ import Portal from '../mpx-portal'
12
13
 
13
14
  type Node = {
14
15
  type: 'node' | 'text'
@@ -69,7 +70,8 @@ const _RichText = forwardRef<HandlerRef<View, _RichTextProps>, _RichTextProps>((
69
70
  normalStyle,
70
71
  hasSelfPercent,
71
72
  setWidth,
72
- setHeight
73
+ setHeight,
74
+ hasPositionFixed
73
75
  } = useTransformStyle(Object.assign({
74
76
  width: '100%',
75
77
  height: webViewHeight
@@ -109,7 +111,7 @@ const _RichText = forwardRef<HandlerRef<View, _RichTextProps>, _RichTextProps>((
109
111
 
110
112
  const html: string = typeof nodes === 'string' ? nodes : jsonToHtmlStr(nodes)
111
113
 
112
- return createElement(View, innerProps,
114
+ let finalComponent: JSX.Element = createElement(View, innerProps,
113
115
  createElement(WebView, {
114
116
  source: { html: generateHTML(html) },
115
117
  onMessage: (event: WebViewMessageEvent) => {
@@ -117,6 +119,12 @@ const _RichText = forwardRef<HandlerRef<View, _RichTextProps>, _RichTextProps>((
117
119
  }
118
120
  })
119
121
  )
122
+
123
+ if (hasPositionFixed) {
124
+ finalComponent = createElement(Portal, null, finalComponent)
125
+ }
126
+
127
+ return finalComponent
120
128
  })
121
129
 
122
130
  _RichText.displayName = 'mpx-rich-text'
@@ -32,58 +32,59 @@
32
32
  * ✔ bindscroll
33
33
  */
34
34
  import { ScrollView, RefreshControl, Gesture, GestureDetector } from 'react-native-gesture-handler'
35
- import { View, NativeSyntheticEvent, NativeScrollEvent, LayoutChangeEvent, ViewStyle } from 'react-native'
35
+ import { View, NativeSyntheticEvent, NativeScrollEvent, LayoutChangeEvent, ViewStyle, Animated as RNAnimated } from 'react-native'
36
36
  import { isValidElement, Children, JSX, ReactNode, RefObject, useRef, useState, useEffect, forwardRef, useContext, useMemo, createElement } from 'react'
37
37
  import Animated, { useAnimatedRef, useSharedValue, withTiming, useAnimatedStyle, runOnJS } from 'react-native-reanimated'
38
- import { warn } from '@mpxjs/utils'
38
+ import { warn, hasOwn } from '@mpxjs/utils'
39
39
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
40
40
  import useNodesRef, { HandlerRef } from './useNodesRef'
41
41
  import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, GestureHandler, HIDDEN_STYLE } from './utils'
42
42
  import { IntersectionObserverContext, ScrollViewContext } from './context'
43
+ import Portal from './mpx-portal'
43
44
 
44
45
  interface ScrollViewProps {
45
- children?: ReactNode
46
- enhanced?: boolean
47
- bounces?: boolean
48
- style?: ViewStyle
49
- scrollEventThrottle?: number
50
- 'scroll-x'?: boolean
51
- 'scroll-y'?: boolean
52
- 'enable-back-to-top'?: boolean
53
- 'show-scrollbar'?: boolean
54
- 'paging-enabled'?: boolean
55
- 'upper-threshold'?: number
56
- 'lower-threshold'?: number
57
- 'scroll-with-animation'?: boolean
58
- 'refresher-triggered'?: boolean
59
- 'refresher-enabled'?: boolean
60
- 'refresher-default-style'?: 'black' | 'white' | 'none'
61
- 'refresher-background'?: string
62
- 'refresher-threshold'?: number
63
- 'scroll-top'?: number
64
- 'scroll-left'?: number
65
- 'enable-offset'?: boolean
66
- 'scroll-into-view'?: string
67
- 'enable-trigger-intersection-observer'?: boolean
68
- 'enable-var'?: boolean
69
- 'external-var-context'?: Record<string, any>
70
- 'parent-font-size'?: number
71
- 'parent-width'?: number
72
- 'parent-height'?: number
73
- 'wait-for'?: Array<GestureHandler>
74
- 'simultaneous-handlers'?: Array<GestureHandler>
75
- 'scroll-event-throttle'?: number
76
- bindscrolltoupper?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void
77
- bindscrolltolower?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void
78
- bindscroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void
79
- bindrefresherrefresh?: (event: NativeSyntheticEvent<unknown>) => void
80
- binddragstart?: (event: NativeSyntheticEvent<DragEvent>) => void
81
- binddragging?: (event: NativeSyntheticEvent<DragEvent>) => void
82
- binddragend?: (event: NativeSyntheticEvent<DragEvent>) => void
83
- bindtouchstart?: (event: NativeSyntheticEvent<TouchEvent>) => void
84
- bindtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void
85
- bindtouchend?: (event: NativeSyntheticEvent<TouchEvent>) => void
86
- bindscrollend?: (event: NativeSyntheticEvent<TouchEvent>) => void
46
+ children?: ReactNode;
47
+ enhanced?: boolean;
48
+ bounces?: boolean;
49
+ style?: ViewStyle;
50
+ 'scroll-x'?: boolean;
51
+ 'scroll-y'?: boolean;
52
+ 'enable-back-to-top'?: boolean;
53
+ 'show-scrollbar'?: boolean;
54
+ 'paging-enabled'?: boolean;
55
+ 'upper-threshold'?: number;
56
+ 'lower-threshold'?: number;
57
+ 'scroll-with-animation'?: boolean;
58
+ 'refresher-triggered'?: boolean;
59
+ 'refresher-enabled'?: boolean;
60
+ 'refresher-default-style'?: 'black' | 'white' | 'none';
61
+ 'refresher-background'?: string;
62
+ 'refresher-threshold'?: number;
63
+ 'scroll-top'?: number;
64
+ 'scroll-left'?: number;
65
+ 'enable-offset'?: boolean;
66
+ 'scroll-into-view'?: string;
67
+ 'enable-trigger-intersection-observer'?: boolean;
68
+ 'enable-var'?: boolean;
69
+ 'external-var-context'?: Record<string, any>;
70
+ 'parent-font-size'?: number;
71
+ 'parent-width'?: number;
72
+ 'parent-height'?: number;
73
+ 'enable-sticky'?: boolean;
74
+ 'wait-for'?: Array<GestureHandler>;
75
+ 'simultaneous-handlers'?: Array<GestureHandler>;
76
+ 'scroll-event-throttle'?:number;
77
+ bindscrolltoupper?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
78
+ bindscrolltolower?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
79
+ bindscroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
80
+ bindrefresherrefresh?: (event: NativeSyntheticEvent<unknown>) => void;
81
+ binddragstart?: (event: NativeSyntheticEvent<DragEvent>) => void;
82
+ binddragging?: (event: NativeSyntheticEvent<DragEvent>) => void;
83
+ binddragend?: (event: NativeSyntheticEvent<DragEvent>) => void;
84
+ bindtouchstart?: (event: NativeSyntheticEvent<TouchEvent>) => void;
85
+ bindtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
86
+ bindtouchend?: (event: NativeSyntheticEvent<TouchEvent>) => void;
87
+ bindscrollend?: (event: NativeSyntheticEvent<TouchEvent>) => void;
87
88
  __selectRef?: (selector: string, nodeType: 'node' | 'component', all?: boolean) => HandlerRef<any, any>
88
89
  }
89
90
  type ScrollAdditionalProps = {
@@ -108,6 +109,8 @@ type ScrollAdditionalProps = {
108
109
  onMomentumScrollEnd?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void
109
110
  }
110
111
 
112
+ const AnimatedScrollView = RNAnimated.createAnimatedComponent(ScrollView) as React.ComponentType<any>
113
+
111
114
  const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, ScrollViewProps>((scrollViewProps: ScrollViewProps = {}, ref): JSX.Element => {
112
115
  const { textProps, innerProps: props = {} } = splitProps(scrollViewProps)
113
116
  const {
@@ -144,10 +147,13 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
144
147
  'parent-height': parentHeight,
145
148
  'simultaneous-handlers': originSimultaneousHandlers,
146
149
  'wait-for': waitFor,
150
+ 'enable-sticky': enableSticky,
147
151
  'scroll-event-throttle': scrollEventThrottle = 0,
148
152
  __selectRef
149
153
  } = props
150
154
 
155
+ const scrollOffset = useRef(new RNAnimated.Value(0)).current
156
+
151
157
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers)
152
158
  const waitForHandlers = flatGesture(waitFor)
153
159
 
@@ -194,6 +200,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
194
200
  hasVarDec,
195
201
  varContextRef,
196
202
  hasSelfPercent,
203
+ hasPositionFixed,
197
204
  setWidth,
198
205
  setHeight
199
206
  } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
@@ -216,14 +223,15 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
216
223
  gestureRef: scrollViewRef
217
224
  })
218
225
 
226
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout })
227
+
219
228
  const contextValue = useMemo(() => {
220
229
  return {
221
- gestureRef: scrollViewRef
230
+ gestureRef: scrollViewRef,
231
+ scrollOffset
222
232
  }
223
233
  }, [])
224
234
 
225
- const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout })
226
-
227
235
  const hasRefresherLayoutRef = useRef(false)
228
236
 
229
237
  // layout 完成前先隐藏,避免安卓闪烁问题
@@ -485,6 +493,16 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
485
493
  updateIntersection()
486
494
  }
487
495
 
496
+ const scrollHandler = RNAnimated.event(
497
+ [{ nativeEvent: { contentOffset: { y: scrollOffset } } }],
498
+ {
499
+ useNativeDriver: true,
500
+ listener: (event: NativeSyntheticEvent<NativeScrollEvent>) => {
501
+ onScroll(event)
502
+ }
503
+ }
504
+ )
505
+
488
506
  function onScrollDragStart (e: NativeSyntheticEvent<NativeScrollEvent>) {
489
507
  hasCallScrollToLower.current = false
490
508
  hasCallScrollToUpper.current = false
@@ -643,7 +661,11 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
643
661
 
644
662
  const scrollAdditionalProps: ScrollAdditionalProps = extendObject(
645
663
  {
646
- style: extendObject({}, innerStyle, layoutStyle),
664
+ style: extendObject(hasOwn(innerStyle, 'flex') || hasOwn(innerStyle, 'flexGrow')
665
+ ? {}
666
+ : {
667
+ flexGrow: 0
668
+ }, innerStyle, layoutStyle),
647
669
  pinchGestureEnabled: false,
648
670
  alwaysBounceVertical: false,
649
671
  alwaysBounceHorizontal: false,
@@ -655,7 +677,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
655
677
  scrollEnabled: !enableScroll ? false : !!(scrollX || scrollY),
656
678
  bounces: false,
657
679
  ref: scrollViewRef,
658
- onScroll: onScroll,
680
+ onScroll: enableSticky ? scrollHandler : onScroll,
659
681
  onContentSizeChange: onContentSizeChange,
660
682
  bindtouchstart: ((enhanced && binddragstart) || bindtouchstart) && onScrollTouchStart,
661
683
  bindtouchmove: ((enhanced && binddragging) || bindtouchmove) && onScrollTouchMove,
@@ -710,11 +732,13 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
710
732
  'bindrefresherrefresh'
711
733
  ], { layoutRef })
712
734
 
735
+ const ScrollViewComponent = enableSticky ? AnimatedScrollView : ScrollView
736
+
713
737
  const withRefresherScrollView = createElement(
714
738
  GestureDetector,
715
739
  { gesture: panGesture },
716
740
  createElement(
717
- ScrollView,
741
+ ScrollViewComponent,
718
742
  innerProps,
719
743
  createElement(
720
744
  Animated.View,
@@ -742,8 +766,8 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
742
766
  )
743
767
 
744
768
  const commonScrollView = createElement(
745
- ScrollView,
746
- extendObject(innerProps, {
769
+ ScrollViewComponent,
770
+ extendObject({}, innerProps, {
747
771
  refreshControl: refresherEnabled
748
772
  ? createElement(RefreshControl, extendObject({
749
773
  progressBackgroundColor: refresherBackground,
@@ -764,7 +788,12 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
764
788
  )
765
789
  )
766
790
 
767
- return hasRefresher ? withRefresherScrollView : commonScrollView
791
+ let scrollViewComponent = hasRefresher ? withRefresherScrollView : commonScrollView
792
+
793
+ if (hasPositionFixed) {
794
+ scrollViewComponent = createElement(Portal, null, scrollViewComponent)
795
+ }
796
+ return scrollViewComponent
768
797
  })
769
798
 
770
799
  _ScrollView.displayName = 'MpxScrollView'
@@ -0,0 +1,179 @@
1
+ import { useEffect, useRef, useContext, forwardRef, useMemo, createElement, ReactNode, useId } from 'react'
2
+ import { Animated, StyleSheet, View, NativeSyntheticEvent, ViewStyle, LayoutChangeEvent, useAnimatedValue } from 'react-native'
3
+ import { ScrollViewContext, StickyContext } from './context'
4
+ import useNodesRef, { HandlerRef } 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
+
9
+ interface StickyHeaderProps {
10
+ children?: ReactNode;
11
+ style?: ViewStyle;
12
+ padding?: [number, number, number, number];
13
+ 'offset-top'?: number;
14
+ 'enable-var'?: boolean;
15
+ 'external-var-context'?: Record<string, any>;
16
+ 'parent-font-size'?: number;
17
+ 'parent-width'?: number;
18
+ 'parent-height'?: number;
19
+ bindstickontopchange?: (e: NativeSyntheticEvent<unknown>) => void;
20
+ }
21
+
22
+ const _StickyHeader = forwardRef<HandlerRef<View, StickyHeaderProps>, StickyHeaderProps>((stickyHeaderProps: StickyHeaderProps = {}, ref): JSX.Element => {
23
+ const { textProps, innerProps: props = {} } = splitProps(stickyHeaderProps)
24
+ const {
25
+ style,
26
+ bindstickontopchange,
27
+ padding = [0, 0, 0, 0],
28
+ 'offset-top': offsetTop = 0,
29
+ 'enable-var': enableVar,
30
+ 'external-var-context': externalVarContext,
31
+ 'parent-font-size': parentFontSize,
32
+ 'parent-width': parentWidth,
33
+ 'parent-height': parentHeight
34
+ } = props
35
+
36
+ const scrollViewContext = useContext(ScrollViewContext)
37
+ const stickyContext = useContext(StickyContext)
38
+ const { scrollOffset } = scrollViewContext
39
+ const { registerStickyHeader, unregisterStickyHeader } = stickyContext
40
+ const headerRef = useRef<View>(null)
41
+ const isStickOnTopRef = useRef(false)
42
+ const id = useId()
43
+
44
+ const {
45
+ normalStyle,
46
+ hasVarDec,
47
+ varContextRef,
48
+ hasSelfPercent,
49
+ setWidth,
50
+ setHeight
51
+ } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
52
+
53
+ const { layoutRef, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: headerRef, onLayout })
54
+
55
+ const { textStyle, innerStyle = {} } = splitStyle(normalStyle)
56
+
57
+ const headerTopAnimated = useAnimatedValue(0)
58
+ // harmony animatedValue 不支持通过 _value 访问
59
+ const headerTopRef = useRef(0)
60
+
61
+ useEffect(() => {
62
+ registerStickyHeader({ key: id, updatePosition })
63
+ return () => {
64
+ unregisterStickyHeader(id)
65
+ }
66
+ }, [])
67
+
68
+ function updatePosition () {
69
+ if (headerRef.current) {
70
+ const scrollViewRef = scrollViewContext.gestureRef
71
+ if (scrollViewRef && scrollViewRef.current) {
72
+ headerRef.current.measureLayout(
73
+ scrollViewRef.current,
74
+ (left: number, top: number) => {
75
+ Animated.timing(headerTopAnimated, {
76
+ toValue: top,
77
+ duration: 0,
78
+ useNativeDriver: true
79
+ }).start()
80
+ headerTopRef.current = top
81
+ }
82
+ )
83
+ } else {
84
+ error('StickyHeader measureLayout error: scrollViewRef is not a valid native component reference')
85
+ }
86
+ }
87
+ }
88
+
89
+ function onLayout (e: LayoutChangeEvent) {
90
+ updatePosition()
91
+ }
92
+
93
+ useNodesRef(props, ref, headerRef, {
94
+ style: normalStyle
95
+ })
96
+
97
+ useEffect(() => {
98
+ if (!bindstickontopchange) return
99
+
100
+ const listener = scrollOffset.addListener((state: { value: number }) => {
101
+ const currentScrollValue = state.value
102
+ const newIsStickOnTop = currentScrollValue > headerTopRef.current
103
+ if (newIsStickOnTop !== isStickOnTopRef.current) {
104
+ isStickOnTopRef.current = newIsStickOnTop
105
+ bindstickontopchange(
106
+ getCustomEvent('stickontopchange', {}, {
107
+ detail: {
108
+ isStickOnTop: newIsStickOnTop
109
+ },
110
+ layoutRef
111
+ }, props))
112
+ }
113
+ })
114
+
115
+ return () => {
116
+ scrollOffset.removeListener(listener)
117
+ }
118
+ }, [])
119
+
120
+ const animatedStyle = useMemo(() => {
121
+ const translateY = Animated.subtract(scrollOffset, headerTopAnimated).interpolate({
122
+ inputRange: [0, 1],
123
+ outputRange: [0, 1],
124
+ extrapolateLeft: 'clamp',
125
+ extrapolateRight: 'extend'
126
+ })
127
+
128
+ const finalTranslateY = offsetTop === 0
129
+ ? translateY
130
+ : Animated.add(
131
+ translateY,
132
+ Animated.subtract(scrollOffset, headerTopAnimated).interpolate({
133
+ inputRange: [0, 1],
134
+ outputRange: [0, offsetTop],
135
+ extrapolate: 'clamp'
136
+ })
137
+ )
138
+
139
+ return {
140
+ transform: [{ translateY: finalTranslateY }]
141
+ }
142
+ }, [scrollOffset, headerTopAnimated, offsetTop])
143
+
144
+ const innerProps = useInnerProps(extendObject({}, props, {
145
+ ref: headerRef,
146
+ style: extendObject({}, styles.content, innerStyle, animatedStyle, {
147
+ paddingTop: padding[0] || 0,
148
+ paddingRight: padding[1] || 0,
149
+ paddingBottom: padding[2] || 0,
150
+ paddingLeft: padding[3] || 0
151
+ })
152
+ }, layoutProps), [], { layoutRef })
153
+
154
+ return (
155
+ createElement(
156
+ Animated.View,
157
+ innerProps,
158
+ wrapChildren(
159
+ props,
160
+ {
161
+ hasVarDec,
162
+ varContext: varContextRef.current,
163
+ textStyle,
164
+ textProps
165
+ }
166
+ )
167
+ )
168
+ )
169
+ })
170
+
171
+ const styles = StyleSheet.create({
172
+ content: {
173
+ width: '100%',
174
+ zIndex: 10
175
+ }
176
+ })
177
+
178
+ _StickyHeader.displayName = 'MpxStickyHeader'
179
+ export default _StickyHeader