@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.
@@ -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":"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"}
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 instance = Taro.getCurrentInstance();
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 instance = Taro.getCurrentInstance();
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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"}
@@ -10,4 +10,5 @@ export declare function useMeasureStartOffsetWeapp(scrollElRef: RefObject<HTMLEl
10
10
  enabled: boolean;
11
11
  isHorizontal?: boolean;
12
12
  startOffsetRef?: MutableRefObject<number>;
13
+ selectorQueryScope?: object;
13
14
  }): number;
@@ -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 instance = Taro.getCurrentInstance();
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 Taro from '@tarojs/taro'\nimport { useEffect, useRef, useState } from 'react'\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> }\n): number {\n const { enabled, isHorizontal = false, startOffsetRef } = 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 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 .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])\n\n return measuredStartOffset\n}\n"],"names":[],"mappings":"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"}
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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"}
@@ -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 {
@@ -4,31 +4,41 @@ import React, { useState, useRef, useCallback } from 'react';
4
4
  import { supportsNativeRefresher } from '../utils.js';
5
5
 
6
6
  /**
7
- * useRefresher - 平台适配的下拉刷新
7
+ * 下拉刷新(List 内部)
8
8
  *
9
- * - 小程序:weapp / jd / tt 使用 ScrollView 原生 refresher-*
10
- * - H5:对齐 taro-components-react pull-down-refresh(2.x 同源)逻辑:触顶 + 增量拖拽 + 阻尼 + 释放保持高度再回弹
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
- /** 最大下拉距离(px) */
15
- const DISTANCE_Y_MAX_LIMIT = 150;
16
- /** 阻尼系数:超过此值不再累加位移,与 2.x default damping 一致 */
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
- * 单次位移的阻尼(与 2.x PullDownRefresh.damping 一致)
24
- * ratio = 已拖拽总距离 / 屏幕高度,damped = diff * (1 - ratio) * 0.6;且当已超过 DAMPING 时不再加
25
- */
26
- function dampIncrement(diff, totalMove, currentPull, screenHeight) {
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 >= DAMPING)
39
+ if (currentPull >= dampingLimit)
30
40
  return 0;
31
- const ratio = Math.min(totalMove / screenHeight, 1);
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 = performance.now();
151
+ const startTime = nowMs();
147
152
  const animate = () => {
148
- const t = Math.min((performance.now() - startTime) / BOUNCE_MS, 1);
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 = performance.now();
176
+ const startTime = nowMs();
173
177
  const animate = () => {
174
178
  var _a, _b;
175
- const t = Math.min((performance.now() - startTime) / BOUNCE_MS, 1);
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
- /** 先动画到 refresherHeight(加载中保持高度),再执行刷新,完成后回弹;与 2.x release 时 setContentStyle(distanceToRefresh+1) 一致 */
198
+ /** 收起到加载高度后再执行 onRefresherRefresh */
196
199
  const runBounceToLoading = (fromValue, toValue, onReach) => {
197
200
  if (rafRef.current != null)
198
201
  cancelAnimationFrame(rafRef.current);
199
- const startTime = performance.now();
202
+ const startTime = nowMs();
200
203
  const animate = () => {
201
- const t = Math.min((performance.now() - startTime) / BOUNCE_MS, 1);
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 damped = dampIncrement(dy, totalMove, lastPull, screenHeight);
296
- const next = Math.min(lastPull + damped, DISTANCE_Y_MAX_LIMIT);
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
- clearTimeout(timeoutId);
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
- // 受控 refresherTriggered:设为 true 时立即展示顶部加载指示器(对齐小程序:无需下拉即显示、固定在顶部)
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
- // 受控 refresherTriggered:父组件设为 false 时同步回弹并清零
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 = performance.now();
461
+ const startTime = nowMs();
464
462
  const animate = () => {
465
463
  var _a;
466
- const t = Math.min((performance.now() - startTime) / BOUNCE_MS, 1);
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
  };