@tarojs/components-advanced 4.1.12-beta.7 → 4.1.12-beta.70
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/dist/components/index.js +2 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/list/hooks/useItemSizeCache.d.ts +1 -1
- package/dist/components/list/hooks/useItemSizeCache.js +5 -5
- package/dist/components/list/hooks/useItemSizeCache.js.map +1 -1
- package/dist/components/list/hooks/useListNestedScroll.d.ts +18 -0
- package/dist/components/list/hooks/useListNestedScroll.js +61 -0
- package/dist/components/list/hooks/useListNestedScroll.js.map +1 -0
- package/dist/components/list/hooks/useListScrollElementAttach.d.ts +25 -0
- package/dist/components/list/hooks/useListScrollElementAttach.js +88 -0
- package/dist/components/list/hooks/useListScrollElementAttach.js.map +1 -0
- package/dist/components/list/hooks/useListScrollElementAttachWeapp.d.ts +15 -0
- package/dist/components/list/hooks/useListScrollElementAttachWeapp.js +135 -0
- package/dist/components/list/hooks/useListScrollElementAttachWeapp.js.map +1 -0
- package/dist/components/list/hooks/useMeasureStartOffset.d.ts +4 -2
- package/dist/components/list/hooks/useMeasureStartOffset.js +56 -15
- package/dist/components/list/hooks/useMeasureStartOffset.js.map +1 -1
- package/dist/components/list/hooks/useMeasureStartOffsetWeapp.d.ts +14 -0
- package/dist/components/list/hooks/useMeasureStartOffsetWeapp.js +87 -0
- package/dist/components/list/hooks/useMeasureStartOffsetWeapp.js.map +1 -0
- package/dist/components/list/hooks/useRefresher.d.ts +8 -2
- package/dist/components/list/hooks/useRefresher.js +125 -67
- package/dist/components/list/hooks/useRefresher.js.map +1 -1
- package/dist/components/list/hooks/useResizeObserver.d.ts +4 -1
- package/dist/components/list/hooks/useResizeObserver.js +8 -7
- package/dist/components/list/hooks/useResizeObserver.js.map +1 -1
- package/dist/components/list/hooks/useScrollCorrection.js +2 -1
- package/dist/components/list/hooks/useScrollCorrection.js.map +1 -1
- package/dist/components/list/hooks/useScrollParentAutoFind.d.ts +20 -0
- package/dist/components/list/hooks/useScrollParentAutoFind.js +81 -0
- package/dist/components/list/hooks/useScrollParentAutoFind.js.map +1 -0
- package/dist/components/list/index.d.ts +12 -17
- package/dist/components/list/index.js +266 -286
- package/dist/components/list/index.js.map +1 -1
- package/dist/components/list/utils.d.ts +12 -0
- package/dist/components/list/utils.js +23 -1
- package/dist/components/list/utils.js.map +1 -1
- package/dist/components/virtual-list/vue/list.d.ts +10 -10
- package/dist/components/virtual-waterfall/vue/waterfall.d.ts +10 -10
- package/dist/components/water-flow/feed-column.d.ts +2 -0
- package/dist/components/water-flow/feed-column.js +10 -0
- package/dist/components/water-flow/feed-column.js.map +1 -0
- package/dist/components/water-flow/flow-item.js +18 -11
- package/dist/components/water-flow/flow-item.js.map +1 -1
- package/dist/components/water-flow/flow-section.js +1 -1
- package/dist/components/water-flow/flow-section.js.map +1 -1
- package/dist/components/water-flow/index.d.ts +1 -1
- package/dist/components/water-flow/interface.d.ts +31 -4
- package/dist/components/water-flow/node.d.ts +3 -0
- package/dist/components/water-flow/node.js +34 -1
- package/dist/components/water-flow/node.js.map +1 -1
- package/dist/components/water-flow/root.d.ts +36 -3
- package/dist/components/water-flow/root.js +143 -44
- package/dist/components/water-flow/root.js.map +1 -1
- package/dist/components/water-flow/section.d.ts +23 -1
- package/dist/components/water-flow/section.js +120 -20
- package/dist/components/water-flow/section.js.map +1 -1
- package/dist/components/water-flow/utils.d.ts +2 -0
- package/dist/components/water-flow/utils.js +3 -1
- package/dist/components/water-flow/utils.js.map +1 -1
- package/dist/components/water-flow/water-flow-node-cache.d.ts +24 -0
- package/dist/components/water-flow/water-flow-node-cache.js +161 -0
- package/dist/components/water-flow/water-flow-node-cache.js.map +1 -0
- package/dist/components/water-flow/water-flow.d.ts +2 -3
- package/dist/components/water-flow/water-flow.js +257 -77
- package/dist/components/water-flow/water-flow.js.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/dom.d.ts +2 -2
- package/dist/utils/dom.js +7 -6
- package/dist/utils/dom.js.map +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/scrollElementContext.d.ts +15 -0
- package/dist/utils/scrollElementContext.js +14 -0
- package/dist/utils/scrollElementContext.js.map +1 -0
- package/dist/utils/scrollParent.d.ts +33 -0
- package/dist/utils/scrollParent.js +88 -0
- package/dist/utils/scrollParent.js.map +1 -0
- package/dist/utils/weapp-scope.d.ts +7 -0
- package/dist/utils/weapp-scope.js +20 -0
- package/dist/utils/weapp-scope.js.map +1 -0
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMeasureStartOffset.js","sources":["../../../../src/components/list/hooks/useMeasureStartOffset.ts"],"sourcesContent":["import { useEffect, useState } from 'react'\n\nimport type { RefObject } from 'react'\n\n/**\n * 测量 scrollElement 内 content 节点之前所有兄弟的高度/宽度之和,作为 startOffset。\n * 用于 scrollElement 模式下无 Context
|
|
1
|
+
{"version":3,"file":"useMeasureStartOffset.js","sources":["../../../../src/components/list/hooks/useMeasureStartOffset.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\n\nimport type { RefObject } from 'react'\n\nconst WARNED_NESTED = new WeakSet<Element>()\nconst MAX_RETRY = 20\nconst RETRY_DELAY = 50\n\n/**\n * 测量 scrollElement 内 content 节点之前所有兄弟的高度/宽度之和,作为 startOffset。\n * 用于 scrollElement 模式下无 Context/props 时自动计算上方内容高度。\n * - 直接子节点:累加前兄弟尺寸(更稳定)\n * - 非直接子节点:getBoundingClientRect 回退,并 warning 建议传 startOffset\n * - ref 未就绪时自动重试,确保绑定到最外层滚动容器后能正确测量\n */\nexport function useMeasureStartOffset(\n scrollElRef: RefObject<HTMLElement | null>,\n contentRef: RefObject<HTMLElement | null>,\n options: { enabled: boolean, isHorizontal?: boolean }\n): number {\n const { enabled, isHorizontal = false } = options\n const [measuredStartOffset, setMeasuredStartOffset] = useState(0)\n const [retryTrigger, setRetryTrigger] = useState(0)\n const retryCountRef = useRef(0)\n\n useEffect(() => {\n if (!enabled) {\n retryCountRef.current = 0\n return\n }\n const scrollEl = scrollElRef.current\n const contentEl = contentRef.current\n\n // ref 未就绪时重试(如 ScrollView 与 WaterFlow 异步挂载)\n if (!scrollEl || !contentEl) {\n if (retryCountRef.current < MAX_RETRY) {\n retryCountRef.current += 1\n const id = setTimeout(() => setRetryTrigger((t) => t + 1), RETRY_DELAY)\n return () => clearTimeout(id)\n }\n return\n }\n retryCountRef.current = 0\n\n const measure = () => {\n const el = contentRef.current\n const scroll = scrollElRef.current\n if (!el || !scroll) return\n\n if (el.parentElement === scroll) {\n // 直接子节点:累加前兄弟尺寸\n let offset = 0\n let prev: Element | null = el.previousElementSibling\n while (prev) {\n offset += isHorizontal ? (prev as HTMLElement).offsetWidth : (prev as HTMLElement).offsetHeight\n prev = prev.previousElementSibling\n }\n setMeasuredStartOffset((prevVal) => (Math.abs(prevVal - offset) < 1 ? prevVal : offset))\n return\n }\n\n // 非直接子节点:getBoundingClientRect 回退\n const scrollRect = scroll.getBoundingClientRect()\n const contentRect = el.getBoundingClientRect()\n const offset = isHorizontal\n ? contentRect.left - scrollRect.left + scroll.scrollLeft\n : contentRect.top - scrollRect.top + scroll.scrollTop\n const value = Math.max(0, offset)\n setMeasuredStartOffset((prevVal) => (Math.abs(prevVal - value) < 1 ? prevVal : value))\n\n if (!WARNED_NESTED.has(el)) {\n WARNED_NESTED.add(el)\n // eslint-disable-next-line no-console\n console.warn(\n '[useMeasureStartOffset] content 不是 scrollElement 的直接子节点,使用 getBoundingClientRect 测量。' +\n '建议通过 props 或 Context 传入 startOffset 以获得更稳定的布局。'\n )\n }\n }\n\n measure()\n scrollEl.addEventListener('scroll', measure, { passive: true })\n const ro = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(measure) : null\n if (ro) {\n ro.observe(scrollEl)\n Array.from(scrollEl.children).forEach((child) => ro.observe(child))\n // 非直接子节点:content 自身或其祖先变化时也需重测\n ro.observe(contentEl)\n }\n return () => {\n scrollEl.removeEventListener('scroll', measure)\n ro?.disconnect()\n }\n }, [enabled, scrollElRef, contentRef, isHorizontal, retryTrigger])\n\n return measuredStartOffset\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MutableRefObject, RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* 小程序版 startOffset:对外层 scroll-view 与带 `contentId` 的外包各执行一次 SelectorQuery(各自 `weappScope(ref)`),
|
|
4
|
+
* 再合并结果;避免二者不在同一自定义组件时,单次 `in` 链式 `select` 查不全。
|
|
5
|
+
*
|
|
6
|
+
* 公式:startOffset = contentRect.top - scrollRect.top + scrollTop
|
|
7
|
+
*/
|
|
8
|
+
export declare function useMeasureStartOffsetWeapp(scrollElRef: RefObject<HTMLElement | null>, contentId: string, options: {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
isHorizontal?: boolean;
|
|
11
|
+
startOffsetRef?: MutableRefObject<number>;
|
|
12
|
+
/** 挂载 `id={contentId}` 的嵌套内容外包 View;第二次 query 用其 ref 取 content 侧 `_scope` */
|
|
13
|
+
contentWrapperRef: RefObject<HTMLElement | null>;
|
|
14
|
+
}): number;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useState, useRef, useEffect } from 'react';
|
|
2
|
+
import { createSelectorQueryForRef } from '../utils.js';
|
|
3
|
+
|
|
4
|
+
const MAX_RETRY = 20;
|
|
5
|
+
const RETRY_DELAY = 50;
|
|
6
|
+
/** 与 useListScrollElementAttachWeapp 容器测量间隔一致(H5 无 interval,用 ResizeObserver+scroll 事件驱动) */
|
|
7
|
+
const MEASURE_INTERVAL = 150;
|
|
8
|
+
/**
|
|
9
|
+
* 小程序版 startOffset:对外层 scroll-view 与带 `contentId` 的外包各执行一次 SelectorQuery(各自 `weappScope(ref)`),
|
|
10
|
+
* 再合并结果;避免二者不在同一自定义组件时,单次 `in` 链式 `select` 查不全。
|
|
11
|
+
*
|
|
12
|
+
* 公式:startOffset = contentRect.top - scrollRect.top + scrollTop
|
|
13
|
+
*/
|
|
14
|
+
function useMeasureStartOffsetWeapp(scrollElRef, contentId, options) {
|
|
15
|
+
const { enabled, isHorizontal = false, startOffsetRef, contentWrapperRef } = options;
|
|
16
|
+
const [measuredStartOffset, setMeasuredStartOffset] = useState(0);
|
|
17
|
+
const [retryTrigger, setRetryTrigger] = useState(0);
|
|
18
|
+
const retryCountRef = useRef(0);
|
|
19
|
+
const autoIdRef = useRef(`_ls_so_${Math.random().toString(36).slice(2, 9)}`);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!enabled || !contentId) {
|
|
22
|
+
retryCountRef.current = 0;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const applyRects = (scrollRect, scrollInfo, contentRect) => {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
if (!scrollRect || !scrollInfo || !contentRect)
|
|
28
|
+
return;
|
|
29
|
+
const scrollTop = (_a = scrollInfo.scrollTop) !== null && _a !== void 0 ? _a : 0;
|
|
30
|
+
const scrollLeft = (_b = scrollInfo.scrollLeft) !== null && _b !== void 0 ? _b : 0;
|
|
31
|
+
const value = isHorizontal
|
|
32
|
+
? Math.max(0, contentRect.left - scrollRect.left + scrollLeft)
|
|
33
|
+
: Math.max(0, contentRect.top - scrollRect.top + scrollTop);
|
|
34
|
+
if (startOffsetRef)
|
|
35
|
+
startOffsetRef.current = value;
|
|
36
|
+
setMeasuredStartOffset((prevVal) => (Math.abs(prevVal - value) < 1 ? prevVal : value));
|
|
37
|
+
};
|
|
38
|
+
const measure = () => {
|
|
39
|
+
const scrollEl = scrollElRef.current;
|
|
40
|
+
if (!scrollEl)
|
|
41
|
+
return;
|
|
42
|
+
if (!scrollEl.id) {
|
|
43
|
+
scrollEl.id = autoIdRef.current;
|
|
44
|
+
}
|
|
45
|
+
const scrollViewId = scrollEl.id;
|
|
46
|
+
createSelectorQueryForRef(scrollElRef)
|
|
47
|
+
.select(`#${scrollViewId}`)
|
|
48
|
+
.boundingClientRect()
|
|
49
|
+
.select(`#${scrollViewId}`)
|
|
50
|
+
.scrollOffset()
|
|
51
|
+
.exec((scrollRes) => {
|
|
52
|
+
const scrollRect = scrollRes === null || scrollRes === void 0 ? void 0 : scrollRes[0];
|
|
53
|
+
const scrollInfo = scrollRes === null || scrollRes === void 0 ? void 0 : scrollRes[1];
|
|
54
|
+
if (!scrollRect || !scrollInfo)
|
|
55
|
+
return;
|
|
56
|
+
createSelectorQueryForRef(contentWrapperRef)
|
|
57
|
+
.select(`#${contentId}`)
|
|
58
|
+
.boundingClientRect()
|
|
59
|
+
.exec((contentRes) => {
|
|
60
|
+
const contentRect = contentRes === null || contentRes === void 0 ? void 0 : contentRes[0];
|
|
61
|
+
applyRects(scrollRect, scrollInfo, contentRect);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
const scrollEl = scrollElRef.current;
|
|
66
|
+
if (!scrollEl) {
|
|
67
|
+
if (retryCountRef.current < MAX_RETRY) {
|
|
68
|
+
retryCountRef.current += 1;
|
|
69
|
+
const id = setTimeout(() => setRetryTrigger((t) => t + 1), RETRY_DELAY);
|
|
70
|
+
return () => clearTimeout(id);
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
retryCountRef.current = 0;
|
|
75
|
+
measure();
|
|
76
|
+
scrollEl.addEventListener('scroll', measure);
|
|
77
|
+
const interval = setInterval(measure, MEASURE_INTERVAL);
|
|
78
|
+
return () => {
|
|
79
|
+
scrollEl.removeEventListener('scroll', measure);
|
|
80
|
+
clearInterval(interval);
|
|
81
|
+
};
|
|
82
|
+
}, [enabled, scrollElRef, contentId, contentWrapperRef, isHorizontal, retryTrigger]);
|
|
83
|
+
return measuredStartOffset;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { useMeasureStartOffsetWeapp };
|
|
87
|
+
//# sourceMappingURL=useMeasureStartOffsetWeapp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMeasureStartOffsetWeapp.js","sources":["../../../../src/components/list/hooks/useMeasureStartOffsetWeapp.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\n\nimport { createSelectorQueryForRef } from '../utils'\n\nimport type { MutableRefObject, RefObject } from 'react'\n\nconst MAX_RETRY = 20\nconst RETRY_DELAY = 50\n/** 与 useListScrollElementAttachWeapp 容器测量间隔一致(H5 无 interval,用 ResizeObserver+scroll 事件驱动) */\nconst MEASURE_INTERVAL = 150\n\n/**\n * 小程序版 startOffset:对外层 scroll-view 与带 `contentId` 的外包各执行一次 SelectorQuery(各自 `weappScope(ref)`),\n * 再合并结果;避免二者不在同一自定义组件时,单次 `in` 链式 `select` 查不全。\n *\n * 公式:startOffset = contentRect.top - scrollRect.top + scrollTop\n */\nexport function useMeasureStartOffsetWeapp(\n scrollElRef: RefObject<HTMLElement | null>,\n contentId: string,\n options: {\n enabled: boolean\n isHorizontal?: boolean\n startOffsetRef?: MutableRefObject<number>\n /** 挂载 `id={contentId}` 的嵌套内容外包 View;第二次 query 用其 ref 取 content 侧 `_scope` */\n contentWrapperRef: RefObject<HTMLElement | null>\n }\n): number {\n const { enabled, isHorizontal = false, startOffsetRef, contentWrapperRef } = options\n const [measuredStartOffset, setMeasuredStartOffset] = useState(0)\n const [retryTrigger, setRetryTrigger] = useState(0)\n const retryCountRef = useRef(0)\n const autoIdRef = useRef(`_ls_so_${Math.random().toString(36).slice(2, 9)}`)\n\n useEffect(() => {\n if (!enabled || !contentId) {\n retryCountRef.current = 0\n return\n }\n\n const applyRects = (scrollRect: any, scrollInfo: any, contentRect: any) => {\n if (!scrollRect || !scrollInfo || !contentRect) return\n const scrollTop = scrollInfo.scrollTop ?? 0\n const scrollLeft = scrollInfo.scrollLeft ?? 0\n const value = isHorizontal\n ? Math.max(0, contentRect.left - scrollRect.left + scrollLeft)\n : Math.max(0, contentRect.top - scrollRect.top + scrollTop)\n if (startOffsetRef) startOffsetRef.current = value\n setMeasuredStartOffset((prevVal) => (Math.abs(prevVal - value) < 1 ? prevVal : value))\n }\n\n const measure = () => {\n const scrollEl = scrollElRef.current as any\n if (!scrollEl) return\n\n if (!scrollEl.id) {\n scrollEl.id = autoIdRef.current\n }\n const scrollViewId = scrollEl.id\n\n createSelectorQueryForRef(scrollElRef)\n .select(`#${scrollViewId}`)\n .boundingClientRect()\n .select(`#${scrollViewId}`)\n .scrollOffset()\n .exec((scrollRes) => {\n const scrollRect = scrollRes?.[0]\n const scrollInfo = scrollRes?.[1]\n if (!scrollRect || !scrollInfo) return\n createSelectorQueryForRef(contentWrapperRef)\n .select(`#${contentId}`)\n .boundingClientRect()\n .exec((contentRes) => {\n const contentRect = contentRes?.[0]\n applyRects(scrollRect, scrollInfo, contentRect)\n })\n })\n }\n\n const scrollEl = scrollElRef.current\n if (!scrollEl) {\n if (retryCountRef.current < MAX_RETRY) {\n retryCountRef.current += 1\n const id = setTimeout(() => setRetryTrigger((t) => t + 1), RETRY_DELAY)\n return () => clearTimeout(id)\n }\n return\n }\n retryCountRef.current = 0\n measure()\n scrollEl.addEventListener('scroll', measure)\n const interval = setInterval(measure, MEASURE_INTERVAL)\n return () => {\n scrollEl.removeEventListener('scroll', measure)\n clearInterval(interval)\n }\n }, [enabled, scrollElRef, contentId, contentWrapperRef, isHorizontal, retryTrigger])\n\n return measuredStartOffset\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -27,7 +27,6 @@ export interface ListRefresherConfig {
|
|
|
27
27
|
};
|
|
28
28
|
}) => void;
|
|
29
29
|
}
|
|
30
|
-
/** 默认刷新层高度(对齐 Dynamic),无自定义 children 时使用 */
|
|
31
30
|
export declare const DEFAULT_REFRESHER_HEIGHT = 50;
|
|
32
31
|
/** 下拉刷新状态枚举(对齐微信小程序 RefreshStatus) */
|
|
33
32
|
export declare const enum RefreshStatus {
|
|
@@ -65,10 +64,17 @@ interface UseRefresherReturn {
|
|
|
65
64
|
};
|
|
66
65
|
addImperativeTouchListeners?: (el: HTMLElement) => () => void;
|
|
67
66
|
renderRefresherContent: () => React.ReactNode | null;
|
|
67
|
+
/**
|
|
68
|
+
* 刷新区几何高度:H5 等非小程序原生路径下与 List 绝对层 height / translateY 及本 hook 内下拉收尾动画一致。
|
|
69
|
+
* 小程序原生 refresher 由 ScrollView 负责展示与布局,List 侧槽位样式不依赖此字段。
|
|
70
|
+
*/
|
|
71
|
+
slotHeight: number;
|
|
68
72
|
}
|
|
69
73
|
export declare function useRefresher(config: ListRefresherConfig | null,
|
|
70
74
|
/** 列表逻辑顶部对应的 scrollTop,用于触顶判断;H5「顶栏悬浮+只滚列表」时传 0,imperative 内以 DOM scrollTop 为准 */
|
|
71
75
|
scrollTopAtLogicalTop: number,
|
|
72
76
|
/** scrollElement 模式下可选:下拉起始点须在 List 内才触发刷新;未传时默认 true(沿用原逻辑) */
|
|
73
|
-
getIsTouchInListArea?: (ev: TouchEvent) => boolean
|
|
77
|
+
getIsTouchInListArea?: (ev: TouchEvent) => boolean,
|
|
78
|
+
/** H5 等:开启刷新区时是否测量自定义内容高度(与 List 侧 isH5 && refresher 一致) */
|
|
79
|
+
enableH5SlotMeasure?: boolean): UseRefresherReturn;
|
|
74
80
|
export {};
|
|
@@ -1,41 +1,65 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { Slot, View } from '@tarojs/components';
|
|
3
|
-
import React, { useState, useRef, useCallback } from 'react';
|
|
3
|
+
import React, { useState, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
|
|
4
4
|
import { supportsNativeRefresher } from '../utils.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* 下拉刷新(List 内部)
|
|
8
8
|
*
|
|
9
|
-
* - 小程序:
|
|
10
|
-
* - H5
|
|
9
|
+
* - 小程序:ScrollView 原生 refresher-*;业务 Promise reject 时补发 Failed(4)(原生不派发)
|
|
10
|
+
* - H5:触顶 + touch 拖拽;最大行程 / 阻尼停拉线为视口高的 0.9 / 0.8(innerHeight);收尾仅依赖 onRefresherRefresh 的 Promise
|
|
11
|
+
* - 动画时间戳用 nowMs():小程序部分环境无 performance,避免抛错中断回弹
|
|
12
|
+
* - rafRef 与受控 refresherTriggered→false 的 effect 共用:effect 内 cancel 可能打断 H5 成功回弹,须在 effect 动画结束处释放 refreshingLockRef
|
|
13
|
+
* - H5 reject:onRefresherStatusChange 顺序 Failed(4) → Completed(3) → Idle(0)
|
|
14
|
+
* - emitStatusChange:仅 status 变化时回调(同 status 不重复)
|
|
11
15
|
*/
|
|
12
16
|
const BOUNCE_MS = 300;
|
|
13
17
|
const AT_TOP_THRESHOLD = 3;
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const DAMPING = 100;
|
|
18
|
-
/** 角度上限(度),小于此值才视为下拉意图 */
|
|
18
|
+
const H5_PULL_DISTANCE_MAX_VH = 0.9;
|
|
19
|
+
const H5_DAMPING_LIMIT_VH = 0.8;
|
|
20
|
+
const VIEWPORT_HEIGHT_FALLBACK = 800;
|
|
19
21
|
const DEG_LIMIT = 40;
|
|
20
|
-
/** 默认刷新层高度(对齐 Dynamic),无自定义 children 时使用 */
|
|
21
22
|
const DEFAULT_REFRESHER_HEIGHT = 50;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
function resolveTaroMeasureElement(ref) {
|
|
24
|
+
var _a;
|
|
25
|
+
const n = ref.current;
|
|
26
|
+
if (!n)
|
|
27
|
+
return null;
|
|
28
|
+
if (typeof Element !== 'undefined' && n instanceof Element)
|
|
29
|
+
return n;
|
|
30
|
+
const el = (_a = n.$el) !== null && _a !== void 0 ? _a : n;
|
|
31
|
+
if (el && typeof Element !== 'undefined' && el instanceof Element)
|
|
32
|
+
return el;
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function nowMs() {
|
|
36
|
+
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
|
|
37
|
+
return performance.now();
|
|
38
|
+
}
|
|
39
|
+
return Date.now();
|
|
40
|
+
}
|
|
41
|
+
function getViewportHeightPx() {
|
|
42
|
+
if (typeof window === 'undefined' || !Number.isFinite(window.innerHeight) || window.innerHeight <= 0) {
|
|
43
|
+
return VIEWPORT_HEIGHT_FALLBACK;
|
|
44
|
+
}
|
|
45
|
+
return window.innerHeight;
|
|
46
|
+
}
|
|
47
|
+
/** 阻尼增量:ratio=总下拉位移/视口高;超过 dampingLimit 后不再累加 */
|
|
48
|
+
function dampIncrement(diff, totalMove, currentPull, viewportHeight, dampingLimit) {
|
|
27
49
|
if (diff <= 0)
|
|
28
50
|
return 0;
|
|
29
|
-
if (currentPull >=
|
|
51
|
+
if (currentPull >= dampingLimit)
|
|
30
52
|
return 0;
|
|
31
|
-
const ratio = Math.min(totalMove /
|
|
53
|
+
const ratio = Math.min(totalMove / viewportHeight, 1);
|
|
32
54
|
return diff * (1 - ratio) * 0.6;
|
|
33
55
|
}
|
|
34
56
|
function useRefresher(config,
|
|
35
57
|
/** 列表逻辑顶部对应的 scrollTop,用于触顶判断;H5「顶栏悬浮+只滚列表」时传 0,imperative 内以 DOM scrollTop 为准 */
|
|
36
58
|
scrollTopAtLogicalTop,
|
|
37
59
|
/** scrollElement 模式下可选:下拉起始点须在 List 内才触发刷新;未传时默认 true(沿用原逻辑) */
|
|
38
|
-
getIsTouchInListArea
|
|
60
|
+
getIsTouchInListArea,
|
|
61
|
+
/** H5 等:开启刷新区时是否测量自定义内容高度(与 List 侧 isH5 && refresher 一致) */
|
|
62
|
+
enableH5SlotMeasure) {
|
|
39
63
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
40
64
|
const [internalRefreshing, setInternalRefreshing] = useState(false);
|
|
41
65
|
const [pullDistance, setPullDistance] = useState(0);
|
|
@@ -61,7 +85,6 @@ getIsTouchInListArea) {
|
|
|
61
85
|
thresholdRef.current = (_c = config === null || config === void 0 ? void 0 : config.refresherThreshold) !== null && _c !== void 0 ? _c : 45;
|
|
62
86
|
const configRef = useRef(config);
|
|
63
87
|
configRef.current = config;
|
|
64
|
-
/** H5:触发 onRefresherStatusChange 回调(仅状态变化时触发) */
|
|
65
88
|
const emitStatusChangeRef = useRef((status, dy) => {
|
|
66
89
|
var _a, _b;
|
|
67
90
|
if (refreshStatusRef.current !== status) {
|
|
@@ -78,9 +101,50 @@ getIsTouchInListArea) {
|
|
|
78
101
|
topThresholdPxRef.current = topThresholdPx;
|
|
79
102
|
const getIsTouchInListAreaRef = useRef(getIsTouchInListArea);
|
|
80
103
|
getIsTouchInListAreaRef.current = getIsTouchInListArea;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
104
|
+
const [measuredSlotHeight, setMeasuredSlotHeight] = useState(0);
|
|
105
|
+
const h5MeasureRef = useRef(null);
|
|
106
|
+
const setH5MeasureRef = useCallback((el) => {
|
|
107
|
+
h5MeasureRef.current = el;
|
|
108
|
+
}, []);
|
|
109
|
+
const hasCustomRefresherChildren = (config === null || config === void 0 ? void 0 : config.children) != null;
|
|
110
|
+
const slotHeight = useMemo(() => {
|
|
111
|
+
if (!config || config.refresherEnabled === false)
|
|
112
|
+
return DEFAULT_REFRESHER_HEIGHT;
|
|
113
|
+
if (!hasCustomRefresherChildren)
|
|
114
|
+
return DEFAULT_REFRESHER_HEIGHT;
|
|
115
|
+
if (supportsNativeRefresher)
|
|
116
|
+
return DEFAULT_REFRESHER_HEIGHT;
|
|
117
|
+
if (!enableH5SlotMeasure)
|
|
118
|
+
return DEFAULT_REFRESHER_HEIGHT;
|
|
119
|
+
return measuredSlotHeight > 0 ? measuredSlotHeight : DEFAULT_REFRESHER_HEIGHT;
|
|
120
|
+
}, [
|
|
121
|
+
config,
|
|
122
|
+
hasCustomRefresherChildren,
|
|
123
|
+
measuredSlotHeight,
|
|
124
|
+
enableH5SlotMeasure,
|
|
125
|
+
]);
|
|
126
|
+
const slotHeightRef = useRef(slotHeight);
|
|
127
|
+
slotHeightRef.current = slotHeight;
|
|
128
|
+
useLayoutEffect(() => {
|
|
129
|
+
if (!enableH5SlotMeasure) {
|
|
130
|
+
setMeasuredSlotHeight(0);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (!hasCustomRefresherChildren)
|
|
134
|
+
return;
|
|
135
|
+
const node = resolveTaroMeasureElement(h5MeasureRef);
|
|
136
|
+
if (!node || typeof ResizeObserver === 'undefined')
|
|
137
|
+
return;
|
|
138
|
+
const ro = new ResizeObserver((entries) => {
|
|
139
|
+
const entry = entries[0];
|
|
140
|
+
if (!entry)
|
|
141
|
+
return;
|
|
142
|
+
const h = Math.max(1, Math.ceil(entry.contentRect.height));
|
|
143
|
+
setMeasuredSlotHeight(prev => (prev === h ? prev : h));
|
|
144
|
+
});
|
|
145
|
+
ro.observe(node);
|
|
146
|
+
return () => ro.disconnect();
|
|
147
|
+
}, [enableH5SlotMeasure, hasCustomRefresherChildren, config === null || config === void 0 ? void 0 : config.children]);
|
|
84
148
|
const scrollViewRefresherProps = config && supportsNativeRefresher ? {
|
|
85
149
|
refresherEnabled: (_d = config.refresherEnabled) !== null && _d !== void 0 ? _d : true,
|
|
86
150
|
refresherThreshold: (_e = config.refresherThreshold) !== null && _e !== void 0 ? _e : 45,
|
|
@@ -94,12 +158,16 @@ getIsTouchInListArea) {
|
|
|
94
158
|
(_a = config.onRefresherPulling) === null || _a === void 0 ? void 0 : _a.call(config, { detail: { deltaY: (_c = (_b = e.detail) === null || _b === void 0 ? void 0 : _b.deltaY) !== null && _c !== void 0 ? _c : 0 } });
|
|
95
159
|
},
|
|
96
160
|
onRefresherRefresh: async () => {
|
|
97
|
-
var _a;
|
|
161
|
+
var _a, _b;
|
|
98
162
|
if (!isControlled)
|
|
99
163
|
setInternalRefreshing(true);
|
|
100
164
|
try {
|
|
101
165
|
await ((_a = config.onRefresherRefresh) === null || _a === void 0 ? void 0 : _a.call(config));
|
|
102
166
|
}
|
|
167
|
+
catch (_c) {
|
|
168
|
+
// 原生不派发 Failed(4),补发便于与 H5 一致
|
|
169
|
+
(_b = config.onRefresherStatusChange) === null || _b === void 0 ? void 0 : _b.call(config, { detail: { status: 4 /* RefreshStatus.Failed */, dy: 0 } });
|
|
170
|
+
}
|
|
103
171
|
finally {
|
|
104
172
|
if (!isControlled)
|
|
105
173
|
setInternalRefreshing(false);
|
|
@@ -115,11 +183,7 @@ getIsTouchInListArea) {
|
|
|
115
183
|
(_a = config.onRefresherStatusChange) === null || _a === void 0 ? void 0 : _a.call(config, { detail: { status: (_b = e.detail) === null || _b === void 0 ? void 0 : _b.status, dy: (_c = e.detail) === null || _c === void 0 ? void 0 : _c.dy } });
|
|
116
184
|
},
|
|
117
185
|
} : {};
|
|
118
|
-
// ========================================
|
|
119
|
-
// H5:单一结构 + 原生 touch 监听(passive: false);refresherEnabled=false 时不挂
|
|
120
|
-
// ========================================
|
|
121
186
|
const addImperativeTouchListeners = React.useCallback((el) => {
|
|
122
|
-
var _a, _b;
|
|
123
187
|
if (supportsNativeRefresher || !configRef.current || !h5RefresherEnabled)
|
|
124
188
|
return () => { };
|
|
125
189
|
const scrollEl = el;
|
|
@@ -134,7 +198,6 @@ getIsTouchInListArea) {
|
|
|
134
198
|
/** 用于 onTouchEnd 判断是否触发刷新;刷新完成后必须置 0,否则下次点击(无 touchMove)会误用上次的 lastPull 再次触发 */
|
|
135
199
|
const lastPullRef = { current: 0 };
|
|
136
200
|
const pullAtReleaseRef = { current: 0 };
|
|
137
|
-
const screenHeight = typeof window !== 'undefined' ? (_b = (_a = window.screen) === null || _a === void 0 ? void 0 : _a.height) !== null && _b !== void 0 ? _b : 600 : 600;
|
|
138
201
|
const setPull = (v) => {
|
|
139
202
|
lastPull = v;
|
|
140
203
|
lastPullRef.current = v;
|
|
@@ -143,9 +206,9 @@ getIsTouchInListArea) {
|
|
|
143
206
|
const runBounceBack = (fromValue, onComplete) => {
|
|
144
207
|
if (rafRef.current != null)
|
|
145
208
|
cancelAnimationFrame(rafRef.current);
|
|
146
|
-
const startTime =
|
|
209
|
+
const startTime = nowMs();
|
|
147
210
|
const animate = () => {
|
|
148
|
-
const t = Math.min((
|
|
211
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
149
212
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
150
213
|
const v = fromValue * (1 - ease);
|
|
151
214
|
setPullDistanceRef.current(v);
|
|
@@ -156,7 +219,6 @@ getIsTouchInListArea) {
|
|
|
156
219
|
}
|
|
157
220
|
else {
|
|
158
221
|
setPullDistanceRef.current(0);
|
|
159
|
-
// 修复闭包问题:使用 ref 获取最新值
|
|
160
222
|
if (!isControlledRef.current)
|
|
161
223
|
setInternalRefreshingRef.current(false);
|
|
162
224
|
rafRef.current = null;
|
|
@@ -169,10 +231,10 @@ getIsTouchInListArea) {
|
|
|
169
231
|
const runBounceBackAbort = (fromValue) => {
|
|
170
232
|
if (rafRef.current != null)
|
|
171
233
|
cancelAnimationFrame(rafRef.current);
|
|
172
|
-
const startTime =
|
|
234
|
+
const startTime = nowMs();
|
|
173
235
|
const animate = () => {
|
|
174
236
|
var _a, _b;
|
|
175
|
-
const t = Math.min((
|
|
237
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
176
238
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
177
239
|
const v = fromValue * (1 - ease);
|
|
178
240
|
setPullDistanceRef.current(v);
|
|
@@ -186,19 +248,18 @@ getIsTouchInListArea) {
|
|
|
186
248
|
lastPullRef.current = 0;
|
|
187
249
|
lastPull = 0;
|
|
188
250
|
(_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherAbort) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
189
|
-
// 状态变化:回到 Idle
|
|
190
251
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
191
252
|
}
|
|
192
253
|
};
|
|
193
254
|
rafRef.current = requestAnimationFrame(animate);
|
|
194
255
|
};
|
|
195
|
-
/**
|
|
256
|
+
/** 收起到加载高度后再执行 onRefresherRefresh */
|
|
196
257
|
const runBounceToLoading = (fromValue, toValue, onReach) => {
|
|
197
258
|
if (rafRef.current != null)
|
|
198
259
|
cancelAnimationFrame(rafRef.current);
|
|
199
|
-
const startTime =
|
|
260
|
+
const startTime = nowMs();
|
|
200
261
|
const animate = () => {
|
|
201
|
-
const t = Math.min((
|
|
262
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
202
263
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
203
264
|
setPullDistanceRef.current(fromValue + (toValue - fromValue) * ease);
|
|
204
265
|
if (t < 1) {
|
|
@@ -292,8 +353,11 @@ getIsTouchInListArea) {
|
|
|
292
353
|
return;
|
|
293
354
|
}
|
|
294
355
|
lastY.current = currentY;
|
|
295
|
-
const
|
|
296
|
-
const
|
|
356
|
+
const vh = getViewportHeightPx();
|
|
357
|
+
const maxPull = vh * H5_PULL_DISTANCE_MAX_VH;
|
|
358
|
+
const dampingLimit = vh * H5_DAMPING_LIMIT_VH;
|
|
359
|
+
const damped = dampIncrement(dy, totalMove, lastPull, vh, dampingLimit);
|
|
360
|
+
const next = Math.min(lastPull + damped, maxPull);
|
|
297
361
|
setPull(next);
|
|
298
362
|
(_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherPulling) === null || _b === void 0 ? void 0 : _b.call(_a, { detail: { deltaY: next } });
|
|
299
363
|
if (next >= thresholdRef.current) {
|
|
@@ -310,11 +374,10 @@ getIsTouchInListArea) {
|
|
|
310
374
|
return;
|
|
311
375
|
refreshingLockRef.current = true;
|
|
312
376
|
const pullValue = lastPull;
|
|
313
|
-
const height =
|
|
377
|
+
const height = slotHeightRef.current;
|
|
314
378
|
pullAtReleaseRef.current = pullValue;
|
|
315
379
|
if (!isControlledRef.current)
|
|
316
380
|
setInternalRefreshingRef.current(true);
|
|
317
|
-
// 状态变化:Refreshing
|
|
318
381
|
emitStatusChangeRef.current(2 /* RefreshStatus.Refreshing */, pullValue);
|
|
319
382
|
runBounceToLoading(pullValue, height, () => {
|
|
320
383
|
var _a, _b;
|
|
@@ -328,30 +391,26 @@ getIsTouchInListArea) {
|
|
|
328
391
|
lastPull = 0;
|
|
329
392
|
pullAtReleaseRef.current = 0;
|
|
330
393
|
(_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherRestore) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
331
|
-
// 状态变化:回到 Idle
|
|
332
394
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
333
395
|
};
|
|
334
|
-
const timeoutId = setTimeout(safeReset, BOUNCE_MS + 400);
|
|
335
396
|
Promise.resolve((_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherRefresh) === null || _b === void 0 ? void 0 : _b.call(_a))
|
|
336
397
|
.then(() => {
|
|
337
|
-
// 若已经通过 safeReset 提前结束刷新(例如超时),则不再执行回弹动画,避免二次「吐舌头」闪现
|
|
338
398
|
if (!refreshingLockRef.current)
|
|
339
399
|
return;
|
|
340
|
-
|
|
341
|
-
emitStatusChangeRef.current(3 /* RefreshStatus.Completed */,
|
|
342
|
-
requestAnimationFrame(() => runBounceBack(
|
|
400
|
+
const slotH = slotHeightRef.current;
|
|
401
|
+
emitStatusChangeRef.current(3 /* RefreshStatus.Completed */, slotH);
|
|
402
|
+
requestAnimationFrame(() => runBounceBack(slotH, () => {
|
|
343
403
|
var _a, _b;
|
|
344
|
-
clearTimeout(timeoutId);
|
|
345
404
|
refreshingLockRef.current = false;
|
|
346
405
|
lastPullRef.current = 0;
|
|
347
406
|
lastPull = 0;
|
|
348
407
|
pullAtReleaseRef.current = 0;
|
|
349
408
|
(_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherRestore) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
350
|
-
// 状态变化:回到 Idle
|
|
351
409
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
352
410
|
}));
|
|
353
411
|
}, () => {
|
|
354
|
-
|
|
412
|
+
emitStatusChangeRef.current(4 /* RefreshStatus.Failed */, pullValue);
|
|
413
|
+
emitStatusChangeRef.current(3 /* RefreshStatus.Completed */, slotHeightRef.current);
|
|
355
414
|
safeReset();
|
|
356
415
|
});
|
|
357
416
|
});
|
|
@@ -423,7 +482,7 @@ getIsTouchInListArea) {
|
|
|
423
482
|
const defaultText = showDefaultText
|
|
424
483
|
? (isRefreshing ? '加载中...' : pullDistance >= threshold ? '释放刷新' : '下拉刷新')
|
|
425
484
|
: null;
|
|
426
|
-
return (jsx(View, { style: {
|
|
485
|
+
return (jsx(View, { ref: !supportsNativeRefresher && hasCustomChildren ? setH5MeasureRef : undefined, style: {
|
|
427
486
|
width: '100%',
|
|
428
487
|
minHeight: hasCustomChildren ? undefined : DEFAULT_REFRESHER_HEIGHT,
|
|
429
488
|
height: hasCustomChildren ? 'auto' : DEFAULT_REFRESHER_HEIGHT,
|
|
@@ -433,19 +492,19 @@ getIsTouchInListArea) {
|
|
|
433
492
|
alignItems: 'center',
|
|
434
493
|
background,
|
|
435
494
|
}, children: hasCustomChildren ? config.children : (defaultText ? jsx(View, { style: textColor ? { color: textColor } : undefined, children: defaultText }) : null) }));
|
|
436
|
-
}, [config, pullDistance, isRefreshing]);
|
|
437
|
-
|
|
495
|
+
}, [config, pullDistance, isRefreshing, setH5MeasureRef]);
|
|
496
|
+
/** 受控 refresherTriggered=true:无下拉也显示加载条 */
|
|
438
497
|
React.useEffect(() => {
|
|
439
498
|
if (!isControlled || !config || config.refresherTriggered !== true)
|
|
440
499
|
return;
|
|
441
|
-
|
|
500
|
+
const h = slotHeightRef.current;
|
|
501
|
+
if (pullDistanceRef.current >= h)
|
|
442
502
|
return;
|
|
443
|
-
setPullDistanceRef.current(
|
|
444
|
-
pullDistanceRef.current =
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
// 受控 refresherTriggered:父组件设为 false 时同步回弹并清零
|
|
503
|
+
setPullDistanceRef.current(h);
|
|
504
|
+
pullDistanceRef.current = h;
|
|
505
|
+
emitStatusChangeRef.current(2 /* RefreshStatus.Refreshing */, h);
|
|
506
|
+
}, [isControlled, config === null || config === void 0 ? void 0 : config.refresherTriggered, slotHeight]);
|
|
507
|
+
/** 受控 refresherTriggered=false:回弹清零;与 touch 共用 rafRef,cancel 时须在此释放 refreshingLockRef */
|
|
449
508
|
const prevTriggeredRef = useRef(config === null || config === void 0 ? void 0 : config.refresherTriggered);
|
|
450
509
|
React.useEffect(() => {
|
|
451
510
|
if (!isControlled || !config || config.refresherTriggered !== false) {
|
|
@@ -457,13 +516,12 @@ getIsTouchInListArea) {
|
|
|
457
516
|
if (prev === true && (pullDistanceRef.current > 0 || isRefreshingRef.current)) {
|
|
458
517
|
if (rafRef.current != null)
|
|
459
518
|
cancelAnimationFrame(rafRef.current);
|
|
460
|
-
const from = pullDistanceRef.current > 0 ? pullDistanceRef.current :
|
|
461
|
-
// 触发 Completed 状态变化
|
|
519
|
+
const from = pullDistanceRef.current > 0 ? pullDistanceRef.current : slotHeightRef.current;
|
|
462
520
|
emitStatusChangeRef.current(3 /* RefreshStatus.Completed */, from);
|
|
463
|
-
const startTime =
|
|
521
|
+
const startTime = nowMs();
|
|
464
522
|
const animate = () => {
|
|
465
523
|
var _a;
|
|
466
|
-
const t = Math.min((
|
|
524
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
467
525
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
468
526
|
const v = from * (1 - ease);
|
|
469
527
|
setPullDistanceRef.current(v);
|
|
@@ -473,18 +531,17 @@ getIsTouchInListArea) {
|
|
|
473
531
|
else {
|
|
474
532
|
setPullDistanceRef.current(0);
|
|
475
533
|
pullDistanceRef.current = 0;
|
|
476
|
-
// 修复闭包问题:使用 ref 获取最新值
|
|
477
534
|
if (!isControlledRef.current)
|
|
478
535
|
setInternalRefreshingRef.current(false);
|
|
479
536
|
rafRef.current = null;
|
|
537
|
+
refreshingLockRef.current = false;
|
|
480
538
|
(_a = config.onRefresherRestore) === null || _a === void 0 ? void 0 : _a.call(config);
|
|
481
|
-
// 触发 Idle 状态变化
|
|
482
539
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
483
540
|
}
|
|
484
541
|
};
|
|
485
542
|
rafRef.current = requestAnimationFrame(animate);
|
|
486
543
|
}
|
|
487
|
-
}, [isControlled, config === null || config === void 0 ? void 0 : config.refresherTriggered]);
|
|
544
|
+
}, [isControlled, config === null || config === void 0 ? void 0 : config.refresherTriggered, slotHeight]);
|
|
488
545
|
return {
|
|
489
546
|
scrollViewRefresherProps,
|
|
490
547
|
scrollViewRefresherHandlers,
|
|
@@ -496,6 +553,7 @@ getIsTouchInListArea) {
|
|
|
496
553
|
},
|
|
497
554
|
addImperativeTouchListeners: !supportsNativeRefresher && config && h5RefresherEnabled ? addImperativeTouchListeners : undefined,
|
|
498
555
|
renderRefresherContent,
|
|
556
|
+
slotHeight,
|
|
499
557
|
};
|
|
500
558
|
}
|
|
501
559
|
|