@tarojs/components-advanced 4.1.12-beta.28 → 4.1.12-beta.32
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/list/hooks/useListNestedScroll.d.ts +1 -1
- package/dist/components/list/hooks/useListNestedScroll.js +2 -2
- package/dist/components/list/hooks/useListNestedScroll.js.map +1 -1
- package/dist/components/list/hooks/useListScrollElementAttachWeapp.d.ts +2 -15
- package/dist/components/list/hooks/useListScrollElementAttachWeapp.js +7 -26
- package/dist/components/list/hooks/useListScrollElementAttachWeapp.js.map +1 -1
- package/dist/components/list/hooks/useMeasureStartOffsetWeapp.d.ts +1 -0
- package/dist/components/list/hooks/useMeasureStartOffsetWeapp.js +4 -7
- package/dist/components/list/hooks/useMeasureStartOffsetWeapp.js.map +1 -1
- package/dist/components/list/hooks/useRefresher.d.ts +0 -1
- package/dist/components/list/hooks/useRefresher.js +50 -53
- package/dist/components/list/hooks/useRefresher.js.map +1 -1
- package/dist/components/list/hooks/useResizeObserver.d.ts +1 -0
- package/dist/components/list/hooks/useResizeObserver.js +6 -6
- 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/index.d.ts +5 -3
- package/dist/components/list/index.js +67 -29
- package/dist/components/list/index.js.map +1 -1
- package/dist/components/list/utils.d.ts +5 -0
- package/dist/components/list/utils.js +17 -1
- package/dist/components/list/utils.js.map +1 -1
- package/package.json +9 -9
|
@@ -15,4 +15,4 @@ export interface UseListNestedScrollResult {
|
|
|
15
15
|
/**
|
|
16
16
|
* 合并嵌套滚动相关逻辑:Context、自动查找、startOffset 测量、scrollElement 解析
|
|
17
17
|
*/
|
|
18
|
-
export declare function useListNestedScroll(listType: 'default' | 'nested', scrollElement?: RefObject<HTMLElement | null>, startOffsetProp?: number, isHorizontal?: boolean): UseListNestedScrollResult;
|
|
18
|
+
export declare function useListNestedScroll(listType: 'default' | 'nested', scrollElement?: RefObject<HTMLElement | null>, startOffsetProp?: number, isHorizontal?: boolean, selectorQueryScope?: object): UseListNestedScrollResult;
|
|
@@ -9,7 +9,7 @@ const EMPTY_SCROLL_REF = { current: null };
|
|
|
9
9
|
/**
|
|
10
10
|
* 合并嵌套滚动相关逻辑:Context、自动查找、startOffset 测量、scrollElement 解析
|
|
11
11
|
*/
|
|
12
|
-
function useListNestedScroll(listType, scrollElement, startOffsetProp, isHorizontal = false) {
|
|
12
|
+
function useListNestedScroll(listType, scrollElement, startOffsetProp, isHorizontal = false, selectorQueryScope) {
|
|
13
13
|
var _a, _b, _c, _d;
|
|
14
14
|
const contentWrapperRef = useRef(null);
|
|
15
15
|
const contentId = useMemo(() => `list-content-${Math.random().toString(36).slice(2, 11)}`, []);
|
|
@@ -34,7 +34,7 @@ function useListNestedScroll(listType, scrollElement, startOffsetProp, isHorizon
|
|
|
34
34
|
!hasExplicitCtxStartOffset;
|
|
35
35
|
const effectiveStartOffsetRef = useRef(0);
|
|
36
36
|
const measuredStartOffset = useMeasureStartOffset(effectiveScrollElement !== null && effectiveScrollElement !== void 0 ? effectiveScrollElement : EMPTY_SCROLL_REF, contentWrapperRef, { enabled: !!needMeasure, isHorizontal });
|
|
37
|
-
const measuredStartOffsetWeapp = useMeasureStartOffsetWeapp(effectiveScrollElement !== null && effectiveScrollElement !== void 0 ? effectiveScrollElement : EMPTY_SCROLL_REF, contentId, { enabled: !!needMeasureWeapp, isHorizontal, startOffsetRef: effectiveStartOffsetRef });
|
|
37
|
+
const measuredStartOffsetWeapp = useMeasureStartOffsetWeapp(effectiveScrollElement !== null && effectiveScrollElement !== void 0 ? effectiveScrollElement : EMPTY_SCROLL_REF, contentId, { enabled: !!needMeasureWeapp, isHorizontal, startOffsetRef: effectiveStartOffsetRef, selectorQueryScope });
|
|
38
38
|
const effectiveStartOffset = (_d = (_c = (_b = startOffsetProp !== null && startOffsetProp !== void 0 ? startOffsetProp : (ctxStart != null && ctxStart > 0 ? ctxStart : null)) !== null && _b !== void 0 ? _b : measuredStartOffset) !== null && _c !== void 0 ? _c : measuredStartOffsetWeapp) !== null && _d !== void 0 ? _d : 0;
|
|
39
39
|
// needMeasureWeapp 时由 useMeasureStartOffsetWeapp 的 exec 回调更新 ref,不在此覆盖,避免 re-render 用 stale 0 覆盖已测量的值
|
|
40
40
|
if (!needMeasureWeapp) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListNestedScroll.js","sources":["../../../../src/components/list/hooks/useListNestedScroll.ts"],"sourcesContent":["import React, { useContext, useMemo, useRef } from 'react'\n\nimport {\n type ScrollElementContextValueShape,\n ScrollElementContextOrFallback,\n} from '../../../utils/scrollElementContext'\nimport { isH5, isWeapp } from '../utils'\nimport { useMeasureStartOffset } from './useMeasureStartOffset'\nimport { useMeasureStartOffsetWeapp } from './useMeasureStartOffsetWeapp'\nimport { useScrollParentAutoFind } from './useScrollParentAutoFind'\n\nimport type { RefObject } from 'react'\n\nconst EMPTY_SCROLL_REF = { current: null as HTMLElement | null }\n\nexport interface UseListNestedScrollResult {\n effectiveScrollElement: RefObject<HTMLElement | null> | null\n effectiveStartOffset: number\n /** 小程序用:实时读取 startOffset,避免 useState 异步更新导致 useListScrollElementAttachWeapp 读到 0 */\n effectiveStartOffsetRef: React.MutableRefObject<number>\n useScrollElementMode: boolean\n needAutoFind: boolean\n autoFindStatus: 'pending' | 'found' | 'not-found'\n contentWrapperRef: RefObject<HTMLDivElement | null>\n /** 小程序自动查找用:content 节点的 id,需挂到 contentWrapper 对应的 View 上 */\n contentId: string\n}\n\n/**\n * 合并嵌套滚动相关逻辑:Context、自动查找、startOffset 测量、scrollElement 解析\n */\nexport function useListNestedScroll(\n listType: 'default' | 'nested',\n scrollElement?: RefObject<HTMLElement | null>,\n startOffsetProp?: number,\n isHorizontal: boolean = false\n): UseListNestedScrollResult {\n const contentWrapperRef = useRef<HTMLDivElement>(null)\n const contentId = useMemo(() => `list-content-${Math.random().toString(36).slice(2, 11)}`, [])\n const scrollElementCtx = useContext(ScrollElementContextOrFallback) as ScrollElementContextValueShape | null\n const ctxStart = scrollElementCtx?.startOffset\n const hasExplicitCtxStartOffset = ctxStart != null && ctxStart > 0\n\n const needAutoFind =\n listType === 'nested' &&\n !scrollElement &&\n !scrollElementCtx?.scrollRef &&\n (isH5 || isWeapp)\n const { scrollParentRef: autoFoundRef, status: autoFindStatus } = useScrollParentAutoFind(\n contentWrapperRef,\n { enabled: !!needAutoFind, isHorizontal, contentId: isWeapp ? contentId : undefined }\n )\n\n const effectiveScrollElement =\n scrollElement ??\n scrollElementCtx?.scrollRef ??\n (needAutoFind && autoFindStatus === 'found' ? autoFoundRef : null)\n\n const needMeasure =\n listType === 'nested' &&\n effectiveScrollElement &&\n isH5 &&\n startOffsetProp == null &&\n !hasExplicitCtxStartOffset\n const needMeasureWeapp =\n listType === 'nested' &&\n effectiveScrollElement &&\n isWeapp &&\n startOffsetProp == null &&\n !hasExplicitCtxStartOffset\n const effectiveStartOffsetRef = useRef(0)\n const measuredStartOffset = useMeasureStartOffset(\n effectiveScrollElement ?? EMPTY_SCROLL_REF,\n contentWrapperRef,\n { enabled: !!needMeasure, isHorizontal }\n )\n const measuredStartOffsetWeapp = useMeasureStartOffsetWeapp(\n effectiveScrollElement ?? EMPTY_SCROLL_REF,\n contentId,\n { enabled: !!needMeasureWeapp, isHorizontal, startOffsetRef: effectiveStartOffsetRef }\n )\n\n const effectiveStartOffset =\n startOffsetProp ??\n (ctxStart != null && ctxStart > 0 ? ctxStart : null) ??\n measuredStartOffset ??\n measuredStartOffsetWeapp ??\n 0\n\n // needMeasureWeapp 时由 useMeasureStartOffsetWeapp 的 exec 回调更新 ref,不在此覆盖,避免 re-render 用 stale 0 覆盖已测量的值\n if (!needMeasureWeapp) {\n effectiveStartOffsetRef.current = effectiveStartOffset\n }\n\n const useScrollElementMode = listType === 'nested' && !!(effectiveScrollElement && (isH5 || isWeapp))\n\n if (listType === 'nested' && !effectiveScrollElement && (isH5 || isWeapp) && autoFindStatus === 'not-found') {\n // eslint-disable-next-line no-console\n console.warn('[List] nestedScroll 模式但无 scrollElement(props/Context/自动查找),回退为 default')\n }\n\n return {\n effectiveScrollElement,\n effectiveStartOffset,\n effectiveStartOffsetRef,\n useScrollElementMode,\n needAutoFind,\n autoFindStatus,\n contentWrapperRef,\n contentId,\n }\n}\n"],"names":[],"mappings":";;;;;;;AAaA,MAAM,gBAAgB,GAAG,EAAE,OAAO,EAAE,IAA0B,EAAE;AAehE;;AAEG;
|
|
1
|
+
{"version":3,"file":"useListNestedScroll.js","sources":["../../../../src/components/list/hooks/useListNestedScroll.ts"],"sourcesContent":["import React, { useContext, useMemo, useRef } from 'react'\n\nimport {\n type ScrollElementContextValueShape,\n ScrollElementContextOrFallback,\n} from '../../../utils/scrollElementContext'\nimport { isH5, isWeapp } from '../utils'\nimport { useMeasureStartOffset } from './useMeasureStartOffset'\nimport { useMeasureStartOffsetWeapp } from './useMeasureStartOffsetWeapp'\nimport { useScrollParentAutoFind } from './useScrollParentAutoFind'\n\nimport type { RefObject } from 'react'\n\nconst EMPTY_SCROLL_REF = { current: null as HTMLElement | null }\n\nexport interface UseListNestedScrollResult {\n effectiveScrollElement: RefObject<HTMLElement | null> | null\n effectiveStartOffset: number\n /** 小程序用:实时读取 startOffset,避免 useState 异步更新导致 useListScrollElementAttachWeapp 读到 0 */\n effectiveStartOffsetRef: React.MutableRefObject<number>\n useScrollElementMode: boolean\n needAutoFind: boolean\n autoFindStatus: 'pending' | 'found' | 'not-found'\n contentWrapperRef: RefObject<HTMLDivElement | null>\n /** 小程序自动查找用:content 节点的 id,需挂到 contentWrapper 对应的 View 上 */\n contentId: string\n}\n\n/**\n * 合并嵌套滚动相关逻辑:Context、自动查找、startOffset 测量、scrollElement 解析\n */\nexport function useListNestedScroll(\n listType: 'default' | 'nested',\n scrollElement?: RefObject<HTMLElement | null>,\n startOffsetProp?: number,\n isHorizontal: boolean = false,\n selectorQueryScope?: object\n): UseListNestedScrollResult {\n const contentWrapperRef = useRef<HTMLDivElement>(null)\n const contentId = useMemo(() => `list-content-${Math.random().toString(36).slice(2, 11)}`, [])\n const scrollElementCtx = useContext(ScrollElementContextOrFallback) as ScrollElementContextValueShape | null\n const ctxStart = scrollElementCtx?.startOffset\n const hasExplicitCtxStartOffset = ctxStart != null && ctxStart > 0\n\n const needAutoFind =\n listType === 'nested' &&\n !scrollElement &&\n !scrollElementCtx?.scrollRef &&\n (isH5 || isWeapp)\n const { scrollParentRef: autoFoundRef, status: autoFindStatus } = useScrollParentAutoFind(\n contentWrapperRef,\n { enabled: !!needAutoFind, isHorizontal, contentId: isWeapp ? contentId : undefined }\n )\n\n const effectiveScrollElement =\n scrollElement ??\n scrollElementCtx?.scrollRef ??\n (needAutoFind && autoFindStatus === 'found' ? autoFoundRef : null)\n\n const needMeasure =\n listType === 'nested' &&\n effectiveScrollElement &&\n isH5 &&\n startOffsetProp == null &&\n !hasExplicitCtxStartOffset\n const needMeasureWeapp =\n listType === 'nested' &&\n effectiveScrollElement &&\n isWeapp &&\n startOffsetProp == null &&\n !hasExplicitCtxStartOffset\n const effectiveStartOffsetRef = useRef(0)\n const measuredStartOffset = useMeasureStartOffset(\n effectiveScrollElement ?? EMPTY_SCROLL_REF,\n contentWrapperRef,\n { enabled: !!needMeasure, isHorizontal }\n )\n const measuredStartOffsetWeapp = useMeasureStartOffsetWeapp(\n effectiveScrollElement ?? EMPTY_SCROLL_REF,\n contentId,\n { enabled: !!needMeasureWeapp, isHorizontal, startOffsetRef: effectiveStartOffsetRef, selectorQueryScope }\n )\n\n const effectiveStartOffset =\n startOffsetProp ??\n (ctxStart != null && ctxStart > 0 ? ctxStart : null) ??\n measuredStartOffset ??\n measuredStartOffsetWeapp ??\n 0\n\n // needMeasureWeapp 时由 useMeasureStartOffsetWeapp 的 exec 回调更新 ref,不在此覆盖,避免 re-render 用 stale 0 覆盖已测量的值\n if (!needMeasureWeapp) {\n effectiveStartOffsetRef.current = effectiveStartOffset\n }\n\n const useScrollElementMode = listType === 'nested' && !!(effectiveScrollElement && (isH5 || isWeapp))\n\n if (listType === 'nested' && !effectiveScrollElement && (isH5 || isWeapp) && autoFindStatus === 'not-found') {\n // eslint-disable-next-line no-console\n console.warn('[List] nestedScroll 模式但无 scrollElement(props/Context/自动查找),回退为 default')\n }\n\n return {\n effectiveScrollElement,\n effectiveStartOffset,\n effectiveStartOffsetRef,\n useScrollElementMode,\n needAutoFind,\n autoFindStatus,\n contentWrapperRef,\n contentId,\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -10,18 +10,5 @@ import type { ListScrollElementAttachRefs } from './useListScrollElementAttach';
|
|
|
10
10
|
* - 若 scroll-view 无 id,自动分配临时 id 供 SelectorQuery 使用
|
|
11
11
|
*/
|
|
12
12
|
export declare function useListScrollElementAttachWeapp(enabled: boolean, effectiveScrollElement: RefObject<HTMLElement | null> | null, effectiveStartOffsetRef: MutableRefObject<number>, effectiveStartOffset: number, isHorizontal: boolean, setContainerLength: (v: number) => void, updateRenderOffset: (offset: number, sync?: boolean, source?: string) => void, scrollRefProp: React.MutableRefObject<HTMLElement | null> | undefined, refsRef: RefObject<ListScrollElementAttachRefs>,
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
*
|
|
16
|
-
* 使用场景:
|
|
17
|
-
* 1. 首次 scroll:SelectorQuery.exec 异步,scroll 事件可能早于测量回调,containerLengthRef 仍为 0
|
|
18
|
-
* 2. 测量长期失败:SelectorQuery 返回空或 rect 尺寸为 0(如 id 错误、节点未挂载)
|
|
19
|
-
*
|
|
20
|
-
* 兜底逻辑:
|
|
21
|
-
* - measure 回调:rect 为空或 measured≤0 时,若有 fallback 则写入 containerLengthRef 与 setContainerLength
|
|
22
|
-
* - scroll handler:containerLengthRef 为 0 时,用 fallback 参与 onScrollToUpper/Lower 判断
|
|
23
|
-
*
|
|
24
|
-
* 取值建议:与 List 的 initialContainerLength 一致(默认 400,或 height/width prop),避免与 renderRange 等逻辑冲突。
|
|
25
|
-
* 注意:fallback 与真实视口差异大时,onScrollToUpper/Lower 可能误触发;measure 每 150ms 轮询,正常会很快覆盖。
|
|
26
|
-
*/
|
|
27
|
-
fallbackContainerLength?: number): void;
|
|
13
|
+
/** 测量未就绪或失败时的容器长度兜底,宜与 initialContainerLength 一致 */
|
|
14
|
+
fallbackContainerLength?: number, selectorQueryScope?: object): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import Taro from '@tarojs/taro';
|
|
2
1
|
import { useRef, useEffect } from 'react';
|
|
2
|
+
import { createSelectorQueryScoped } from '../utils.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* 小程序版 scrollElement 模式:监听外部 scroll-view 的滚动事件驱动 renderOffset,
|
|
@@ -11,21 +11,8 @@ import { useRef, useEffect } from 'react';
|
|
|
11
11
|
* - 若 scroll-view 无 id,自动分配临时 id 供 SelectorQuery 使用
|
|
12
12
|
*/
|
|
13
13
|
function useListScrollElementAttachWeapp(enabled, effectiveScrollElement, effectiveStartOffsetRef, effectiveStartOffset, isHorizontal, setContainerLength, updateRenderOffset, scrollRefProp, refsRef,
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
* 使用场景:
|
|
18
|
-
* 1. 首次 scroll:SelectorQuery.exec 异步,scroll 事件可能早于测量回调,containerLengthRef 仍为 0
|
|
19
|
-
* 2. 测量长期失败:SelectorQuery 返回空或 rect 尺寸为 0(如 id 错误、节点未挂载)
|
|
20
|
-
*
|
|
21
|
-
* 兜底逻辑:
|
|
22
|
-
* - measure 回调:rect 为空或 measured≤0 时,若有 fallback 则写入 containerLengthRef 与 setContainerLength
|
|
23
|
-
* - scroll handler:containerLengthRef 为 0 时,用 fallback 参与 onScrollToUpper/Lower 判断
|
|
24
|
-
*
|
|
25
|
-
* 取值建议:与 List 的 initialContainerLength 一致(默认 400,或 height/width prop),避免与 renderRange 等逻辑冲突。
|
|
26
|
-
* 注意:fallback 与真实视口差异大时,onScrollToUpper/Lower 可能误触发;measure 每 150ms 轮询,正常会很快覆盖。
|
|
27
|
-
*/
|
|
28
|
-
fallbackContainerLength) {
|
|
14
|
+
/** 测量未就绪或失败时的容器长度兜底,宜与 initialContainerLength 一致 */
|
|
15
|
+
fallbackContainerLength, selectorQueryScope) {
|
|
29
16
|
const containerLengthRef = useRef(0);
|
|
30
17
|
const autoIdRef = useRef(`_ls_${Math.random().toString(36).slice(2, 9)}`);
|
|
31
18
|
useEffect(() => {
|
|
@@ -41,10 +28,7 @@ fallbackContainerLength) {
|
|
|
41
28
|
}
|
|
42
29
|
const scrollViewId = el.id;
|
|
43
30
|
const measure = () => {
|
|
44
|
-
const
|
|
45
|
-
const query = (instance === null || instance === void 0 ? void 0 : instance.page)
|
|
46
|
-
? Taro.createSelectorQuery().in(instance.page)
|
|
47
|
-
: Taro.createSelectorQuery();
|
|
31
|
+
const query = createSelectorQueryScoped(selectorQueryScope);
|
|
48
32
|
query
|
|
49
33
|
.select(`#${scrollViewId}`)
|
|
50
34
|
.boundingClientRect()
|
|
@@ -68,7 +52,7 @@ fallbackContainerLength) {
|
|
|
68
52
|
measure();
|
|
69
53
|
const interval = setInterval(measure, 150);
|
|
70
54
|
return () => clearInterval(interval);
|
|
71
|
-
}, [enabled, effectiveScrollElement, isHorizontal, scrollRefProp, setContainerLength, fallbackContainerLength]);
|
|
55
|
+
}, [enabled, effectiveScrollElement, isHorizontal, scrollRefProp, setContainerLength, fallbackContainerLength, selectorQueryScope]);
|
|
72
56
|
useEffect(() => {
|
|
73
57
|
if (!enabled || !effectiveScrollElement)
|
|
74
58
|
return;
|
|
@@ -124,10 +108,7 @@ fallbackContainerLength) {
|
|
|
124
108
|
const scrollViewId = target.id || autoIdRef.current;
|
|
125
109
|
if (!target.id)
|
|
126
110
|
target.id = scrollViewId;
|
|
127
|
-
const
|
|
128
|
-
const query = (instance === null || instance === void 0 ? void 0 : instance.page)
|
|
129
|
-
? Taro.createSelectorQuery().in(instance.page)
|
|
130
|
-
: Taro.createSelectorQuery();
|
|
111
|
+
const query = createSelectorQueryScoped(selectorQueryScope);
|
|
131
112
|
query.select(`#${scrollViewId}`).scrollOffset().exec((res) => {
|
|
132
113
|
var _a, _b;
|
|
133
114
|
const info = res === null || res === void 0 ? void 0 : res[0];
|
|
@@ -146,7 +127,7 @@ fallbackContainerLength) {
|
|
|
146
127
|
cancelled = true;
|
|
147
128
|
teardown === null || teardown === void 0 ? void 0 : teardown();
|
|
148
129
|
};
|
|
149
|
-
}, [enabled, effectiveScrollElement, isHorizontal, effectiveStartOffsetRef, effectiveStartOffset, updateRenderOffset, refsRef, fallbackContainerLength]);
|
|
130
|
+
}, [enabled, effectiveScrollElement, isHorizontal, effectiveStartOffsetRef, effectiveStartOffset, updateRenderOffset, refsRef, fallbackContainerLength, selectorQueryScope]);
|
|
150
131
|
}
|
|
151
132
|
|
|
152
133
|
export { useListScrollElementAttachWeapp };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListScrollElementAttachWeapp.js","sources":["../../../../src/components/list/hooks/useListScrollElementAttachWeapp.ts"],"sourcesContent":["import Taro from '@tarojs/taro'\nimport { useEffect, useRef } from 'react'\n\nimport type { MutableRefObject, RefObject } from 'react'\nimport type { ListScrollElementAttachRefs } from './useListScrollElementAttach'\n\n/**\n * 小程序版 scrollElement 模式:监听外部 scroll-view 的滚动事件驱动 renderOffset,\n * 并通过 createSelectorQuery 测量容器尺寸。\n *\n * 与 H5 版 useListScrollElementAttach 的区别:\n * - 容器尺寸通过 createSelectorQuery 测量(无 ResizeObserver)\n * - 滚动事件通过 TaroElement.addEventListener,格式为 TaroEvent(detail 合并到 target 上)\n * - 若 scroll-view 无 id,自动分配临时 id 供 SelectorQuery 使用\n */\nexport function useListScrollElementAttachWeapp(\n enabled: boolean,\n effectiveScrollElement: RefObject<HTMLElement | null> | null,\n effectiveStartOffsetRef: MutableRefObject<number>,\n effectiveStartOffset: number,\n isHorizontal: boolean,\n setContainerLength: (v: number) => void,\n updateRenderOffset: (offset: number, sync?: boolean, source?: string) => void,\n scrollRefProp: React.MutableRefObject<HTMLElement | null> | undefined,\n refsRef: RefObject<ListScrollElementAttachRefs>,\n /**\n * 【兜底】容器尺寸测量失败或未完成时的回退值。\n *\n * 使用场景:\n * 1. 首次 scroll:SelectorQuery.exec 异步,scroll 事件可能早于测量回调,containerLengthRef 仍为 0\n * 2. 测量长期失败:SelectorQuery 返回空或 rect 尺寸为 0(如 id 错误、节点未挂载)\n *\n * 兜底逻辑:\n * - measure 回调:rect 为空或 measured≤0 时,若有 fallback 则写入 containerLengthRef 与 setContainerLength\n * - scroll handler:containerLengthRef 为 0 时,用 fallback 参与 onScrollToUpper/Lower 判断\n *\n * 取值建议:与 List 的 initialContainerLength 一致(默认 400,或 height/width prop),避免与 renderRange 等逻辑冲突。\n * 注意:fallback 与真实视口差异大时,onScrollToUpper/Lower 可能误触发;measure 每 150ms 轮询,正常会很快覆盖。\n */\n fallbackContainerLength?: number\n) {\n const containerLengthRef = useRef(0)\n const autoIdRef = useRef(`_ls_${Math.random().toString(36).slice(2, 9)}`)\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n const el = effectiveScrollElement.current as any\n if (!el) return\n if (scrollRefProp) scrollRefProp.current = el\n\n if (!el.id) {\n el.id = autoIdRef.current\n }\n const scrollViewId = el.id\n\n const measure = () => {\n const instance = Taro.getCurrentInstance()\n const query = instance?.page\n ? Taro.createSelectorQuery().in(instance.page as any)\n : Taro.createSelectorQuery()\n query\n .select(`#${scrollViewId}`)\n .boundingClientRect()\n .exec((res) => {\n const rect = res?.[0]\n if (rect) {\n const measured = isHorizontal ? rect.width : rect.height\n if (measured > 0) {\n containerLengthRef.current = measured\n setContainerLength(measured)\n return\n }\n }\n // 兜底:rect 为空或 measured≤0 时,使用 fallback 避免 containerLengthRef 长期为 0\n if (fallbackContainerLength != null && fallbackContainerLength > 0) {\n containerLengthRef.current = fallbackContainerLength\n setContainerLength(fallbackContainerLength)\n }\n })\n }\n\n measure()\n const interval = setInterval(measure, 150)\n return () => clearInterval(interval)\n }, [enabled, effectiveScrollElement, isHorizontal, scrollRefProp, setContainerLength, fallbackContainerLength])\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n\n let cancelled = false\n let teardown: (() => void) | null = null\n const maxRetries = 20\n\n const tryAttach = (retryCount = 0) => {\n if (cancelled) return\n const target = effectiveScrollElement.current as any\n if (!target) {\n if (retryCount < maxRetries) {\n setTimeout(() => tryAttach(retryCount + 1), 50)\n }\n return\n }\n\n let inUpperZone = true\n let inLowerZone = false\n\n const handler = (e: any) => {\n const r = refsRef.current\n if (!r) return\n\n const scrollPos = isHorizontal\n ? (e?.target?.scrollLeft ?? e?.mpEvent?.detail?.scrollLeft ?? 0)\n : (e?.target?.scrollTop ?? e?.mpEvent?.detail?.scrollTop ?? 0)\n const startOffset = effectiveStartOffsetRef.current\n const adjustedPos = scrollPos - startOffset\n const effectiveAdjusted = Math.max(0, adjustedPos)\n\n r.scrollCorrection.markUserScrolling()\n updateRenderOffset(effectiveAdjusted, false, 'scrollElement')\n const scrollTop = isHorizontal ? 0 : scrollPos\n const scrollLeft = isHorizontal ? scrollPos : 0\n r.onScroll?.({ scrollTop, scrollLeft, detail: { scrollTop, scrollLeft } })\n\n // 兜底:measure 未完成时 containerLengthRef 为 0,用 fallback 参与触顶/触底判断,避免漏触发\n const clientSize = containerLengthRef.current || fallbackContainerLength || 0\n if (clientSize > 0) {\n const { upper, lower } = r.threshold\n const innerContentLen = r.listContentLength\n const nowInUpper = effectiveAdjusted <= upper\n const nowInLower = innerContentLen > 0 && effectiveAdjusted + clientSize >= innerContentLen - lower\n if (nowInUpper && !inUpperZone) r.onScrollToUpper?.()\n if (nowInLower && !inLowerZone) r.onScrollToLower?.()\n inUpperZone = nowInUpper\n inLowerZone = nowInLower\n }\n }\n\n target.addEventListener('scroll', handler)\n teardown = () => target.removeEventListener('scroll', handler)\n\n // 初始 renderOffset:weapp 无法直接读 target.scrollTop,需通过 SelectorQuery 查询\n const scrollViewId = target.id || autoIdRef.current\n if (!target.id) target.id = scrollViewId\n const instance = Taro.getCurrentInstance()\n const query = instance?.page\n ? Taro.createSelectorQuery().in(instance.page as any)\n : Taro.createSelectorQuery()\n query.select(`#${scrollViewId}`).scrollOffset().exec((res) => {\n const info = res?.[0]\n if (info) {\n const scrollTopVal = info.scrollTop ?? 0\n const scrollLeftVal = info.scrollLeft ?? 0\n const scrollPos = isHorizontal ? scrollLeftVal : scrollTopVal\n const startOffset = effectiveStartOffsetRef.current\n const initialAdjusted = Math.max(0, scrollPos - startOffset)\n updateRenderOffset(initialAdjusted, false, 'scrollElement')\n }\n })\n }\n\n tryAttach()\n return () => {\n cancelled = true\n teardown?.()\n }\n }, [enabled, effectiveScrollElement, isHorizontal, effectiveStartOffsetRef, effectiveStartOffset, updateRenderOffset, refsRef, fallbackContainerLength])\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useListScrollElementAttachWeapp.js","sources":["../../../../src/components/list/hooks/useListScrollElementAttachWeapp.ts"],"sourcesContent":["import { useEffect, useRef } from 'react'\n\nimport { createSelectorQueryScoped } from '../utils'\n\nimport type { MutableRefObject, RefObject } from 'react'\nimport type { ListScrollElementAttachRefs } from './useListScrollElementAttach'\n\n/**\n * 小程序版 scrollElement 模式:监听外部 scroll-view 的滚动事件驱动 renderOffset,\n * 并通过 createSelectorQuery 测量容器尺寸。\n *\n * 与 H5 版 useListScrollElementAttach 的区别:\n * - 容器尺寸通过 createSelectorQuery 测量(无 ResizeObserver)\n * - 滚动事件通过 TaroElement.addEventListener,格式为 TaroEvent(detail 合并到 target 上)\n * - 若 scroll-view 无 id,自动分配临时 id 供 SelectorQuery 使用\n */\nexport function useListScrollElementAttachWeapp(\n enabled: boolean,\n effectiveScrollElement: RefObject<HTMLElement | null> | null,\n effectiveStartOffsetRef: MutableRefObject<number>,\n effectiveStartOffset: number,\n isHorizontal: boolean,\n setContainerLength: (v: number) => void,\n updateRenderOffset: (offset: number, sync?: boolean, source?: string) => void,\n scrollRefProp: React.MutableRefObject<HTMLElement | null> | undefined,\n refsRef: RefObject<ListScrollElementAttachRefs>,\n /** 测量未就绪或失败时的容器长度兜底,宜与 initialContainerLength 一致 */\n fallbackContainerLength?: number,\n selectorQueryScope?: object\n) {\n const containerLengthRef = useRef(0)\n const autoIdRef = useRef(`_ls_${Math.random().toString(36).slice(2, 9)}`)\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n const el = effectiveScrollElement.current as any\n if (!el) return\n if (scrollRefProp) scrollRefProp.current = el\n\n if (!el.id) {\n el.id = autoIdRef.current\n }\n const scrollViewId = el.id\n\n const measure = () => {\n const query = createSelectorQueryScoped(selectorQueryScope)\n query\n .select(`#${scrollViewId}`)\n .boundingClientRect()\n .exec((res) => {\n const rect = res?.[0]\n if (rect) {\n const measured = isHorizontal ? rect.width : rect.height\n if (measured > 0) {\n containerLengthRef.current = measured\n setContainerLength(measured)\n return\n }\n }\n // 兜底:rect 为空或 measured≤0 时,使用 fallback 避免 containerLengthRef 长期为 0\n if (fallbackContainerLength != null && fallbackContainerLength > 0) {\n containerLengthRef.current = fallbackContainerLength\n setContainerLength(fallbackContainerLength)\n }\n })\n }\n\n measure()\n const interval = setInterval(measure, 150)\n return () => clearInterval(interval)\n }, [enabled, effectiveScrollElement, isHorizontal, scrollRefProp, setContainerLength, fallbackContainerLength, selectorQueryScope])\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n\n let cancelled = false\n let teardown: (() => void) | null = null\n const maxRetries = 20\n\n const tryAttach = (retryCount = 0) => {\n if (cancelled) return\n const target = effectiveScrollElement.current as any\n if (!target) {\n if (retryCount < maxRetries) {\n setTimeout(() => tryAttach(retryCount + 1), 50)\n }\n return\n }\n\n let inUpperZone = true\n let inLowerZone = false\n\n const handler = (e: any) => {\n const r = refsRef.current\n if (!r) return\n\n const scrollPos = isHorizontal\n ? (e?.target?.scrollLeft ?? e?.mpEvent?.detail?.scrollLeft ?? 0)\n : (e?.target?.scrollTop ?? e?.mpEvent?.detail?.scrollTop ?? 0)\n const startOffset = effectiveStartOffsetRef.current\n const adjustedPos = scrollPos - startOffset\n const effectiveAdjusted = Math.max(0, adjustedPos)\n\n r.scrollCorrection.markUserScrolling()\n updateRenderOffset(effectiveAdjusted, false, 'scrollElement')\n const scrollTop = isHorizontal ? 0 : scrollPos\n const scrollLeft = isHorizontal ? scrollPos : 0\n r.onScroll?.({ scrollTop, scrollLeft, detail: { scrollTop, scrollLeft } })\n\n // 兜底:measure 未完成时 containerLengthRef 为 0,用 fallback 参与触顶/触底判断,避免漏触发\n const clientSize = containerLengthRef.current || fallbackContainerLength || 0\n if (clientSize > 0) {\n const { upper, lower } = r.threshold\n const innerContentLen = r.listContentLength\n const nowInUpper = effectiveAdjusted <= upper\n const nowInLower = innerContentLen > 0 && effectiveAdjusted + clientSize >= innerContentLen - lower\n if (nowInUpper && !inUpperZone) r.onScrollToUpper?.()\n if (nowInLower && !inLowerZone) r.onScrollToLower?.()\n inUpperZone = nowInUpper\n inLowerZone = nowInLower\n }\n }\n\n target.addEventListener('scroll', handler)\n teardown = () => target.removeEventListener('scroll', handler)\n\n // 初始 renderOffset:weapp 无法直接读 target.scrollTop,需通过 SelectorQuery 查询\n const scrollViewId = target.id || autoIdRef.current\n if (!target.id) target.id = scrollViewId\n const query = createSelectorQueryScoped(selectorQueryScope)\n query.select(`#${scrollViewId}`).scrollOffset().exec((res) => {\n const info = res?.[0]\n if (info) {\n const scrollTopVal = info.scrollTop ?? 0\n const scrollLeftVal = info.scrollLeft ?? 0\n const scrollPos = isHorizontal ? scrollLeftVal : scrollTopVal\n const startOffset = effectiveStartOffsetRef.current\n const initialAdjusted = Math.max(0, scrollPos - startOffset)\n updateRenderOffset(initialAdjusted, false, 'scrollElement')\n }\n })\n }\n\n tryAttach()\n return () => {\n cancelled = true\n teardown?.()\n }\n }, [enabled, effectiveScrollElement, isHorizontal, effectiveStartOffsetRef, effectiveStartOffset, updateRenderOffset, refsRef, fallbackContainerLength, selectorQueryScope])\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import Taro from '@tarojs/taro';
|
|
2
1
|
import { useState, useRef, useEffect } from 'react';
|
|
2
|
+
import { createSelectorQueryScoped } from '../utils.js';
|
|
3
3
|
|
|
4
4
|
const MAX_RETRY = 20;
|
|
5
5
|
const RETRY_DELAY = 50;
|
|
@@ -13,7 +13,7 @@ const MEASURE_INTERVAL = 150;
|
|
|
13
13
|
* (content 在屏幕上的 top - scroll 可视区 top + 已滚动距离)
|
|
14
14
|
*/
|
|
15
15
|
function useMeasureStartOffsetWeapp(scrollElRef, contentId, options) {
|
|
16
|
-
const { enabled, isHorizontal = false, startOffsetRef } = options;
|
|
16
|
+
const { enabled, isHorizontal = false, startOffsetRef, selectorQueryScope } = options;
|
|
17
17
|
const [measuredStartOffset, setMeasuredStartOffset] = useState(0);
|
|
18
18
|
const [retryTrigger, setRetryTrigger] = useState(0);
|
|
19
19
|
const retryCountRef = useRef(0);
|
|
@@ -31,10 +31,7 @@ function useMeasureStartOffsetWeapp(scrollElRef, contentId, options) {
|
|
|
31
31
|
scrollEl.id = autoIdRef.current;
|
|
32
32
|
}
|
|
33
33
|
const scrollViewId = scrollEl.id;
|
|
34
|
-
const
|
|
35
|
-
const query = (instance === null || instance === void 0 ? void 0 : instance.page)
|
|
36
|
-
? Taro.createSelectorQuery().in(instance.page)
|
|
37
|
-
: Taro.createSelectorQuery();
|
|
34
|
+
const query = createSelectorQueryScoped(selectorQueryScope);
|
|
38
35
|
query
|
|
39
36
|
.select(`#${scrollViewId}`)
|
|
40
37
|
.boundingClientRect()
|
|
@@ -77,7 +74,7 @@ function useMeasureStartOffsetWeapp(scrollElRef, contentId, options) {
|
|
|
77
74
|
scrollEl.removeEventListener('scroll', measure);
|
|
78
75
|
clearInterval(interval);
|
|
79
76
|
};
|
|
80
|
-
}, [enabled, scrollElRef, contentId, isHorizontal, retryTrigger]);
|
|
77
|
+
}, [enabled, scrollElRef, contentId, isHorizontal, retryTrigger, selectorQueryScope]);
|
|
81
78
|
return measuredStartOffset;
|
|
82
79
|
}
|
|
83
80
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMeasureStartOffsetWeapp.js","sources":["../../../../src/components/list/hooks/useMeasureStartOffsetWeapp.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"useMeasureStartOffsetWeapp.js","sources":["../../../../src/components/list/hooks/useMeasureStartOffsetWeapp.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\n\nimport { createSelectorQueryScoped } 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 测量:通过 createSelectorQuery 获取 scroll-view 与 content 的位置,\n * 计算 content 相对于 scroll 内容顶部的偏移。\n *\n * 公式:startOffset = contentRect.top - scrollRect.top + scrollTop\n * (content 在屏幕上的 top - scroll 可视区 top + 已滚动距离)\n */\nexport function useMeasureStartOffsetWeapp(\n scrollElRef: RefObject<HTMLElement | null>,\n contentId: string,\n options: { enabled: boolean, isHorizontal?: boolean, startOffsetRef?: MutableRefObject<number>, selectorQueryScope?: object }\n): number {\n const { enabled, isHorizontal = false, startOffsetRef, selectorQueryScope } = 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 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 const query = createSelectorQueryScoped(selectorQueryScope)\n query\n .select(`#${scrollViewId}`)\n .boundingClientRect()\n .select(`#${scrollViewId}`)\n .scrollOffset()\n .select(`#${contentId}`)\n .boundingClientRect()\n .exec((res) => {\n const scrollRect = res?.[0]\n const scrollInfo = res?.[1]\n const contentRect = res?.[2]\n if (!scrollRect || !scrollInfo || !contentRect) return\n\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 startOffsetRef && (startOffsetRef.current = value)\n setMeasuredStartOffset((prevVal) => (Math.abs(prevVal - value) < 1 ? prevVal : value))\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 // 对标 H5:scroll 时触发测量(H5 用 scrollEl.addEventListener('scroll', measure))\n scrollEl.addEventListener('scroll', measure)\n // 无 ResizeObserver,用 interval 兜底布局变化\n const interval = setInterval(measure, MEASURE_INTERVAL)\n return () => {\n scrollEl.removeEventListener('scroll', measure)\n clearInterval(interval)\n }\n }, [enabled, scrollElRef, contentId, isHorizontal, retryTrigger, selectorQueryScope])\n\n return measuredStartOffset\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -4,31 +4,41 @@ import React, { useState, useRef, useCallback } 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 nowMs() {
|
|
24
|
+
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
|
|
25
|
+
return performance.now();
|
|
26
|
+
}
|
|
27
|
+
return Date.now();
|
|
28
|
+
}
|
|
29
|
+
function getViewportHeightPx() {
|
|
30
|
+
if (typeof window === 'undefined' || !Number.isFinite(window.innerHeight) || window.innerHeight <= 0) {
|
|
31
|
+
return VIEWPORT_HEIGHT_FALLBACK;
|
|
32
|
+
}
|
|
33
|
+
return window.innerHeight;
|
|
34
|
+
}
|
|
35
|
+
/** 阻尼增量:ratio=总下拉位移/视口高;超过 dampingLimit 后不再累加 */
|
|
36
|
+
function dampIncrement(diff, totalMove, currentPull, viewportHeight, dampingLimit) {
|
|
27
37
|
if (diff <= 0)
|
|
28
38
|
return 0;
|
|
29
|
-
if (currentPull >=
|
|
39
|
+
if (currentPull >= dampingLimit)
|
|
30
40
|
return 0;
|
|
31
|
-
const ratio = Math.min(totalMove /
|
|
41
|
+
const ratio = Math.min(totalMove / viewportHeight, 1);
|
|
32
42
|
return diff * (1 - ratio) * 0.6;
|
|
33
43
|
}
|
|
34
44
|
function useRefresher(config,
|
|
@@ -61,7 +71,6 @@ getIsTouchInListArea) {
|
|
|
61
71
|
thresholdRef.current = (_c = config === null || config === void 0 ? void 0 : config.refresherThreshold) !== null && _c !== void 0 ? _c : 45;
|
|
62
72
|
const configRef = useRef(config);
|
|
63
73
|
configRef.current = config;
|
|
64
|
-
/** H5:触发 onRefresherStatusChange 回调(仅状态变化时触发) */
|
|
65
74
|
const emitStatusChangeRef = useRef((status, dy) => {
|
|
66
75
|
var _a, _b;
|
|
67
76
|
if (refreshStatusRef.current !== status) {
|
|
@@ -78,9 +87,6 @@ getIsTouchInListArea) {
|
|
|
78
87
|
topThresholdPxRef.current = topThresholdPx;
|
|
79
88
|
const getIsTouchInListAreaRef = useRef(getIsTouchInListArea);
|
|
80
89
|
getIsTouchInListAreaRef.current = getIsTouchInListArea;
|
|
81
|
-
// ========================================
|
|
82
|
-
// 小程序:原生 refresher-* API
|
|
83
|
-
// ========================================
|
|
84
90
|
const scrollViewRefresherProps = config && supportsNativeRefresher ? {
|
|
85
91
|
refresherEnabled: (_d = config.refresherEnabled) !== null && _d !== void 0 ? _d : true,
|
|
86
92
|
refresherThreshold: (_e = config.refresherThreshold) !== null && _e !== void 0 ? _e : 45,
|
|
@@ -94,12 +100,16 @@ getIsTouchInListArea) {
|
|
|
94
100
|
(_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
101
|
},
|
|
96
102
|
onRefresherRefresh: async () => {
|
|
97
|
-
var _a;
|
|
103
|
+
var _a, _b;
|
|
98
104
|
if (!isControlled)
|
|
99
105
|
setInternalRefreshing(true);
|
|
100
106
|
try {
|
|
101
107
|
await ((_a = config.onRefresherRefresh) === null || _a === void 0 ? void 0 : _a.call(config));
|
|
102
108
|
}
|
|
109
|
+
catch (_c) {
|
|
110
|
+
// 原生不派发 Failed(4),补发便于与 H5 一致
|
|
111
|
+
(_b = config.onRefresherStatusChange) === null || _b === void 0 ? void 0 : _b.call(config, { detail: { status: 4 /* RefreshStatus.Failed */, dy: 0 } });
|
|
112
|
+
}
|
|
103
113
|
finally {
|
|
104
114
|
if (!isControlled)
|
|
105
115
|
setInternalRefreshing(false);
|
|
@@ -115,11 +125,7 @@ getIsTouchInListArea) {
|
|
|
115
125
|
(_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
126
|
},
|
|
117
127
|
} : {};
|
|
118
|
-
// ========================================
|
|
119
|
-
// H5:单一结构 + 原生 touch 监听(passive: false);refresherEnabled=false 时不挂
|
|
120
|
-
// ========================================
|
|
121
128
|
const addImperativeTouchListeners = React.useCallback((el) => {
|
|
122
|
-
var _a, _b;
|
|
123
129
|
if (supportsNativeRefresher || !configRef.current || !h5RefresherEnabled)
|
|
124
130
|
return () => { };
|
|
125
131
|
const scrollEl = el;
|
|
@@ -134,7 +140,6 @@ getIsTouchInListArea) {
|
|
|
134
140
|
/** 用于 onTouchEnd 判断是否触发刷新;刷新完成后必须置 0,否则下次点击(无 touchMove)会误用上次的 lastPull 再次触发 */
|
|
135
141
|
const lastPullRef = { current: 0 };
|
|
136
142
|
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
143
|
const setPull = (v) => {
|
|
139
144
|
lastPull = v;
|
|
140
145
|
lastPullRef.current = v;
|
|
@@ -143,9 +148,9 @@ getIsTouchInListArea) {
|
|
|
143
148
|
const runBounceBack = (fromValue, onComplete) => {
|
|
144
149
|
if (rafRef.current != null)
|
|
145
150
|
cancelAnimationFrame(rafRef.current);
|
|
146
|
-
const startTime =
|
|
151
|
+
const startTime = nowMs();
|
|
147
152
|
const animate = () => {
|
|
148
|
-
const t = Math.min((
|
|
153
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
149
154
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
150
155
|
const v = fromValue * (1 - ease);
|
|
151
156
|
setPullDistanceRef.current(v);
|
|
@@ -156,7 +161,6 @@ getIsTouchInListArea) {
|
|
|
156
161
|
}
|
|
157
162
|
else {
|
|
158
163
|
setPullDistanceRef.current(0);
|
|
159
|
-
// 修复闭包问题:使用 ref 获取最新值
|
|
160
164
|
if (!isControlledRef.current)
|
|
161
165
|
setInternalRefreshingRef.current(false);
|
|
162
166
|
rafRef.current = null;
|
|
@@ -169,10 +173,10 @@ getIsTouchInListArea) {
|
|
|
169
173
|
const runBounceBackAbort = (fromValue) => {
|
|
170
174
|
if (rafRef.current != null)
|
|
171
175
|
cancelAnimationFrame(rafRef.current);
|
|
172
|
-
const startTime =
|
|
176
|
+
const startTime = nowMs();
|
|
173
177
|
const animate = () => {
|
|
174
178
|
var _a, _b;
|
|
175
|
-
const t = Math.min((
|
|
179
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
176
180
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
177
181
|
const v = fromValue * (1 - ease);
|
|
178
182
|
setPullDistanceRef.current(v);
|
|
@@ -186,19 +190,18 @@ getIsTouchInListArea) {
|
|
|
186
190
|
lastPullRef.current = 0;
|
|
187
191
|
lastPull = 0;
|
|
188
192
|
(_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherAbort) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
189
|
-
// 状态变化:回到 Idle
|
|
190
193
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
191
194
|
}
|
|
192
195
|
};
|
|
193
196
|
rafRef.current = requestAnimationFrame(animate);
|
|
194
197
|
};
|
|
195
|
-
/**
|
|
198
|
+
/** 收起到加载高度后再执行 onRefresherRefresh */
|
|
196
199
|
const runBounceToLoading = (fromValue, toValue, onReach) => {
|
|
197
200
|
if (rafRef.current != null)
|
|
198
201
|
cancelAnimationFrame(rafRef.current);
|
|
199
|
-
const startTime =
|
|
202
|
+
const startTime = nowMs();
|
|
200
203
|
const animate = () => {
|
|
201
|
-
const t = Math.min((
|
|
204
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
202
205
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
203
206
|
setPullDistanceRef.current(fromValue + (toValue - fromValue) * ease);
|
|
204
207
|
if (t < 1) {
|
|
@@ -292,8 +295,11 @@ getIsTouchInListArea) {
|
|
|
292
295
|
return;
|
|
293
296
|
}
|
|
294
297
|
lastY.current = currentY;
|
|
295
|
-
const
|
|
296
|
-
const
|
|
298
|
+
const vh = getViewportHeightPx();
|
|
299
|
+
const maxPull = vh * H5_PULL_DISTANCE_MAX_VH;
|
|
300
|
+
const dampingLimit = vh * H5_DAMPING_LIMIT_VH;
|
|
301
|
+
const damped = dampIncrement(dy, totalMove, lastPull, vh, dampingLimit);
|
|
302
|
+
const next = Math.min(lastPull + damped, maxPull);
|
|
297
303
|
setPull(next);
|
|
298
304
|
(_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
305
|
if (next >= thresholdRef.current) {
|
|
@@ -314,7 +320,6 @@ getIsTouchInListArea) {
|
|
|
314
320
|
pullAtReleaseRef.current = pullValue;
|
|
315
321
|
if (!isControlledRef.current)
|
|
316
322
|
setInternalRefreshingRef.current(true);
|
|
317
|
-
// 状态变化:Refreshing
|
|
318
323
|
emitStatusChangeRef.current(2 /* RefreshStatus.Refreshing */, pullValue);
|
|
319
324
|
runBounceToLoading(pullValue, height, () => {
|
|
320
325
|
var _a, _b;
|
|
@@ -328,30 +333,25 @@ getIsTouchInListArea) {
|
|
|
328
333
|
lastPull = 0;
|
|
329
334
|
pullAtReleaseRef.current = 0;
|
|
330
335
|
(_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherRestore) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
331
|
-
// 状态变化:回到 Idle
|
|
332
336
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
333
337
|
};
|
|
334
|
-
const timeoutId = setTimeout(safeReset, BOUNCE_MS + 400);
|
|
335
338
|
Promise.resolve((_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherRefresh) === null || _b === void 0 ? void 0 : _b.call(_a))
|
|
336
339
|
.then(() => {
|
|
337
|
-
// 若已经通过 safeReset 提前结束刷新(例如超时),则不再执行回弹动画,避免二次「吐舌头」闪现
|
|
338
340
|
if (!refreshingLockRef.current)
|
|
339
341
|
return;
|
|
340
|
-
// 状态变化:Completed
|
|
341
342
|
emitStatusChangeRef.current(3 /* RefreshStatus.Completed */, DEFAULT_REFRESHER_HEIGHT);
|
|
342
343
|
requestAnimationFrame(() => runBounceBack(DEFAULT_REFRESHER_HEIGHT, () => {
|
|
343
344
|
var _a, _b;
|
|
344
|
-
clearTimeout(timeoutId);
|
|
345
345
|
refreshingLockRef.current = false;
|
|
346
346
|
lastPullRef.current = 0;
|
|
347
347
|
lastPull = 0;
|
|
348
348
|
pullAtReleaseRef.current = 0;
|
|
349
349
|
(_b = (_a = configRef.current) === null || _a === void 0 ? void 0 : _a.onRefresherRestore) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
350
|
-
// 状态变化:回到 Idle
|
|
351
350
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
352
351
|
}));
|
|
353
352
|
}, () => {
|
|
354
|
-
|
|
353
|
+
emitStatusChangeRef.current(4 /* RefreshStatus.Failed */, pullValue);
|
|
354
|
+
emitStatusChangeRef.current(3 /* RefreshStatus.Completed */, DEFAULT_REFRESHER_HEIGHT);
|
|
355
355
|
safeReset();
|
|
356
356
|
});
|
|
357
357
|
});
|
|
@@ -434,7 +434,7 @@ getIsTouchInListArea) {
|
|
|
434
434
|
background,
|
|
435
435
|
}, children: hasCustomChildren ? config.children : (defaultText ? jsx(View, { style: textColor ? { color: textColor } : undefined, children: defaultText }) : null) }));
|
|
436
436
|
}, [config, pullDistance, isRefreshing]);
|
|
437
|
-
|
|
437
|
+
/** 受控 refresherTriggered=true:无下拉也显示加载条 */
|
|
438
438
|
React.useEffect(() => {
|
|
439
439
|
if (!isControlled || !config || config.refresherTriggered !== true)
|
|
440
440
|
return;
|
|
@@ -442,10 +442,9 @@ getIsTouchInListArea) {
|
|
|
442
442
|
return;
|
|
443
443
|
setPullDistanceRef.current(DEFAULT_REFRESHER_HEIGHT);
|
|
444
444
|
pullDistanceRef.current = DEFAULT_REFRESHER_HEIGHT;
|
|
445
|
-
// 触发 Refreshing 状态变化
|
|
446
445
|
emitStatusChangeRef.current(2 /* RefreshStatus.Refreshing */, DEFAULT_REFRESHER_HEIGHT);
|
|
447
446
|
}, [isControlled, config === null || config === void 0 ? void 0 : config.refresherTriggered]);
|
|
448
|
-
|
|
447
|
+
/** 受控 refresherTriggered=false:回弹清零;与 touch 共用 rafRef,cancel 时须在此释放 refreshingLockRef */
|
|
449
448
|
const prevTriggeredRef = useRef(config === null || config === void 0 ? void 0 : config.refresherTriggered);
|
|
450
449
|
React.useEffect(() => {
|
|
451
450
|
if (!isControlled || !config || config.refresherTriggered !== false) {
|
|
@@ -458,12 +457,11 @@ getIsTouchInListArea) {
|
|
|
458
457
|
if (rafRef.current != null)
|
|
459
458
|
cancelAnimationFrame(rafRef.current);
|
|
460
459
|
const from = pullDistanceRef.current > 0 ? pullDistanceRef.current : DEFAULT_REFRESHER_HEIGHT;
|
|
461
|
-
// 触发 Completed 状态变化
|
|
462
460
|
emitStatusChangeRef.current(3 /* RefreshStatus.Completed */, from);
|
|
463
|
-
const startTime =
|
|
461
|
+
const startTime = nowMs();
|
|
464
462
|
const animate = () => {
|
|
465
463
|
var _a;
|
|
466
|
-
const t = Math.min((
|
|
464
|
+
const t = Math.min((nowMs() - startTime) / BOUNCE_MS, 1);
|
|
467
465
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
468
466
|
const v = from * (1 - ease);
|
|
469
467
|
setPullDistanceRef.current(v);
|
|
@@ -473,12 +471,11 @@ getIsTouchInListArea) {
|
|
|
473
471
|
else {
|
|
474
472
|
setPullDistanceRef.current(0);
|
|
475
473
|
pullDistanceRef.current = 0;
|
|
476
|
-
// 修复闭包问题:使用 ref 获取最新值
|
|
477
474
|
if (!isControlledRef.current)
|
|
478
475
|
setInternalRefreshingRef.current(false);
|
|
479
476
|
rafRef.current = null;
|
|
477
|
+
refreshingLockRef.current = false;
|
|
480
478
|
(_a = config.onRefresherRestore) === null || _a === void 0 ? void 0 : _a.call(config);
|
|
481
|
-
// 触发 Idle 状态变化
|
|
482
479
|
emitStatusChangeRef.current(0 /* RefreshStatus.Idle */, 0);
|
|
483
480
|
}
|
|
484
481
|
};
|