@mpxjs/webpack-plugin 2.10.7 → 2.10.8-beta.3

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 (138) hide show
  1. package/lib/dependencies/RecordPageConfigsMapDependency.js +1 -1
  2. package/lib/dependencies/RequireExternalDependency.js +61 -0
  3. package/lib/file-loader.js +3 -2
  4. package/lib/index.js +55 -9
  5. package/lib/json-compiler/index.js +1 -0
  6. package/lib/parser.js +1 -1
  7. package/lib/platform/json/wx/index.js +43 -25
  8. package/lib/platform/style/wx/index.js +7 -0
  9. package/lib/platform/template/wx/component-config/fix-component-name.js +2 -2
  10. package/lib/platform/template/wx/component-config/index.js +9 -1
  11. package/lib/platform/template/wx/component-config/nav-container.js +27 -0
  12. package/lib/platform/template/wx/component-config/page-container.js +19 -0
  13. package/lib/platform/template/wx/component-config/sticky-header.js +23 -0
  14. package/lib/platform/template/wx/component-config/sticky-section.js +23 -0
  15. package/lib/platform/template/wx/component-config/unsupported.js +1 -1
  16. package/lib/react/LoadAsyncChunkModule.js +74 -0
  17. package/lib/react/index.js +3 -1
  18. package/lib/react/processJSON.js +74 -13
  19. package/lib/react/processScript.js +6 -6
  20. package/lib/react/script-helper.js +100 -41
  21. package/lib/runtime/components/ali/mpx-nav-container.mpx +3 -0
  22. package/lib/runtime/components/react/context.ts +27 -7
  23. package/lib/runtime/components/react/dist/context.d.ts +78 -0
  24. package/lib/runtime/components/react/dist/context.js +5 -1
  25. package/lib/runtime/components/react/dist/event.config.d.ts +7 -0
  26. package/lib/runtime/components/react/dist/getInnerListeners.d.ts +7 -0
  27. package/lib/runtime/components/react/dist/mpx-async-suspense.d.ts +12 -0
  28. package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +135 -0
  29. package/lib/runtime/components/react/dist/mpx-button.d.ts +68 -0
  30. package/lib/runtime/components/react/dist/mpx-button.jsx +2 -2
  31. package/lib/runtime/components/react/dist/mpx-canvas/Bus.d.ts +23 -0
  32. package/lib/runtime/components/react/dist/mpx-canvas/CanvasGradient.d.ts +7 -0
  33. package/lib/runtime/components/react/dist/mpx-canvas/CanvasRenderingContext2D.d.ts +6 -0
  34. package/lib/runtime/components/react/dist/mpx-canvas/Image.d.ts +20 -0
  35. package/lib/runtime/components/react/dist/mpx-canvas/ImageData.d.ts +8 -0
  36. package/lib/runtime/components/react/dist/mpx-canvas/constructorsRegistry.d.ts +10 -0
  37. package/lib/runtime/components/react/dist/mpx-canvas/html.d.ts +2 -0
  38. package/lib/runtime/components/react/dist/mpx-canvas/index.d.ts +32 -0
  39. package/lib/runtime/components/react/dist/mpx-canvas/utils.d.ts +52 -0
  40. package/lib/runtime/components/react/dist/mpx-checkbox-group.d.ts +20 -0
  41. package/lib/runtime/components/react/dist/mpx-checkbox.d.ts +32 -0
  42. package/lib/runtime/components/react/dist/mpx-form.d.ts +27 -0
  43. package/lib/runtime/components/react/dist/mpx-icon/index.d.ts +18 -0
  44. package/lib/runtime/components/react/dist/mpx-image.d.ts +21 -0
  45. package/lib/runtime/components/react/dist/mpx-inline-text.d.ts +7 -0
  46. package/lib/runtime/components/react/dist/mpx-input.d.ts +49 -0
  47. package/lib/runtime/components/react/dist/mpx-input.jsx +28 -9
  48. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.d.ts +12 -0
  49. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +42 -46
  50. package/lib/runtime/components/react/dist/mpx-label.d.ts +20 -0
  51. package/lib/runtime/components/react/dist/mpx-movable-area.d.ts +20 -0
  52. package/lib/runtime/components/react/dist/mpx-movable-view.d.ts +63 -0
  53. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +8 -6
  54. package/lib/runtime/components/react/dist/mpx-nav-container.d.ts +9 -0
  55. package/lib/runtime/components/react/dist/mpx-nav-container.jsx +23 -0
  56. package/lib/runtime/components/react/dist/mpx-navigator.d.ts +9 -0
  57. package/lib/runtime/components/react/dist/mpx-page-container.d.ts +27 -0
  58. package/lib/runtime/components/react/dist/mpx-page-container.jsx +255 -0
  59. package/lib/runtime/components/react/dist/mpx-picker/date.d.ts +6 -0
  60. package/lib/runtime/components/react/dist/mpx-picker/dateData.d.ts +7 -0
  61. package/lib/runtime/components/react/dist/mpx-picker/index.d.ts +6 -0
  62. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.d.ts +6 -0
  63. package/lib/runtime/components/react/dist/mpx-picker/region.d.ts +6 -0
  64. package/lib/runtime/components/react/dist/mpx-picker/regionData.d.ts +2 -0
  65. package/lib/runtime/components/react/dist/mpx-picker/selector.d.ts +6 -0
  66. package/lib/runtime/components/react/dist/mpx-picker/time.d.ts +6 -0
  67. package/lib/runtime/components/react/dist/mpx-picker/type.d.ts +106 -0
  68. package/lib/runtime/components/react/dist/mpx-picker-view/index.d.ts +31 -0
  69. package/lib/runtime/components/react/dist/mpx-picker-view/pickerVIewContext.d.ts +8 -0
  70. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.d.ts +22 -0
  71. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItem.d.ts +14 -0
  72. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewFaces.d.ts +16 -0
  73. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewIndicator.d.ts +12 -0
  74. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewMask.d.ts +11 -0
  75. package/lib/runtime/components/react/dist/mpx-popup/index.d.ts +22 -0
  76. package/lib/runtime/components/react/dist/mpx-popup/popupBase.d.ts +16 -0
  77. package/lib/runtime/components/react/dist/mpx-portal/index.d.ts +15 -0
  78. package/lib/runtime/components/react/dist/mpx-portal/portal-host.d.ts +29 -0
  79. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.d.ts +9 -0
  80. package/lib/runtime/components/react/dist/mpx-radio-group.d.ts +20 -0
  81. package/lib/runtime/components/react/dist/mpx-radio.d.ts +26 -0
  82. package/lib/runtime/components/react/dist/mpx-rich-text/html.d.ts +1 -0
  83. package/lib/runtime/components/react/dist/mpx-rich-text/index.d.ts +24 -0
  84. package/lib/runtime/components/react/dist/mpx-root-portal.d.ts +14 -0
  85. package/lib/runtime/components/react/dist/mpx-scroll-view.d.ts +54 -0
  86. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +31 -15
  87. package/lib/runtime/components/react/dist/mpx-simple-text.d.ts +7 -0
  88. package/lib/runtime/components/react/dist/mpx-simple-view.d.ts +7 -0
  89. package/lib/runtime/components/react/dist/mpx-sticky-header.d.ts +17 -0
  90. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +117 -0
  91. package/lib/runtime/components/react/dist/mpx-sticky-section.d.ts +15 -0
  92. package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
  93. package/lib/runtime/components/react/dist/mpx-swiper-item.d.ts +18 -0
  94. package/lib/runtime/components/react/dist/mpx-swiper.d.ts +52 -0
  95. package/lib/runtime/components/react/dist/mpx-swiper.jsx +3 -2
  96. package/lib/runtime/components/react/dist/mpx-switch.d.ts +26 -0
  97. package/lib/runtime/components/react/dist/mpx-text.d.ts +21 -0
  98. package/lib/runtime/components/react/dist/mpx-textarea.d.ts +7 -0
  99. package/lib/runtime/components/react/dist/mpx-video.d.ts +101 -0
  100. package/lib/runtime/components/react/dist/mpx-view.d.ts +34 -0
  101. package/lib/runtime/components/react/dist/mpx-web-view.d.ts +22 -0
  102. package/lib/runtime/components/react/dist/nav.d.ts +11 -0
  103. package/lib/runtime/components/react/dist/nav.jsx +141 -0
  104. package/lib/runtime/components/react/dist/parser.d.ts +39 -0
  105. package/lib/runtime/components/react/dist/useAnimationHooks.d.ts +32 -0
  106. package/lib/runtime/components/react/dist/useNavShared.d.ts +2 -0
  107. package/lib/runtime/components/react/dist/useNavShared.js +6 -0
  108. package/lib/runtime/components/react/dist/useNodesRef.d.ts +11 -0
  109. package/lib/runtime/components/react/dist/utils.d.ts +121 -0
  110. package/lib/runtime/components/react/mpx-async-suspense.tsx +180 -0
  111. package/lib/runtime/components/react/mpx-button.tsx +3 -2
  112. package/lib/runtime/components/react/mpx-input.tsx +35 -16
  113. package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +46 -45
  114. package/lib/runtime/components/react/mpx-movable-view.tsx +8 -4
  115. package/lib/runtime/components/react/mpx-nav-container.tsx +33 -0
  116. package/lib/runtime/components/react/mpx-page-container.tsx +394 -0
  117. package/lib/runtime/components/react/mpx-scroll-view.tsx +84 -59
  118. package/lib/runtime/components/react/mpx-sticky-header.tsx +181 -0
  119. package/lib/runtime/components/react/mpx-sticky-section.tsx +96 -0
  120. package/lib/runtime/components/react/mpx-swiper.tsx +4 -2
  121. package/lib/runtime/components/react/nav.tsx +172 -0
  122. package/lib/runtime/components/react/types/common.d.ts +19 -0
  123. package/lib/runtime/components/react/useNavShared.ts +8 -0
  124. package/lib/runtime/components/web/mpx-nav-container.vue +13 -0
  125. package/lib/runtime/components/web/mpx-scroll-view.vue +18 -4
  126. package/lib/runtime/components/web/mpx-sticky-header.vue +99 -0
  127. package/lib/runtime/components/web/mpx-sticky-section.vue +15 -0
  128. package/lib/runtime/components/wx/mpx-nav-container.mpx +9 -0
  129. package/lib/runtime/optionProcessorReact.d.ts +18 -0
  130. package/lib/runtime/optionProcessorReact.js +30 -0
  131. package/lib/script-setup-compiler/index.js +27 -5
  132. package/lib/template-compiler/bind-this.js +2 -1
  133. package/lib/template-compiler/compiler.js +27 -6
  134. package/lib/utils/dom-tag-config.js +18 -4
  135. package/lib/utils/trans-async-sub-rules.js +19 -0
  136. package/lib/web/script-helper.js +1 -1
  137. package/package.json +4 -4
  138. package/LICENSE +0 -433
@@ -0,0 +1,33 @@
1
+ import { AnimatedStyle } from 'react-native-reanimated'
2
+ import { useNavShared } from './useNavShared'
3
+ import { NavSharedContext, NavSharedValue } from './context'
4
+ import { useLayoutEffect, useMemo, useState } from 'react'
5
+ import { StyleProp } from 'react-native'
6
+ import { isAndroid } from './utils'
7
+
8
+ interface MpxNavContainerProps {
9
+ children?: React.ReactNode
10
+ }
11
+
12
+ export default function MpxNavContainer(props: MpxNavContainerProps) {
13
+ const [, setCustomNav] = useNavShared()
14
+
15
+ useLayoutEffect(() => {
16
+ if (!isAndroid) return
17
+ if (props.children) {
18
+ setCustomNav(props.children)
19
+ }
20
+
21
+ return () => {
22
+ setCustomNav(undefined)
23
+ }
24
+ }, [props.children])
25
+
26
+ return isAndroid ? null : props.children
27
+ }
28
+
29
+ export function NavSharedProvider({ children }: { children?: React.ReactNode }) {
30
+ const [customNav, setCustomNav] = useState()
31
+ const value = useMemo(() => ({ customNav, setCustomNav } as NavSharedValue), [customNav])
32
+ return <NavSharedContext.Provider value={value}>{children}</NavSharedContext.Provider>
33
+ }
@@ -0,0 +1,394 @@
1
+ import React, { createElement, forwardRef, useEffect, useRef, useState } from 'react'
2
+ import {
3
+ StyleSheet,
4
+ Animated,
5
+ Dimensions,
6
+ TouchableWithoutFeedback,
7
+ PanResponder,
8
+ StyleProp,
9
+ ViewStyle
10
+ } from 'react-native'
11
+ import Portal from './mpx-portal/index'
12
+ import { PreventRemoveEvent, usePreventRemove } from '@react-navigation/native'
13
+ import { extendObject, useLayout, useNavigation } from './utils'
14
+ import useInnerProps, { getCustomEvent } from './getInnerListeners'
15
+ import useNodesRef from './useNodesRef'
16
+
17
+ type Position = 'top' | 'bottom' | 'right' | 'center';
18
+
19
+ interface PageContainerProps {
20
+ show: boolean;
21
+ duration?: number;
22
+ 'z-index'?: number;
23
+ overlay?: boolean;
24
+ position?: Position;
25
+ round?: boolean;
26
+ 'close-on-slide-down'?: boolean;
27
+ 'overlay-style'?: StyleProp<ViewStyle>;
28
+ 'custom-style'?: StyleProp<ViewStyle>;
29
+
30
+ bindclose: (event: CustomEvent<{ value: boolean}>) => void;
31
+
32
+ bindbeforeenter?: (event: CustomEvent) => void;
33
+ bindenter?: (event: CustomEvent) => void;
34
+ bindafterenter?: (event: CustomEvent) => void;
35
+ bindbeforeleave?: (event: CustomEvent) => void;
36
+ bindleave?: (event: CustomEvent) => void;
37
+ bindafterleave?: (event: CustomEvent) => void;
38
+ bindclickoverlay?: (event: CustomEvent) => void;
39
+ children: React.ReactNode;
40
+ }
41
+
42
+ const screenHeight = Dimensions.get('window').height
43
+ const screenWidth = Dimensions.get('window').width
44
+
45
+ function nextTick (cb: () => void) {
46
+ setTimeout(cb, 0)
47
+ }
48
+
49
+ export default forwardRef<any, PageContainerProps>((props, ref) => {
50
+ const {
51
+ show,
52
+ duration = 300,
53
+ 'z-index': zIndex = 100,
54
+ overlay = true,
55
+ position = 'bottom',
56
+ round = false,
57
+ 'close-on-slide-down': closeOnSlideDown = false,
58
+ 'overlay-style': overlayStyle,
59
+ 'custom-style': customStyle,
60
+ bindclose, // RN下特有属性,用于同步show状态到父组件
61
+ bindbeforeenter,
62
+ bindenter,
63
+ bindafterenter,
64
+ bindbeforeleave,
65
+ bindleave,
66
+ bindafterleave,
67
+ bindclickoverlay,
68
+ children
69
+ } = props
70
+
71
+ const isFirstRenderFlag = useRef(true)
72
+ const isFirstRender = isFirstRenderFlag.current
73
+ if (isFirstRenderFlag.current) {
74
+ isFirstRenderFlag.current = false
75
+ }
76
+
77
+ const close = () => {
78
+ bindclose(getCustomEvent(
79
+ 'close',
80
+ {},
81
+ { detail: { value: false, source: 'close' } },
82
+ props
83
+ ))
84
+ }
85
+
86
+ const [internalVisible, setInternalVisible] = useState(show) // 控制组件是否挂载
87
+
88
+ const overlayOpacity = useRef(new Animated.Value(0)).current
89
+ const contentOpacity = useRef(new Animated.Value(position === 'center' ? 0 : 1)).current
90
+ const contentTranslate = useRef(new Animated.Value(getInitialPosition())).current
91
+
92
+ const currentAnimation = useRef<Array<Animated.CompositeAnimation> | null>(null)
93
+
94
+ function getInitialPosition () {
95
+ switch (position) {
96
+ case 'top':
97
+ return -screenHeight
98
+ case 'bottom':
99
+ return screenHeight
100
+ case 'right':
101
+ return screenWidth
102
+ case 'center':
103
+ return 0
104
+ default:
105
+ return screenHeight
106
+ }
107
+ }
108
+
109
+ const currentTick = useRef(0)
110
+ function createTick () {
111
+ currentTick.current++
112
+ console.log('currentTick.current++', currentTick.current)
113
+ const current = currentTick.current
114
+ return () => {
115
+ console.log('currentTick.current', currentTick.current, 'current', current)
116
+ return currentTick.current === current
117
+ }
118
+ }
119
+ // 播放入场动画
120
+ const animateIn = () => {
121
+ const isCurrentTick = createTick()
122
+ const animateOutFinish = currentAnimation.current === null
123
+ if (!animateOutFinish) {
124
+ currentAnimation.current!.forEach((animation) => animation.stop())
125
+ }
126
+ const animations: Animated.CompositeAnimation[] = [
127
+ Animated.timing(contentTranslate, {
128
+ toValue: 0,
129
+ duration,
130
+ useNativeDriver: true
131
+ }),
132
+ Animated.timing(contentOpacity, {
133
+ toValue: 1,
134
+ duration,
135
+ useNativeDriver: true
136
+ }),
137
+ Animated.timing(overlayOpacity, {
138
+ toValue: 1,
139
+ duration,
140
+ useNativeDriver: true
141
+ })
142
+ ]
143
+ currentAnimation.current = animations
144
+ // 所有生命周期需相隔一个nextTick以保证在生命周期中修改show可在组件内部监听到
145
+ bindbeforeenter && bindbeforeenter(getCustomEvent(
146
+ 'beforeenter',
147
+ {},
148
+ { detail: { value: false, source: 'beforeenter' } },
149
+ props
150
+ ))
151
+ nextTick(() => {
152
+ bindenter && bindenter(getCustomEvent(
153
+ 'enter',
154
+ {},
155
+ { detail: { value: false, source: 'enter' } },
156
+ props
157
+ ))
158
+ // 与微信对其, bindenter 需要执行,所以 isCurrentTick 放在后面
159
+ if (!isCurrentTick()) return
160
+
161
+ console.log('animateIn start')
162
+ // 设置为动画初始状态(特殊情况, 如果退场动画没有结束 或者 退场动画还未开始,则无需初始化,而是从当前位置完成动画)
163
+ if (animateOutFinish) {
164
+ contentTranslate.setValue(getInitialPosition())
165
+ contentOpacity.setValue(position === 'center' ? 0 : 1)
166
+ }
167
+ Animated.parallel(animations).start(() => {
168
+ bindafterenter && bindafterenter(getCustomEvent(
169
+ 'afterenter',
170
+ {},
171
+ { detail: { value: false, source: 'afterenter' } },
172
+ props
173
+ ))
174
+ })
175
+ })
176
+ }
177
+
178
+ // 播放离场动画
179
+ const animateOut = () => {
180
+ const isCurrentTick = createTick()
181
+ // 停止入场动画
182
+ currentAnimation.current?.forEach((animation) => animation.stop())
183
+ const animations: Animated.CompositeAnimation[] = [Animated.timing(overlayOpacity, {
184
+ toValue: 0,
185
+ duration,
186
+ useNativeDriver: true
187
+ })
188
+ ]
189
+ if (position === 'center') {
190
+ animations.push(Animated.timing(contentOpacity, {
191
+ toValue: 0,
192
+ duration,
193
+ useNativeDriver: true
194
+ }))
195
+ } else {
196
+ animations.push(Animated.timing(contentTranslate, {
197
+ toValue: getInitialPosition(),
198
+ duration,
199
+ useNativeDriver: true
200
+ }))
201
+ }
202
+ currentAnimation.current = animations
203
+ bindbeforeleave && bindbeforeleave(getCustomEvent(
204
+ 'beforeleave',
205
+ {},
206
+ { detail: { value: false, source: 'beforeleave' } },
207
+ props
208
+ ))
209
+ nextTick(() => {
210
+ bindleave && bindleave(getCustomEvent(
211
+ 'leave',
212
+ {},
213
+ { detail: { value: false, source: 'leave' } },
214
+ props
215
+ ))
216
+ if (!isCurrentTick()) return
217
+ console.log('animateOut start')
218
+ Animated.parallel(animations).start(() => {
219
+ currentAnimation.current = null
220
+ bindafterleave && bindafterleave(getCustomEvent(
221
+ 'afterleave',
222
+ {},
223
+ { detail: { value: false, source: 'afterleave' } },
224
+ props
225
+ ))
226
+ setInternalVisible(false) // 动画播放完后,才卸载
227
+ })
228
+ })
229
+ }
230
+
231
+ useEffect(() => {
232
+ console.log('====comp show', show, 'internalVisible', internalVisible)
233
+ // 如果展示状态和挂载状态一致,则不需要做任何操作
234
+ if (show) {
235
+ setInternalVisible(true) // 确保挂载
236
+ animateIn()
237
+ } else {
238
+ if (!isFirstRender) animateOut()
239
+ }
240
+ }, [show])
241
+
242
+ const navigation = useNavigation()
243
+ usePreventRemove(show, (event: PreventRemoveEvent) => {
244
+ const { data } = event
245
+ if (show) {
246
+ close()
247
+ } else {
248
+ navigation?.dispatch(data.action)
249
+ }
250
+ })
251
+
252
+ // IOS 下需要关闭手势返回(原因: IOS手势返回时页面会跟随手指滑动,但是实际返回动作是在松手时触发,需禁掉页面跟随手指滑动的效果)
253
+ useEffect(() => {
254
+ navigation?.setOptions({
255
+ gestureEnabled: !show
256
+ })
257
+ }, [show])
258
+
259
+ const SCREEN_EDGE_THRESHOLD = 60 // 从屏幕左侧 30px 内触发
260
+
261
+ // 内容区 手势下滑关闭
262
+ const contentPanResponder = PanResponder.create({
263
+ onMoveShouldSetPanResponder: (_, gestureState) => {
264
+ const { dx, dy } = gestureState
265
+ return dy > 200 && Math.abs(dx) < 60
266
+ },
267
+ onPanResponderRelease: () => {
268
+ close()
269
+ }
270
+ })
271
+
272
+ // 全屏幕 IOS 右滑手势返回
273
+ const screenPanResponder = PanResponder.create({
274
+ onMoveShouldSetPanResponder: (_, gestureState) => {
275
+ const { moveX, dx, dy } = gestureState
276
+
277
+ const isFromEdge = moveX < SCREEN_EDGE_THRESHOLD
278
+ const isHorizontalSwipe = dx > 10 && Math.abs(dy) < 20
279
+ return isFromEdge && isHorizontalSwipe
280
+ },
281
+ onPanResponderRelease: (_, gestureState) => {
282
+ if (gestureState.dx > 100) {
283
+ close()
284
+ }
285
+ }
286
+ })
287
+
288
+ const getTransformStyle: () => ViewStyle = () => {
289
+ switch (position) {
290
+ case 'top':
291
+ case 'bottom':
292
+ return { transform: [{ translateY: contentTranslate }] }
293
+ case 'right':
294
+ return { transform: [{ translateX: contentTranslate }] }
295
+ case 'center':
296
+ return {}
297
+ }
298
+ }
299
+
300
+ const renderMask = () => {
301
+ const onPress = () => {
302
+ close()
303
+ bindclickoverlay && bindclickoverlay(getCustomEvent(
304
+ 'clickoverlay',
305
+ {},
306
+ { detail: { value: false, source: 'clickoverlay' } },
307
+ props
308
+ ))
309
+ }
310
+ return createElement(TouchableWithoutFeedback, { onPress },
311
+ createElement(Animated.View, { style: [styles.overlay, overlayStyle, { opacity: overlayOpacity }] }))
312
+ }
313
+
314
+ const renderContent = (children: React.ReactNode) => {
315
+ const contentProps = extendObject(
316
+ {
317
+ style: [
318
+ styles.container,
319
+ round ? styles.rounded : null,
320
+ positionStyle[position],
321
+ customStyle,
322
+ getTransformStyle(),
323
+ { opacity: contentOpacity }
324
+ ]
325
+ },
326
+ closeOnSlideDown ? contentPanResponder.panHandlers : null
327
+ )
328
+ return createElement(Animated.View, contentProps, children)
329
+ }
330
+
331
+ const nodeRef = useRef(null)
332
+ useNodesRef(props, ref, nodeRef, {})
333
+ const { layoutRef, layoutProps } = useLayout({ props, hasSelfPercent: false, nodeRef })
334
+ const innerProps = useInnerProps(
335
+ extendObject(
336
+ {},
337
+ props,
338
+ {
339
+ ref: nodeRef
340
+ },
341
+ layoutProps
342
+ ),
343
+ [],
344
+ { layoutRef }
345
+ )
346
+ const wrapperProps = extendObject(
347
+ innerProps,
348
+ {
349
+ style: [styles.wrapper, { zIndex }]
350
+ },
351
+ __mpx_mode__ === 'ios' ? screenPanResponder.panHandlers : {}
352
+ )
353
+
354
+ // TODO 是否有必要支持refs? dataset?
355
+ return createElement(Portal, null,
356
+ internalVisible
357
+ ? createElement(Animated.View, wrapperProps,
358
+ overlay ? renderMask() : null,
359
+ renderContent(children)
360
+ )
361
+ : null
362
+ )
363
+ })
364
+
365
+ const styles = StyleSheet.create({
366
+ wrapper: extendObject(
367
+ {
368
+ justifyContent: 'flex-end',
369
+ alignItems: 'center'
370
+ } as const,
371
+ StyleSheet.absoluteFillObject
372
+ ),
373
+ overlay: extendObject(
374
+ {
375
+ backgroundColor: 'rgba(0,0,0,0.5)'
376
+ },
377
+ StyleSheet.absoluteFillObject
378
+ ),
379
+ container: {
380
+ position: 'absolute',
381
+ backgroundColor: 'white'
382
+ },
383
+ rounded: {
384
+ borderTopLeftRadius: 20,
385
+ borderTopRightRadius: 20
386
+ }
387
+ })
388
+
389
+ const positionStyle: Record<Position, ViewStyle> = {
390
+ bottom: { bottom: 0, width: '100%', height: 'auto' },
391
+ top: { top: 0, width: '100%', height: 'auto' },
392
+ right: extendObject({}, StyleSheet.absoluteFillObject, { right: 0 }),
393
+ center: extendObject({}, StyleSheet.absoluteFillObject)
394
+ }
@@ -32,7 +32,7 @@
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
38
  import { warn, hasOwn } from '@mpxjs/utils'
@@ -43,48 +43,49 @@ import { IntersectionObserverContext, ScrollViewContext } from './context'
43
43
  import Portal from './mpx-portal'
44
44
 
45
45
  interface ScrollViewProps {
46
- children?: ReactNode
47
- enhanced?: boolean
48
- bounces?: boolean
49
- style?: ViewStyle
50
- scrollEventThrottle?: number
51
- 'scroll-x'?: boolean
52
- 'scroll-y'?: boolean
53
- 'enable-back-to-top'?: boolean
54
- 'show-scrollbar'?: boolean
55
- 'paging-enabled'?: boolean
56
- 'upper-threshold'?: number
57
- 'lower-threshold'?: number
58
- 'scroll-with-animation'?: boolean
59
- 'refresher-triggered'?: boolean
60
- 'refresher-enabled'?: boolean
61
- 'refresher-default-style'?: 'black' | 'white' | 'none'
62
- 'refresher-background'?: string
63
- 'refresher-threshold'?: number
64
- 'scroll-top'?: number
65
- 'scroll-left'?: number
66
- 'enable-offset'?: boolean
67
- 'scroll-into-view'?: string
68
- 'enable-trigger-intersection-observer'?: boolean
69
- 'enable-var'?: boolean
70
- 'external-var-context'?: Record<string, any>
71
- 'parent-font-size'?: number
72
- 'parent-width'?: number
73
- 'parent-height'?: number
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
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
+ 'scroll-into-view-offset'?: number;
78
+ bindscrolltoupper?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
79
+ bindscrolltolower?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
80
+ bindscroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
81
+ bindrefresherrefresh?: (event: NativeSyntheticEvent<unknown>) => void;
82
+ binddragstart?: (event: NativeSyntheticEvent<DragEvent>) => void;
83
+ binddragging?: (event: NativeSyntheticEvent<DragEvent>) => void;
84
+ binddragend?: (event: NativeSyntheticEvent<DragEvent>) => void;
85
+ bindtouchstart?: (event: NativeSyntheticEvent<TouchEvent>) => void;
86
+ bindtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
87
+ bindtouchend?: (event: NativeSyntheticEvent<TouchEvent>) => void;
88
+ bindscrollend?: (event: NativeSyntheticEvent<TouchEvent>) => void;
88
89
  __selectRef?: (selector: string, nodeType: 'node' | 'component', all?: boolean) => HandlerRef<any, any>
89
90
  }
90
91
  type ScrollAdditionalProps = {
@@ -109,6 +110,8 @@ type ScrollAdditionalProps = {
109
110
  onMomentumScrollEnd?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void
110
111
  }
111
112
 
113
+ const AnimatedScrollView = RNAnimated.createAnimatedComponent(ScrollView) as React.ComponentType<any>
114
+
112
115
  const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, ScrollViewProps>((scrollViewProps: ScrollViewProps = {}, ref): JSX.Element => {
113
116
  const { textProps, innerProps: props = {} } = splitProps(scrollViewProps)
114
117
  const {
@@ -145,10 +148,14 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
145
148
  'parent-height': parentHeight,
146
149
  'simultaneous-handlers': originSimultaneousHandlers,
147
150
  'wait-for': waitFor,
151
+ 'enable-sticky': enableSticky,
148
152
  'scroll-event-throttle': scrollEventThrottle = 0,
153
+ 'scroll-into-view-offset': scrollIntoViewOffset = 0,
149
154
  __selectRef
150
155
  } = props
151
156
 
157
+ const scrollOffset = useRef(new RNAnimated.Value(0)).current
158
+
152
159
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers)
153
160
  const waitForHandlers = flatGesture(waitFor)
154
161
 
@@ -180,7 +187,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
180
187
  const initialTimeout = useRef<ReturnType<typeof setTimeout> | null>(null)
181
188
  const intersectionObservers = useContext(IntersectionObserverContext)
182
189
 
183
- const firstScrollIntoViewChange = useRef<boolean>(false)
190
+ const firstScrollIntoViewChange = useRef<boolean>(true)
184
191
 
185
192
  const refreshColor = {
186
193
  black: ['#000'],
@@ -213,19 +220,21 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
213
220
  pagingEnabled,
214
221
  fastDeceleration: false,
215
222
  decelerationDisabled: false,
216
- scrollTo
223
+ scrollTo,
224
+ scrollIntoView: handleScrollIntoView
217
225
  },
218
226
  gestureRef: scrollViewRef
219
227
  })
220
228
 
229
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout })
230
+
221
231
  const contextValue = useMemo(() => {
222
232
  return {
223
- gestureRef: scrollViewRef
233
+ gestureRef: scrollViewRef,
234
+ scrollOffset
224
235
  }
225
236
  }, [])
226
237
 
227
- const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout })
228
-
229
238
  const hasRefresherLayoutRef = useRef(false)
230
239
 
231
240
  // layout 完成前先隐藏,避免安卓闪烁问题
@@ -251,13 +260,15 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
251
260
 
252
261
  useEffect(() => {
253
262
  if (scrollIntoView && __selectRef) {
254
- if (!firstScrollIntoViewChange.current) {
255
- setTimeout(handleScrollIntoView)
263
+ if (firstScrollIntoViewChange.current) {
264
+ setTimeout(() => {
265
+ handleScrollIntoView(scrollIntoView, { offset: scrollIntoViewOffset, animated: scrollWithAnimation })
266
+ })
256
267
  } else {
257
- handleScrollIntoView()
268
+ handleScrollIntoView(scrollIntoView, { offset: scrollIntoViewOffset, animated: scrollWithAnimation })
258
269
  }
259
270
  }
260
- firstScrollIntoViewChange.current = true
271
+ firstScrollIntoViewChange.current = false
261
272
  }, [scrollIntoView])
262
273
 
263
274
  useEffect(() => {
@@ -280,14 +291,16 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
280
291
  scrollToOffset(left, top, animated)
281
292
  }
282
293
 
283
- function handleScrollIntoView () {
284
- const refs = __selectRef!(`#${scrollIntoView}`, 'node')
294
+ function handleScrollIntoView (selector = '', { offset = 0, animated = true } = {}) {
295
+ const refs = __selectRef!(`#${selector}`, 'node')
285
296
  if (!refs) return
286
297
  const { nodeRef } = refs.getNodeInstance()
287
298
  nodeRef.current?.measureLayout(
288
299
  scrollViewRef.current,
289
300
  (left: number, top: number) => {
290
- scrollToOffset(left, top)
301
+ const adjustedLeft = scrollX ? left + offset : left
302
+ const adjustedTop = scrollY ? top + offset : top
303
+ scrollToOffset(adjustedLeft, adjustedTop, animated)
291
304
  }
292
305
  )
293
306
  }
@@ -487,6 +500,16 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
487
500
  updateIntersection()
488
501
  }
489
502
 
503
+ const scrollHandler = RNAnimated.event(
504
+ [{ nativeEvent: { contentOffset: { y: scrollOffset } } }],
505
+ {
506
+ useNativeDriver: true,
507
+ listener: (event: NativeSyntheticEvent<NativeScrollEvent>) => {
508
+ onScroll(event)
509
+ }
510
+ }
511
+ )
512
+
490
513
  function onScrollDragStart (e: NativeSyntheticEvent<NativeScrollEvent>) {
491
514
  hasCallScrollToLower.current = false
492
515
  hasCallScrollToUpper.current = false
@@ -661,7 +684,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
661
684
  scrollEnabled: !enableScroll ? false : !!(scrollX || scrollY),
662
685
  bounces: false,
663
686
  ref: scrollViewRef,
664
- onScroll: onScroll,
687
+ onScroll: enableSticky ? scrollHandler : onScroll,
665
688
  onContentSizeChange: onContentSizeChange,
666
689
  bindtouchstart: ((enhanced && binddragstart) || bindtouchstart) && onScrollTouchStart,
667
690
  bindtouchmove: ((enhanced && binddragging) || bindtouchmove) && onScrollTouchMove,
@@ -716,11 +739,13 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
716
739
  'bindrefresherrefresh'
717
740
  ], { layoutRef })
718
741
 
742
+ const ScrollViewComponent = enableSticky ? AnimatedScrollView : ScrollView
743
+
719
744
  const withRefresherScrollView = createElement(
720
745
  GestureDetector,
721
746
  { gesture: panGesture },
722
747
  createElement(
723
- ScrollView,
748
+ ScrollViewComponent,
724
749
  innerProps,
725
750
  createElement(
726
751
  Animated.View,
@@ -748,8 +773,8 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
748
773
  )
749
774
 
750
775
  const commonScrollView = createElement(
751
- ScrollView,
752
- extendObject(innerProps, {
776
+ ScrollViewComponent,
777
+ extendObject({}, innerProps, {
753
778
  refreshControl: refresherEnabled
754
779
  ? createElement(RefreshControl, extendObject({
755
780
  progressBackgroundColor: refresherBackground,