@mpxjs/webpack-plugin 2.10.6 → 2.10.7-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.
- package/lib/file-loader.js +1 -1
- package/lib/index.js +41 -13
- package/lib/platform/json/wx/index.js +43 -26
- package/lib/platform/template/wx/component-config/button.js +1 -1
- package/lib/platform/template/wx/component-config/fix-component-name.js +2 -2
- package/lib/platform/template/wx/component-config/index.js +5 -1
- package/lib/platform/template/wx/component-config/input.js +1 -1
- package/lib/platform/template/wx/component-config/sticky-header.js +23 -0
- package/lib/platform/template/wx/component-config/sticky-section.js +23 -0
- package/lib/platform/template/wx/index.js +2 -1
- package/lib/react/LoadAsyncChunkModule.js +68 -0
- package/lib/react/index.js +3 -1
- package/lib/react/processJSON.js +68 -12
- package/lib/react/processScript.js +4 -3
- package/lib/react/script-helper.js +92 -18
- package/lib/runtime/components/react/AsyncContainer.tsx +217 -0
- package/lib/runtime/components/react/AsyncSuspense.tsx +81 -0
- package/lib/runtime/components/react/context.ts +12 -3
- package/lib/runtime/components/react/dist/AsyncContainer.jsx +160 -0
- package/lib/runtime/components/react/dist/AsyncSuspense.jsx +68 -0
- package/lib/runtime/components/react/dist/context.js +4 -1
- package/lib/runtime/components/react/dist/getInnerListeners.js +1 -1
- package/lib/runtime/components/react/dist/mpx-button.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-input.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-movable-area.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +55 -40
- package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +3 -0
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +17 -6
- package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +115 -0
- package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
- package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +2 -2
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +53 -27
- package/lib/runtime/components/react/dist/mpx-view.jsx +21 -7
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +16 -30
- package/lib/runtime/components/react/dist/useAnimationHooks.js +2 -87
- package/lib/runtime/components/react/dist/utils.jsx +105 -1
- package/lib/runtime/components/react/getInnerListeners.ts +1 -1
- package/lib/runtime/components/react/mpx-button.tsx +3 -2
- package/lib/runtime/components/react/mpx-input.tsx +1 -1
- package/lib/runtime/components/react/mpx-movable-area.tsx +1 -1
- package/lib/runtime/components/react/mpx-movable-view.tsx +60 -41
- package/lib/runtime/components/react/mpx-rich-text/index.tsx +3 -0
- package/lib/runtime/components/react/mpx-scroll-view.tsx +68 -50
- package/lib/runtime/components/react/mpx-sticky-header.tsx +179 -0
- package/lib/runtime/components/react/mpx-sticky-section.tsx +96 -0
- package/lib/runtime/components/react/mpx-swiper-item.tsx +2 -2
- package/lib/runtime/components/react/mpx-swiper.tsx +53 -25
- package/lib/runtime/components/react/mpx-view.tsx +20 -7
- package/lib/runtime/components/react/mpx-web-view.tsx +14 -34
- package/lib/runtime/components/react/types/global.d.ts +15 -0
- package/lib/runtime/components/react/useAnimationHooks.ts +2 -85
- package/lib/runtime/components/react/utils.tsx +93 -1
- package/lib/runtime/components/web/mpx-scroll-view.vue +21 -4
- package/lib/runtime/components/web/mpx-sticky-header.vue +91 -0
- package/lib/runtime/components/web/mpx-sticky-section.vue +15 -0
- package/lib/runtime/optionProcessor.js +0 -2
- package/lib/template-compiler/compiler.js +2 -2
- package/lib/utils/dom-tag-config.js +17 -3
- package/lib/web/script-helper.js +1 -1
- package/package.json +4 -4
- package/LICENSE +0 -433
|
@@ -44,6 +44,7 @@ interface MovableViewProps {
|
|
|
44
44
|
disabled?: boolean
|
|
45
45
|
animation?: boolean
|
|
46
46
|
id?: string
|
|
47
|
+
changeThrottleTime?:number
|
|
47
48
|
bindchange?: (event: unknown) => void
|
|
48
49
|
bindtouchstart?: (event: GestureTouchEvent) => void
|
|
49
50
|
catchtouchstart?: (event: GestureTouchEvent) => void
|
|
@@ -105,6 +106,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
105
106
|
'simultaneous-handlers': originSimultaneousHandlers = [],
|
|
106
107
|
'wait-for': waitFor = [],
|
|
107
108
|
style = {},
|
|
109
|
+
changeThrottleTime = 60,
|
|
108
110
|
bindtouchstart,
|
|
109
111
|
catchtouchstart,
|
|
110
112
|
bindhtouchmove,
|
|
@@ -114,7 +116,8 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
114
116
|
catchvtouchmove,
|
|
115
117
|
catchtouchmove,
|
|
116
118
|
bindtouchend,
|
|
117
|
-
catchtouchend
|
|
119
|
+
catchtouchend,
|
|
120
|
+
bindchange
|
|
118
121
|
} = props
|
|
119
122
|
|
|
120
123
|
const {
|
|
@@ -140,6 +143,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
140
143
|
x: 0,
|
|
141
144
|
y: 0
|
|
142
145
|
})
|
|
146
|
+
|
|
143
147
|
const draggableXRange = useSharedValue<[min: number, max: number]>([0, 0])
|
|
144
148
|
const draggableYRange = useSharedValue<[min: number, max: number]>([0, 0])
|
|
145
149
|
const isMoving = useSharedValue(false)
|
|
@@ -148,6 +152,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
148
152
|
const isFirstTouch = useSharedValue(true)
|
|
149
153
|
const touchEvent = useSharedValue<string>('')
|
|
150
154
|
const initialViewPosition = useSharedValue({ x: x || 0, y: y || 0 })
|
|
155
|
+
const lastChangeTime = useSharedValue(0)
|
|
151
156
|
|
|
152
157
|
const MovableAreaLayout = useContext(MovableAreaContext)
|
|
153
158
|
|
|
@@ -195,6 +200,16 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
195
200
|
)
|
|
196
201
|
}, [])
|
|
197
202
|
|
|
203
|
+
// 节流版本的 change 事件触发
|
|
204
|
+
const handleTriggerChangeThrottled = useCallback(({ x, y, type }: { x: number; y: number; type?: string }) => {
|
|
205
|
+
'worklet'
|
|
206
|
+
const now = Date.now()
|
|
207
|
+
if (now - lastChangeTime.value >= changeThrottleTime) {
|
|
208
|
+
lastChangeTime.value = now
|
|
209
|
+
runOnJS(handleTriggerChange)({ x, y, type })
|
|
210
|
+
}
|
|
211
|
+
}, [changeThrottleTime])
|
|
212
|
+
|
|
198
213
|
useEffect(() => {
|
|
199
214
|
runOnUI(() => {
|
|
200
215
|
if (offsetX.value !== x || offsetY.value !== y) {
|
|
@@ -215,7 +230,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
215
230
|
})
|
|
216
231
|
: newY
|
|
217
232
|
}
|
|
218
|
-
if (
|
|
233
|
+
if (bindchange) {
|
|
219
234
|
runOnJS(handleTriggerChange)({
|
|
220
235
|
x: newX,
|
|
221
236
|
y: newY,
|
|
@@ -325,7 +340,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
325
340
|
setHeight(height || 0)
|
|
326
341
|
}
|
|
327
342
|
nodeRef.current?.measure((x: number, y: number, width: number, height: number) => {
|
|
328
|
-
const {
|
|
343
|
+
const { top: navigationY = 0 } = navigation?.layout || {}
|
|
329
344
|
layoutRef.current = { x, y: y - navigationY, width, height, offsetLeft: 0, offsetTop: 0 }
|
|
330
345
|
resetBoundaryAndCheck({ width, height })
|
|
331
346
|
})
|
|
@@ -333,7 +348,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
333
348
|
}
|
|
334
349
|
|
|
335
350
|
const extendEvent = useCallback((e: any, type: 'start' | 'move' | 'end') => {
|
|
336
|
-
const {
|
|
351
|
+
const { top: navigationY = 0 } = navigation?.layout || {}
|
|
337
352
|
const touchArr = [e.changedTouches, e.allTouches]
|
|
338
353
|
touchArr.forEach(touches => {
|
|
339
354
|
touches && touches.forEach((item: { absoluteX: number; absoluteY: number; pageX: number; pageY: number; clientX: number; clientY: number }) => {
|
|
@@ -356,12 +371,14 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
356
371
|
}, [])
|
|
357
372
|
|
|
358
373
|
const triggerStartOnJS = ({ e }: { e: GestureTouchEvent }) => {
|
|
374
|
+
const { bindtouchstart, catchtouchstart } = propsRef.current
|
|
359
375
|
extendEvent(e, 'start')
|
|
360
376
|
bindtouchstart && bindtouchstart(e)
|
|
361
377
|
catchtouchstart && catchtouchstart(e)
|
|
362
378
|
}
|
|
363
379
|
|
|
364
380
|
const triggerMoveOnJS = ({ e, hasTouchmove, hasCatchTouchmove, touchEvent }: { e: GestureTouchEvent; hasTouchmove: boolean; hasCatchTouchmove: boolean; touchEvent: string }) => {
|
|
381
|
+
const { bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove } = propsRef.current
|
|
365
382
|
extendEvent(e, 'move')
|
|
366
383
|
if (hasTouchmove) {
|
|
367
384
|
if (touchEvent === 'htouchmove') {
|
|
@@ -383,6 +400,7 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
383
400
|
}
|
|
384
401
|
|
|
385
402
|
const triggerEndOnJS = ({ e }: { e: GestureTouchEvent }) => {
|
|
403
|
+
const { bindtouchend, catchtouchend } = propsRef.current
|
|
386
404
|
extendEvent(e, 'end')
|
|
387
405
|
bindtouchend && bindtouchend(e)
|
|
388
406
|
catchtouchend && catchtouchend(e)
|
|
@@ -454,8 +472,9 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
454
472
|
offsetY.value = newY
|
|
455
473
|
}
|
|
456
474
|
}
|
|
457
|
-
if (
|
|
458
|
-
runOnJS
|
|
475
|
+
if (bindchange) {
|
|
476
|
+
// 使用节流版本减少 runOnJS 调用
|
|
477
|
+
handleTriggerChangeThrottled({
|
|
459
478
|
x: offsetX.value,
|
|
460
479
|
y: offsetY.value
|
|
461
480
|
})
|
|
@@ -493,47 +512,47 @@ const _MovableView = forwardRef<HandlerRef<View, MovableViewProps>, MovableViewP
|
|
|
493
512
|
})
|
|
494
513
|
: y
|
|
495
514
|
}
|
|
496
|
-
if (
|
|
515
|
+
if (bindchange) {
|
|
497
516
|
runOnJS(handleTriggerChange)({
|
|
498
517
|
x,
|
|
499
518
|
y
|
|
500
519
|
})
|
|
501
520
|
}
|
|
502
521
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
}
|
|
522
|
+
} else if (inertia) {
|
|
523
|
+
// 惯性处理
|
|
524
|
+
if (direction === 'horizontal' || direction === 'all') {
|
|
525
|
+
xInertialMotion.value = true
|
|
526
|
+
offsetX.value = withDecay({
|
|
527
|
+
velocity: e.velocityX / 10,
|
|
528
|
+
rubberBandEffect: outOfBounds,
|
|
529
|
+
clamp: draggableXRange.value
|
|
530
|
+
}, () => {
|
|
531
|
+
xInertialMotion.value = false
|
|
532
|
+
if (bindchange) {
|
|
533
|
+
runOnJS(handleTriggerChange)({
|
|
534
|
+
x: offsetX.value,
|
|
535
|
+
y: offsetY.value
|
|
536
|
+
})
|
|
537
|
+
}
|
|
538
|
+
})
|
|
539
|
+
}
|
|
540
|
+
if (direction === 'vertical' || direction === 'all') {
|
|
541
|
+
yInertialMotion.value = true
|
|
542
|
+
offsetY.value = withDecay({
|
|
543
|
+
velocity: e.velocityY / 10,
|
|
544
|
+
rubberBandEffect: outOfBounds,
|
|
545
|
+
clamp: draggableYRange.value
|
|
546
|
+
}, () => {
|
|
547
|
+
yInertialMotion.value = false
|
|
548
|
+
if (bindchange) {
|
|
549
|
+
runOnJS(handleTriggerChange)({
|
|
550
|
+
x: offsetX.value,
|
|
551
|
+
y: offsetY.value
|
|
552
|
+
})
|
|
553
|
+
}
|
|
554
|
+
})
|
|
555
|
+
}
|
|
537
556
|
}
|
|
538
557
|
})
|
|
539
558
|
.withRef(movableGestureRef)
|
|
@@ -116,6 +116,9 @@ const _RichText = forwardRef<HandlerRef<View, _RichTextProps>, _RichTextProps>((
|
|
|
116
116
|
source: { html: generateHTML(html) },
|
|
117
117
|
onMessage: (event: WebViewMessageEvent) => {
|
|
118
118
|
setWebViewHeight(+event.nativeEvent.data)
|
|
119
|
+
},
|
|
120
|
+
style: {
|
|
121
|
+
backgroundColor: 'transparent'
|
|
119
122
|
}
|
|
120
123
|
})
|
|
121
124
|
)
|
|
@@ -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,48 @@ 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
|
-
|
|
51
|
-
'scroll-
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'
|
|
59
|
-
'refresher-
|
|
60
|
-
'refresher-
|
|
61
|
-
'refresher-
|
|
62
|
-
'refresher-
|
|
63
|
-
'
|
|
64
|
-
'scroll-
|
|
65
|
-
'
|
|
66
|
-
'
|
|
67
|
-
'
|
|
68
|
-
'enable-
|
|
69
|
-
'
|
|
70
|
-
'
|
|
71
|
-
'parent-
|
|
72
|
-
'parent-
|
|
73
|
-
'
|
|
74
|
-
'wait-for'?: Array<GestureHandler
|
|
75
|
-
'simultaneous-handlers'?: Array<GestureHandler
|
|
76
|
-
'scroll-event-throttle'?:
|
|
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
|
+
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;
|
|
88
88
|
__selectRef?: (selector: string, nodeType: 'node' | 'component', all?: boolean) => HandlerRef<any, any>
|
|
89
89
|
}
|
|
90
90
|
type ScrollAdditionalProps = {
|
|
@@ -109,6 +109,8 @@ type ScrollAdditionalProps = {
|
|
|
109
109
|
onMomentumScrollEnd?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
const AnimatedScrollView = RNAnimated.createAnimatedComponent(ScrollView) as React.ComponentType<any>
|
|
113
|
+
|
|
112
114
|
const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, ScrollViewProps>((scrollViewProps: ScrollViewProps = {}, ref): JSX.Element => {
|
|
113
115
|
const { textProps, innerProps: props = {} } = splitProps(scrollViewProps)
|
|
114
116
|
const {
|
|
@@ -145,10 +147,13 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
145
147
|
'parent-height': parentHeight,
|
|
146
148
|
'simultaneous-handlers': originSimultaneousHandlers,
|
|
147
149
|
'wait-for': waitFor,
|
|
150
|
+
'enable-sticky': enableSticky,
|
|
148
151
|
'scroll-event-throttle': scrollEventThrottle = 0,
|
|
149
152
|
__selectRef
|
|
150
153
|
} = props
|
|
151
154
|
|
|
155
|
+
const scrollOffset = useRef(new RNAnimated.Value(0)).current
|
|
156
|
+
|
|
152
157
|
const simultaneousHandlers = flatGesture(originSimultaneousHandlers)
|
|
153
158
|
const waitForHandlers = flatGesture(waitFor)
|
|
154
159
|
|
|
@@ -218,14 +223,15 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
218
223
|
gestureRef: scrollViewRef
|
|
219
224
|
})
|
|
220
225
|
|
|
226
|
+
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout })
|
|
227
|
+
|
|
221
228
|
const contextValue = useMemo(() => {
|
|
222
229
|
return {
|
|
223
|
-
gestureRef: scrollViewRef
|
|
230
|
+
gestureRef: scrollViewRef,
|
|
231
|
+
scrollOffset
|
|
224
232
|
}
|
|
225
233
|
}, [])
|
|
226
234
|
|
|
227
|
-
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout })
|
|
228
|
-
|
|
229
235
|
const hasRefresherLayoutRef = useRef(false)
|
|
230
236
|
|
|
231
237
|
// layout 完成前先隐藏,避免安卓闪烁问题
|
|
@@ -487,6 +493,16 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
487
493
|
updateIntersection()
|
|
488
494
|
}
|
|
489
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
|
+
|
|
490
506
|
function onScrollDragStart (e: NativeSyntheticEvent<NativeScrollEvent>) {
|
|
491
507
|
hasCallScrollToLower.current = false
|
|
492
508
|
hasCallScrollToUpper.current = false
|
|
@@ -661,7 +677,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
661
677
|
scrollEnabled: !enableScroll ? false : !!(scrollX || scrollY),
|
|
662
678
|
bounces: false,
|
|
663
679
|
ref: scrollViewRef,
|
|
664
|
-
onScroll: onScroll,
|
|
680
|
+
onScroll: enableSticky ? scrollHandler : onScroll,
|
|
665
681
|
onContentSizeChange: onContentSizeChange,
|
|
666
682
|
bindtouchstart: ((enhanced && binddragstart) || bindtouchstart) && onScrollTouchStart,
|
|
667
683
|
bindtouchmove: ((enhanced && binddragging) || bindtouchmove) && onScrollTouchMove,
|
|
@@ -716,11 +732,13 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
716
732
|
'bindrefresherrefresh'
|
|
717
733
|
], { layoutRef })
|
|
718
734
|
|
|
735
|
+
const ScrollViewComponent = enableSticky ? AnimatedScrollView : ScrollView
|
|
736
|
+
|
|
719
737
|
const withRefresherScrollView = createElement(
|
|
720
738
|
GestureDetector,
|
|
721
739
|
{ gesture: panGesture },
|
|
722
740
|
createElement(
|
|
723
|
-
|
|
741
|
+
ScrollViewComponent,
|
|
724
742
|
innerProps,
|
|
725
743
|
createElement(
|
|
726
744
|
Animated.View,
|
|
@@ -748,8 +766,8 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
|
|
|
748
766
|
)
|
|
749
767
|
|
|
750
768
|
const commonScrollView = createElement(
|
|
751
|
-
|
|
752
|
-
extendObject(innerProps, {
|
|
769
|
+
ScrollViewComponent,
|
|
770
|
+
extendObject({}, innerProps, {
|
|
753
771
|
refreshControl: refresherEnabled
|
|
754
772
|
? createElement(RefreshControl, extendObject({
|
|
755
773
|
progressBackgroundColor: refresherBackground,
|
|
@@ -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
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
|
|
2
|
+
import { useRef, forwardRef, createElement, ReactNode, useCallback, useMemo } from 'react'
|
|
3
|
+
import { View, ViewStyle } from 'react-native'
|
|
4
|
+
import useNodesRef, { HandlerRef } from './useNodesRef'
|
|
5
|
+
import { splitProps, splitStyle, useTransformStyle, wrapChildren, useLayout, extendObject } from './utils'
|
|
6
|
+
import { StickyContext } from './context'
|
|
7
|
+
import useInnerProps from './getInnerListeners'
|
|
8
|
+
|
|
9
|
+
interface StickySectionProps {
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
style?: ViewStyle;
|
|
12
|
+
'offset-top'?: number;
|
|
13
|
+
'enable-var'?: boolean;
|
|
14
|
+
'external-var-context'?: Record<string, any>;
|
|
15
|
+
'parent-font-size'?: number;
|
|
16
|
+
'parent-width'?: number;
|
|
17
|
+
'parent-height'?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const _StickySection = forwardRef<HandlerRef<View, StickySectionProps>, StickySectionProps>((stickySectionProps: StickySectionProps = {}, ref): JSX.Element => {
|
|
21
|
+
const { textProps, innerProps: props = {} } = splitProps(stickySectionProps)
|
|
22
|
+
const {
|
|
23
|
+
style,
|
|
24
|
+
'enable-var': enableVar,
|
|
25
|
+
'external-var-context': externalVarContext,
|
|
26
|
+
'parent-font-size': parentFontSize,
|
|
27
|
+
'parent-width': parentWidth,
|
|
28
|
+
'parent-height': parentHeight
|
|
29
|
+
} = props
|
|
30
|
+
const sectionRef = useRef<View>(null)
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
normalStyle,
|
|
34
|
+
hasVarDec,
|
|
35
|
+
varContextRef,
|
|
36
|
+
hasSelfPercent,
|
|
37
|
+
setWidth,
|
|
38
|
+
setHeight
|
|
39
|
+
} = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
|
|
40
|
+
|
|
41
|
+
const { layoutRef, layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: sectionRef, onLayout })
|
|
42
|
+
|
|
43
|
+
const { textStyle, innerStyle = {} } = splitStyle(normalStyle)
|
|
44
|
+
|
|
45
|
+
const stickyHeaders = useRef<Map<string, any>>(new Map())
|
|
46
|
+
|
|
47
|
+
const registerStickyHeader = useCallback((item: { id: string, updatePosition: Function }) => {
|
|
48
|
+
stickyHeaders.current.set(item.id, item)
|
|
49
|
+
}, [])
|
|
50
|
+
|
|
51
|
+
const unregisterStickyHeader = useCallback((id: string) => {
|
|
52
|
+
stickyHeaders.current.delete(id)
|
|
53
|
+
}, [])
|
|
54
|
+
|
|
55
|
+
const contextValue = useMemo(() => ({
|
|
56
|
+
registerStickyHeader,
|
|
57
|
+
unregisterStickyHeader
|
|
58
|
+
}), [])
|
|
59
|
+
|
|
60
|
+
useNodesRef(props, ref, sectionRef, {
|
|
61
|
+
style: normalStyle
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
function onLayout () {
|
|
65
|
+
stickyHeaders.current.forEach(item => {
|
|
66
|
+
item.updatePosition()
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const innerProps = useInnerProps(extendObject({}, props, {
|
|
71
|
+
style: extendObject(innerStyle, layoutStyle),
|
|
72
|
+
ref: sectionRef
|
|
73
|
+
}, layoutProps), [], { layoutRef })
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
createElement(
|
|
77
|
+
View,
|
|
78
|
+
innerProps,
|
|
79
|
+
createElement(
|
|
80
|
+
StickyContext.Provider,
|
|
81
|
+
{ value: contextValue },
|
|
82
|
+
wrapChildren(
|
|
83
|
+
props,
|
|
84
|
+
{
|
|
85
|
+
hasVarDec,
|
|
86
|
+
varContext: varContextRef.current,
|
|
87
|
+
textStyle,
|
|
88
|
+
textProps
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
))
|
|
92
|
+
)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
_StickySection.displayName = 'MpxStickySection'
|
|
96
|
+
export default _StickySection
|