@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
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
2
|
+
import { DefaultFallback, DefaultLoading, PageWrapper } from './AsyncContainer';
|
|
3
|
+
const asyncChunkMap = new Map();
|
|
4
|
+
const AsyncSuspense = ({ type, props, chunkName, request, loading, fallback, getChildren }) => {
|
|
5
|
+
const [status, setStatus] = useState('pending');
|
|
6
|
+
const loaded = asyncChunkMap.has(request);
|
|
7
|
+
const [, setKey] = useState(0);
|
|
8
|
+
const chunkPromise = useRef(null);
|
|
9
|
+
const reloadPage = useCallback(() => {
|
|
10
|
+
setKey((preV) => preV + 1);
|
|
11
|
+
console.log('[mpxAsyncSuspense]: reload page');
|
|
12
|
+
setStatus('pending');
|
|
13
|
+
}, []);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!loaded && status === 'pending') {
|
|
16
|
+
// todo 清楚副作用?
|
|
17
|
+
console.log('the current :', chunkPromise.current);
|
|
18
|
+
chunkPromise.current
|
|
19
|
+
.then((m) => {
|
|
20
|
+
console.log('[mpxAsyncSuspense]: load sucess');
|
|
21
|
+
asyncChunkMap.set(request, m.__esModule ? m.default : m);
|
|
22
|
+
setStatus('loaded');
|
|
23
|
+
})
|
|
24
|
+
.catch((e) => {
|
|
25
|
+
if (type === 'component') {
|
|
26
|
+
console.log(11111, e);
|
|
27
|
+
global.onLazyLoadError({
|
|
28
|
+
type: 'subpackage',
|
|
29
|
+
subpackage: [chunkName],
|
|
30
|
+
errMsg: `loadSubpackage: ${e.type}`
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
console.log('[mpxAsyncSuspense]: load eror', e);
|
|
34
|
+
chunkPromise.current = null;
|
|
35
|
+
setStatus('error');
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
if (loaded) {
|
|
40
|
+
const Comp = asyncChunkMap.get(request);
|
|
41
|
+
return <Comp {...props}></Comp>;
|
|
42
|
+
}
|
|
43
|
+
else if (status === 'error') {
|
|
44
|
+
console.log('the status is:', status);
|
|
45
|
+
if (type === 'page') {
|
|
46
|
+
const Fallback = fallback || DefaultFallback;
|
|
47
|
+
return <><PageWrapper><Fallback onReload={reloadPage}></Fallback></PageWrapper></>;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const Fallback = loading;
|
|
51
|
+
return <Fallback {...props}></Fallback>;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
if (!chunkPromise.current) {
|
|
56
|
+
chunkPromise.current = getChildren();
|
|
57
|
+
}
|
|
58
|
+
if (type === 'page') {
|
|
59
|
+
const Fallback = loading || DefaultLoading;
|
|
60
|
+
return <PageWrapper><Fallback /></PageWrapper>;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const Fallback = loading;
|
|
64
|
+
return <Fallback {...props}></Fallback>;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
export default AsyncSuspense;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createContext } from 'react';
|
|
2
|
+
import { Animated } from 'react-native';
|
|
3
|
+
import { noop } from '@mpxjs/utils';
|
|
2
4
|
export const MovableAreaContext = createContext({ width: 0, height: 0 });
|
|
3
5
|
export const FormContext = createContext(null);
|
|
4
6
|
export const CheckboxGroupContext = createContext(null);
|
|
@@ -10,5 +12,6 @@ export const IntersectionObserverContext = createContext(null);
|
|
|
10
12
|
export const RouteContext = createContext(null);
|
|
11
13
|
export const SwiperContext = createContext({});
|
|
12
14
|
export const KeyboardAvoidContext = createContext(null);
|
|
13
|
-
export const ScrollViewContext = createContext({ gestureRef: null });
|
|
15
|
+
export const ScrollViewContext = createContext({ gestureRef: null, scrollOffset: new Animated.Value(0) });
|
|
14
16
|
export const PortalContext = createContext(null);
|
|
17
|
+
export const StickyContext = createContext({ registerStickyHeader: noop, unregisterStickyHeader: noop });
|
|
@@ -8,7 +8,7 @@ const globalEventState = {
|
|
|
8
8
|
const getTouchEvent = (type, event, config) => {
|
|
9
9
|
const { navigation, propsRef, layoutRef } = config;
|
|
10
10
|
const props = propsRef.current;
|
|
11
|
-
const {
|
|
11
|
+
const { top: navigationY = 0 } = navigation?.layout || {};
|
|
12
12
|
const nativeEvent = event.nativeEvent;
|
|
13
13
|
const { timestamp, pageX, pageY, touches, changedTouches } = nativeEvent;
|
|
14
14
|
const { id } = props;
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* ✔ bindtap
|
|
36
36
|
*/
|
|
37
37
|
import { createElement, useEffect, useRef, forwardRef, useContext } from 'react';
|
|
38
|
-
import { View, StyleSheet, Animated, Easing } from 'react-native';
|
|
38
|
+
import { View, StyleSheet, Animated, Easing, useAnimatedValue } from 'react-native';
|
|
39
39
|
import { warn } from '@mpxjs/utils';
|
|
40
40
|
import { GestureDetector } from 'react-native-gesture-handler';
|
|
41
41
|
import { getCurrentPage, splitProps, splitStyle, useLayout, useTransformStyle, wrapChildren, extendObject, useHover } from './utils';
|
|
@@ -104,7 +104,7 @@ const timer = (data, time = 3000) => new Promise((resolve) => {
|
|
|
104
104
|
}, time);
|
|
105
105
|
});
|
|
106
106
|
const Loading = ({ alone = false }) => {
|
|
107
|
-
const image =
|
|
107
|
+
const image = useAnimatedValue(0);
|
|
108
108
|
const rotate = image.interpolate({
|
|
109
109
|
inputRange: [0, 1],
|
|
110
110
|
outputRange: ['0deg', '360deg']
|
|
@@ -91,7 +91,7 @@ const Input = forwardRef((props, ref) => {
|
|
|
91
91
|
});
|
|
92
92
|
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef });
|
|
93
93
|
useEffect(() => {
|
|
94
|
-
if (
|
|
94
|
+
if (value !== tmpValue.current) {
|
|
95
95
|
const parsed = parseValue(value);
|
|
96
96
|
tmpValue.current = parsed;
|
|
97
97
|
setInputValue(parsed);
|
|
@@ -21,7 +21,7 @@ const _MovableArea = forwardRef((props, ref) => {
|
|
|
21
21
|
}), [normalStyle.width, normalStyle.height]);
|
|
22
22
|
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: movableViewRef });
|
|
23
23
|
const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
|
|
24
|
-
style: extendObject({ height: contextValue.height, width: contextValue.width
|
|
24
|
+
style: extendObject({ height: contextValue.height, width: contextValue.width }, normalStyle, layoutStyle),
|
|
25
25
|
ref: movableViewRef
|
|
26
26
|
}), [], { layoutRef });
|
|
27
27
|
let movableComponent = createElement(MovableAreaContext.Provider, { value: contextValue }, createElement(View, innerProps, wrapChildren(props, {
|
|
@@ -41,7 +41,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
41
41
|
const hasLayoutRef = useRef(false);
|
|
42
42
|
const propsRef = useRef({});
|
|
43
43
|
propsRef.current = (props || {});
|
|
44
|
-
const { x = 0, y = 0, inertia = false, disabled = false, animation = true, 'out-of-bounds': outOfBounds = false, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, direction = 'none', 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, bindtouchstart, catchtouchstart, bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove, bindtouchend, catchtouchend } = props;
|
|
44
|
+
const { x = 0, y = 0, inertia = false, disabled = false, animation = true, 'out-of-bounds': outOfBounds = false, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, direction = 'none', 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, changeThrottleTime = 60, bindtouchstart, catchtouchstart, bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove, bindtouchend, catchtouchend, bindchange } = props;
|
|
45
45
|
const { hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(Object.assign({}, style, styles.container), { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
46
46
|
const navigation = useNavigation();
|
|
47
47
|
const prevSimultaneousHandlersRef = useRef(originSimultaneousHandlers || []);
|
|
@@ -62,6 +62,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
62
62
|
const isFirstTouch = useSharedValue(true);
|
|
63
63
|
const touchEvent = useSharedValue('');
|
|
64
64
|
const initialViewPosition = useSharedValue({ x: x || 0, y: y || 0 });
|
|
65
|
+
const lastChangeTime = useSharedValue(0);
|
|
65
66
|
const MovableAreaLayout = useContext(MovableAreaContext);
|
|
66
67
|
const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
|
|
67
68
|
const waitForHandlers = flatGesture(waitFor);
|
|
@@ -99,6 +100,15 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
99
100
|
layoutRef
|
|
100
101
|
}, propsRef.current));
|
|
101
102
|
}, []);
|
|
103
|
+
// 节流版本的 change 事件触发
|
|
104
|
+
const handleTriggerChangeThrottled = useCallback(({ x, y, type }) => {
|
|
105
|
+
'worklet';
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
if (now - lastChangeTime.value >= changeThrottleTime) {
|
|
108
|
+
lastChangeTime.value = now;
|
|
109
|
+
runOnJS(handleTriggerChange)({ x, y, type });
|
|
110
|
+
}
|
|
111
|
+
}, [changeThrottleTime]);
|
|
102
112
|
useEffect(() => {
|
|
103
113
|
runOnUI(() => {
|
|
104
114
|
if (offsetX.value !== x || offsetY.value !== y) {
|
|
@@ -119,7 +129,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
119
129
|
})
|
|
120
130
|
: newY;
|
|
121
131
|
}
|
|
122
|
-
if (
|
|
132
|
+
if (bindchange) {
|
|
123
133
|
runOnJS(handleTriggerChange)({
|
|
124
134
|
x: newX,
|
|
125
135
|
y: newY,
|
|
@@ -223,14 +233,14 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
223
233
|
setHeight(height || 0);
|
|
224
234
|
}
|
|
225
235
|
nodeRef.current?.measure((x, y, width, height) => {
|
|
226
|
-
const {
|
|
236
|
+
const { top: navigationY = 0 } = navigation?.layout || {};
|
|
227
237
|
layoutRef.current = { x, y: y - navigationY, width, height, offsetLeft: 0, offsetTop: 0 };
|
|
228
238
|
resetBoundaryAndCheck({ width, height });
|
|
229
239
|
});
|
|
230
240
|
props.onLayout && props.onLayout(e);
|
|
231
241
|
};
|
|
232
242
|
const extendEvent = useCallback((e, type) => {
|
|
233
|
-
const {
|
|
243
|
+
const { top: navigationY = 0 } = navigation?.layout || {};
|
|
234
244
|
const touchArr = [e.changedTouches, e.allTouches];
|
|
235
245
|
touchArr.forEach(touches => {
|
|
236
246
|
touches && touches.forEach((item) => {
|
|
@@ -252,11 +262,13 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
252
262
|
});
|
|
253
263
|
}, []);
|
|
254
264
|
const triggerStartOnJS = ({ e }) => {
|
|
265
|
+
const { bindtouchstart, catchtouchstart } = propsRef.current;
|
|
255
266
|
extendEvent(e, 'start');
|
|
256
267
|
bindtouchstart && bindtouchstart(e);
|
|
257
268
|
catchtouchstart && catchtouchstart(e);
|
|
258
269
|
};
|
|
259
270
|
const triggerMoveOnJS = ({ e, hasTouchmove, hasCatchTouchmove, touchEvent }) => {
|
|
271
|
+
const { bindhtouchmove, bindvtouchmove, bindtouchmove, catchhtouchmove, catchvtouchmove, catchtouchmove } = propsRef.current;
|
|
260
272
|
extendEvent(e, 'move');
|
|
261
273
|
if (hasTouchmove) {
|
|
262
274
|
if (touchEvent === 'htouchmove') {
|
|
@@ -278,6 +290,7 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
278
290
|
}
|
|
279
291
|
};
|
|
280
292
|
const triggerEndOnJS = ({ e }) => {
|
|
293
|
+
const { bindtouchend, catchtouchend } = propsRef.current;
|
|
281
294
|
extendEvent(e, 'end');
|
|
282
295
|
bindtouchend && bindtouchend(e);
|
|
283
296
|
catchtouchend && catchtouchend(e);
|
|
@@ -350,8 +363,9 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
350
363
|
offsetY.value = newY;
|
|
351
364
|
}
|
|
352
365
|
}
|
|
353
|
-
if (
|
|
354
|
-
runOnJS
|
|
366
|
+
if (bindchange) {
|
|
367
|
+
// 使用节流版本减少 runOnJS 调用
|
|
368
|
+
handleTriggerChangeThrottled({
|
|
355
369
|
x: offsetX.value,
|
|
356
370
|
y: offsetY.value
|
|
357
371
|
});
|
|
@@ -390,47 +404,48 @@ const _MovableView = forwardRef((movableViewProps, ref) => {
|
|
|
390
404
|
})
|
|
391
405
|
: y;
|
|
392
406
|
}
|
|
393
|
-
if (
|
|
407
|
+
if (bindchange) {
|
|
394
408
|
runOnJS(handleTriggerChange)({
|
|
395
409
|
x,
|
|
396
410
|
y
|
|
397
411
|
});
|
|
398
412
|
}
|
|
399
413
|
}
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
// 惯性处理
|
|
403
|
-
if (direction === 'horizontal' || direction === 'all') {
|
|
404
|
-
xInertialMotion.value = true;
|
|
405
|
-
offsetX.value = withDecay({
|
|
406
|
-
velocity: e.velocityX / 10,
|
|
407
|
-
rubberBandEffect: outOfBounds,
|
|
408
|
-
clamp: draggableXRange.value
|
|
409
|
-
}, () => {
|
|
410
|
-
xInertialMotion.value = false;
|
|
411
|
-
if (propsRef.current.bindchange) {
|
|
412
|
-
runOnJS(handleTriggerChange)({
|
|
413
|
-
x: offsetX.value,
|
|
414
|
-
y: offsetY.value
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
414
|
}
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
415
|
+
else if (inertia) {
|
|
416
|
+
// 惯性处理
|
|
417
|
+
if (direction === 'horizontal' || direction === 'all') {
|
|
418
|
+
xInertialMotion.value = true;
|
|
419
|
+
offsetX.value = withDecay({
|
|
420
|
+
velocity: e.velocityX / 10,
|
|
421
|
+
rubberBandEffect: outOfBounds,
|
|
422
|
+
clamp: draggableXRange.value
|
|
423
|
+
}, () => {
|
|
424
|
+
xInertialMotion.value = false;
|
|
425
|
+
if (bindchange) {
|
|
426
|
+
runOnJS(handleTriggerChange)({
|
|
427
|
+
x: offsetX.value,
|
|
428
|
+
y: offsetY.value
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
if (direction === 'vertical' || direction === 'all') {
|
|
434
|
+
yInertialMotion.value = true;
|
|
435
|
+
offsetY.value = withDecay({
|
|
436
|
+
velocity: e.velocityY / 10,
|
|
437
|
+
rubberBandEffect: outOfBounds,
|
|
438
|
+
clamp: draggableYRange.value
|
|
439
|
+
}, () => {
|
|
440
|
+
yInertialMotion.value = false;
|
|
441
|
+
if (bindchange) {
|
|
442
|
+
runOnJS(handleTriggerChange)({
|
|
443
|
+
x: offsetX.value,
|
|
444
|
+
y: offsetY.value
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
}
|
|
434
449
|
}
|
|
435
450
|
})
|
|
436
451
|
.withRef(movableGestureRef);
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
* ✔ bindscroll
|
|
33
33
|
*/
|
|
34
34
|
import { ScrollView, RefreshControl, Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
35
|
+
import { Animated as RNAnimated } from 'react-native';
|
|
35
36
|
import { isValidElement, Children, useRef, useState, useEffect, forwardRef, useContext, useMemo, createElement } from 'react';
|
|
36
37
|
import Animated, { useAnimatedRef, useSharedValue, withTiming, useAnimatedStyle, runOnJS } from 'react-native-reanimated';
|
|
37
38
|
import { warn, hasOwn } from '@mpxjs/utils';
|
|
@@ -40,9 +41,11 @@ import useNodesRef from './useNodesRef';
|
|
|
40
41
|
import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, HIDDEN_STYLE } from './utils';
|
|
41
42
|
import { IntersectionObserverContext, ScrollViewContext } from './context';
|
|
42
43
|
import Portal from './mpx-portal';
|
|
44
|
+
const AnimatedScrollView = RNAnimated.createAnimatedComponent(ScrollView);
|
|
43
45
|
const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
44
46
|
const { textProps, innerProps: props = {} } = splitProps(scrollViewProps);
|
|
45
|
-
const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'refresher-threshold': refresherThreshold = 45, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, 'scroll-event-throttle': scrollEventThrottle = 0, __selectRef } = props;
|
|
47
|
+
const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'refresher-threshold': refresherThreshold = 45, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, 'enable-sticky': enableSticky, 'scroll-event-throttle': scrollEventThrottle = 0, __selectRef } = props;
|
|
48
|
+
const scrollOffset = useRef(new RNAnimated.Value(0)).current;
|
|
46
49
|
const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
|
|
47
50
|
const waitForHandlers = flatGesture(waitFor);
|
|
48
51
|
const snapScrollTop = useRef(0);
|
|
@@ -90,12 +93,13 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
90
93
|
},
|
|
91
94
|
gestureRef: scrollViewRef
|
|
92
95
|
});
|
|
96
|
+
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
|
|
93
97
|
const contextValue = useMemo(() => {
|
|
94
98
|
return {
|
|
95
|
-
gestureRef: scrollViewRef
|
|
99
|
+
gestureRef: scrollViewRef,
|
|
100
|
+
scrollOffset
|
|
96
101
|
};
|
|
97
102
|
}, []);
|
|
98
|
-
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
|
|
99
103
|
const hasRefresherLayoutRef = useRef(false);
|
|
100
104
|
// layout 完成前先隐藏,避免安卓闪烁问题
|
|
101
105
|
const refresherLayoutStyle = useMemo(() => { return !hasRefresherLayoutRef.current ? HIDDEN_STYLE : {}; }, [hasRefresherLayoutRef.current]);
|
|
@@ -321,6 +325,12 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
321
325
|
updateScrollOptions(e, { scrollLeft, scrollTop });
|
|
322
326
|
updateIntersection();
|
|
323
327
|
}
|
|
328
|
+
const scrollHandler = RNAnimated.event([{ nativeEvent: { contentOffset: { y: scrollOffset } } }], {
|
|
329
|
+
useNativeDriver: true,
|
|
330
|
+
listener: (event) => {
|
|
331
|
+
onScroll(event);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
324
334
|
function onScrollDragStart(e) {
|
|
325
335
|
hasCallScrollToLower.current = false;
|
|
326
336
|
hasCallScrollToUpper.current = false;
|
|
@@ -481,7 +491,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
481
491
|
scrollEnabled: !enableScroll ? false : !!(scrollX || scrollY),
|
|
482
492
|
bounces: false,
|
|
483
493
|
ref: scrollViewRef,
|
|
484
|
-
onScroll: onScroll,
|
|
494
|
+
onScroll: enableSticky ? scrollHandler : onScroll,
|
|
485
495
|
onContentSizeChange: onContentSizeChange,
|
|
486
496
|
bindtouchstart: ((enhanced && binddragstart) || bindtouchstart) && onScrollTouchStart,
|
|
487
497
|
bindtouchmove: ((enhanced && binddragging) || bindtouchmove) && onScrollTouchMove,
|
|
@@ -523,13 +533,14 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
|
|
|
523
533
|
'bindscrolltolower',
|
|
524
534
|
'bindrefresherrefresh'
|
|
525
535
|
], { layoutRef });
|
|
526
|
-
const
|
|
536
|
+
const ScrollViewComponent = enableSticky ? AnimatedScrollView : ScrollView;
|
|
537
|
+
const withRefresherScrollView = createElement(GestureDetector, { gesture: panGesture }, createElement(ScrollViewComponent, innerProps, createElement(Animated.View, { style: [refresherAnimatedStyle, refresherLayoutStyle], onLayout: onRefresherLayout }, refresherContent), createElement(Animated.View, { style: contentAnimatedStyle }, createElement(ScrollViewContext.Provider, { value: contextValue }, wrapChildren(extendObject({}, props, { children: otherContent }), {
|
|
527
538
|
hasVarDec,
|
|
528
539
|
varContext: varContextRef.current,
|
|
529
540
|
textStyle,
|
|
530
541
|
textProps
|
|
531
542
|
})))));
|
|
532
|
-
const commonScrollView = createElement(
|
|
543
|
+
const commonScrollView = createElement(ScrollViewComponent, extendObject({}, innerProps, {
|
|
533
544
|
refreshControl: refresherEnabled
|
|
534
545
|
? createElement(RefreshControl, extendObject({
|
|
535
546
|
progressBackgroundColor: refresherBackground,
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { useEffect, useRef, useContext, forwardRef, useMemo, createElement, useId } from 'react';
|
|
2
|
+
import { Animated, StyleSheet, useAnimatedValue } from 'react-native';
|
|
3
|
+
import { ScrollViewContext, StickyContext } from './context';
|
|
4
|
+
import useNodesRef from './useNodesRef';
|
|
5
|
+
import { splitProps, splitStyle, useTransformStyle, wrapChildren, useLayout, extendObject } from './utils';
|
|
6
|
+
import { error } from '@mpxjs/utils';
|
|
7
|
+
import useInnerProps, { getCustomEvent } from './getInnerListeners';
|
|
8
|
+
const _StickyHeader = forwardRef((stickyHeaderProps = {}, ref) => {
|
|
9
|
+
const { textProps, innerProps: props = {} } = splitProps(stickyHeaderProps);
|
|
10
|
+
const { style, bindstickontopchange, padding = [0, 0, 0, 0], 'offset-top': offsetTop = 0, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
|
|
11
|
+
const scrollViewContext = useContext(ScrollViewContext);
|
|
12
|
+
const stickyContext = useContext(StickyContext);
|
|
13
|
+
const { scrollOffset } = scrollViewContext;
|
|
14
|
+
const { registerStickyHeader, unregisterStickyHeader } = stickyContext;
|
|
15
|
+
const headerRef = useRef(null);
|
|
16
|
+
const isStickOnTopRef = useRef(false);
|
|
17
|
+
const id = useId();
|
|
18
|
+
const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
19
|
+
const { layoutRef, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: headerRef, onLayout });
|
|
20
|
+
const { textStyle, innerStyle = {} } = splitStyle(normalStyle);
|
|
21
|
+
const headerTopAnimated = useAnimatedValue(0);
|
|
22
|
+
// harmony animatedValue 不支持通过 _value 访问
|
|
23
|
+
const headerTopRef = useRef(0);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
registerStickyHeader({ key: id, updatePosition });
|
|
26
|
+
return () => {
|
|
27
|
+
unregisterStickyHeader(id);
|
|
28
|
+
};
|
|
29
|
+
}, []);
|
|
30
|
+
function updatePosition() {
|
|
31
|
+
if (headerRef.current) {
|
|
32
|
+
const scrollViewRef = scrollViewContext.gestureRef;
|
|
33
|
+
if (scrollViewRef && scrollViewRef.current) {
|
|
34
|
+
headerRef.current.measureLayout(scrollViewRef.current, (left, top) => {
|
|
35
|
+
Animated.timing(headerTopAnimated, {
|
|
36
|
+
toValue: top,
|
|
37
|
+
duration: 0,
|
|
38
|
+
useNativeDriver: true
|
|
39
|
+
}).start();
|
|
40
|
+
headerTopRef.current = top;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
error('StickyHeader measureLayout error: scrollViewRef is not a valid native component reference');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function onLayout(e) {
|
|
49
|
+
updatePosition();
|
|
50
|
+
}
|
|
51
|
+
useNodesRef(props, ref, headerRef, {
|
|
52
|
+
style: normalStyle
|
|
53
|
+
});
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (!bindstickontopchange)
|
|
56
|
+
return;
|
|
57
|
+
const listener = scrollOffset.addListener((state) => {
|
|
58
|
+
const currentScrollValue = state.value;
|
|
59
|
+
const newIsStickOnTop = currentScrollValue > headerTopRef.current;
|
|
60
|
+
if (newIsStickOnTop !== isStickOnTopRef.current) {
|
|
61
|
+
isStickOnTopRef.current = newIsStickOnTop;
|
|
62
|
+
bindstickontopchange(getCustomEvent('stickontopchange', {}, {
|
|
63
|
+
detail: {
|
|
64
|
+
isStickOnTop: newIsStickOnTop
|
|
65
|
+
},
|
|
66
|
+
layoutRef
|
|
67
|
+
}, props));
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return () => {
|
|
71
|
+
scrollOffset.removeListener(listener);
|
|
72
|
+
};
|
|
73
|
+
}, []);
|
|
74
|
+
const animatedStyle = useMemo(() => {
|
|
75
|
+
const translateY = Animated.subtract(scrollOffset, headerTopAnimated).interpolate({
|
|
76
|
+
inputRange: [0, 1],
|
|
77
|
+
outputRange: [0, 1],
|
|
78
|
+
extrapolateLeft: 'clamp',
|
|
79
|
+
extrapolateRight: 'extend'
|
|
80
|
+
});
|
|
81
|
+
const finalTranslateY = offsetTop === 0
|
|
82
|
+
? translateY
|
|
83
|
+
: Animated.add(translateY, Animated.subtract(scrollOffset, headerTopAnimated).interpolate({
|
|
84
|
+
inputRange: [0, 1],
|
|
85
|
+
outputRange: [0, offsetTop],
|
|
86
|
+
extrapolate: 'clamp'
|
|
87
|
+
}));
|
|
88
|
+
return {
|
|
89
|
+
transform: [{ translateY: finalTranslateY }]
|
|
90
|
+
};
|
|
91
|
+
}, [scrollOffset, headerTopAnimated, offsetTop]);
|
|
92
|
+
const innerProps = useInnerProps(extendObject({}, props, {
|
|
93
|
+
ref: headerRef,
|
|
94
|
+
style: extendObject({}, styles.content, innerStyle, animatedStyle, {
|
|
95
|
+
paddingTop: padding[0] || 0,
|
|
96
|
+
paddingRight: padding[1] || 0,
|
|
97
|
+
paddingBottom: padding[2] || 0,
|
|
98
|
+
paddingLeft: padding[3] || 0
|
|
99
|
+
})
|
|
100
|
+
}, layoutProps), [], { layoutRef });
|
|
101
|
+
return (createElement(Animated.View, innerProps, wrapChildren(props, {
|
|
102
|
+
hasVarDec,
|
|
103
|
+
varContext: varContextRef.current,
|
|
104
|
+
textStyle,
|
|
105
|
+
textProps
|
|
106
|
+
})));
|
|
107
|
+
});
|
|
108
|
+
const styles = StyleSheet.create({
|
|
109
|
+
content: {
|
|
110
|
+
width: '100%',
|
|
111
|
+
zIndex: 10
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
_StickyHeader.displayName = 'MpxStickyHeader';
|
|
115
|
+
export default _StickyHeader;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useRef, forwardRef, createElement, useCallback, useMemo } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import useNodesRef from './useNodesRef';
|
|
4
|
+
import { splitProps, splitStyle, useTransformStyle, wrapChildren, useLayout, extendObject } from './utils';
|
|
5
|
+
import { StickyContext } from './context';
|
|
6
|
+
import useInnerProps from './getInnerListeners';
|
|
7
|
+
const _StickySection = forwardRef((stickySectionProps = {}, ref) => {
|
|
8
|
+
const { textProps, innerProps: props = {} } = splitProps(stickySectionProps);
|
|
9
|
+
const { style, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
|
|
10
|
+
const sectionRef = useRef(null);
|
|
11
|
+
const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
12
|
+
const { layoutRef, layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: sectionRef, onLayout });
|
|
13
|
+
const { textStyle, innerStyle = {} } = splitStyle(normalStyle);
|
|
14
|
+
const stickyHeaders = useRef(new Map());
|
|
15
|
+
const registerStickyHeader = useCallback((item) => {
|
|
16
|
+
stickyHeaders.current.set(item.id, item);
|
|
17
|
+
}, []);
|
|
18
|
+
const unregisterStickyHeader = useCallback((id) => {
|
|
19
|
+
stickyHeaders.current.delete(id);
|
|
20
|
+
}, []);
|
|
21
|
+
const contextValue = useMemo(() => ({
|
|
22
|
+
registerStickyHeader,
|
|
23
|
+
unregisterStickyHeader
|
|
24
|
+
}), []);
|
|
25
|
+
useNodesRef(props, ref, sectionRef, {
|
|
26
|
+
style: normalStyle
|
|
27
|
+
});
|
|
28
|
+
function onLayout() {
|
|
29
|
+
stickyHeaders.current.forEach(item => {
|
|
30
|
+
item.updatePosition();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const innerProps = useInnerProps(extendObject({}, props, {
|
|
34
|
+
style: extendObject(innerStyle, layoutStyle),
|
|
35
|
+
ref: sectionRef
|
|
36
|
+
}, layoutProps), [], { layoutRef });
|
|
37
|
+
return (createElement(View, innerProps, createElement(StickyContext.Provider, { value: contextValue }, wrapChildren(props, {
|
|
38
|
+
hasVarDec,
|
|
39
|
+
varContext: varContextRef.current,
|
|
40
|
+
textStyle,
|
|
41
|
+
textProps
|
|
42
|
+
}))));
|
|
43
|
+
});
|
|
44
|
+
_StickySection.displayName = 'MpxStickySection';
|
|
45
|
+
export default _StickySection;
|
|
@@ -2,7 +2,7 @@ import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated
|
|
|
2
2
|
import { forwardRef, useRef, useContext, createElement } from 'react';
|
|
3
3
|
import useInnerProps from './getInnerListeners';
|
|
4
4
|
import useNodesRef from './useNodesRef'; // 引入辅助函数
|
|
5
|
-
import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout, extendObject } from './utils';
|
|
5
|
+
import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout, extendObject, isHarmony } from './utils';
|
|
6
6
|
import { SwiperContext } from './context';
|
|
7
7
|
const _SwiperItem = forwardRef((props, ref) => {
|
|
8
8
|
const { 'enable-var': enableVar, 'external-var-context': externalVarContext, style, customStyle, itemIndex } = props;
|
|
@@ -29,7 +29,7 @@ const _SwiperItem = forwardRef((props, ref) => {
|
|
|
29
29
|
'style'
|
|
30
30
|
], { layoutRef });
|
|
31
31
|
const itemAnimatedStyle = useAnimatedStyle(() => {
|
|
32
|
-
if (!step.value)
|
|
32
|
+
if (!step.value && !isHarmony)
|
|
33
33
|
return {};
|
|
34
34
|
const inputRange = [step.value, 0];
|
|
35
35
|
const outputRange = [0.7, 1];
|