@tarojs/components-advanced 4.1.12-beta.7 → 4.1.12-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/index.js +2 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/list/hooks/useListNestedScroll.d.ts +13 -0
- package/dist/components/list/hooks/useListNestedScroll.js +46 -0
- package/dist/components/list/hooks/useListNestedScroll.js.map +1 -0
- package/dist/components/list/hooks/useListScrollElementAttach.d.ts +21 -0
- package/dist/components/list/hooks/useListScrollElementAttach.js +86 -0
- package/dist/components/list/hooks/useListScrollElementAttach.js.map +1 -0
- package/dist/components/list/hooks/useMeasureStartOffset.d.ts +4 -2
- package/dist/components/list/hooks/useMeasureStartOffset.js +56 -15
- package/dist/components/list/hooks/useMeasureStartOffset.js.map +1 -1
- package/dist/components/list/hooks/useScrollParentAutoFind.d.ts +17 -0
- package/dist/components/list/hooks/useScrollParentAutoFind.js +54 -0
- package/dist/components/list/hooks/useScrollParentAutoFind.js.map +1 -0
- package/dist/components/list/index.d.ts +7 -12
- package/dist/components/list/index.js +53 -119
- package/dist/components/list/index.js.map +1 -1
- package/dist/components/water-flow/interface.d.ts +3 -3
- package/dist/components/water-flow/root.d.ts +8 -0
- package/dist/components/water-flow/root.js +33 -17
- package/dist/components/water-flow/root.js.map +1 -1
- package/dist/components/water-flow/section.d.ts +1 -1
- package/dist/components/water-flow/section.js +2 -2
- package/dist/components/water-flow/section.js.map +1 -1
- package/dist/components/water-flow/water-flow.d.ts +1 -0
- package/dist/components/water-flow/water-flow.js +79 -43
- package/dist/components/water-flow/water-flow.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/scrollParent.d.ts +22 -0
- package/dist/utils/scrollParent.js +55 -0
- package/dist/utils/scrollParent.js.map +1 -0
- package/package.json +9 -9
package/dist/components/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export { List,
|
|
1
|
+
export { List, accumulate, isShaking } from './list/index.js';
|
|
2
2
|
export { VirtualList } from './virtual-list/index.js';
|
|
3
3
|
export { VirtualWaterfall } from './virtual-waterfall/index.js';
|
|
4
4
|
import './water-flow/index.js';
|
|
5
|
+
export { ScrollElementContext as ListScrollElementContext } from '@tarojs/components-react';
|
|
5
6
|
export { ListItem } from './list/ListItem.js';
|
|
6
7
|
export { NoMore } from './list/NoMore.js';
|
|
7
8
|
export { StickyHeader } from './list/StickyHeader.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
export interface UseListNestedScrollResult {
|
|
3
|
+
effectiveScrollElement: RefObject<HTMLElement | null> | null;
|
|
4
|
+
effectiveStartOffset: number;
|
|
5
|
+
useScrollElementMode: boolean;
|
|
6
|
+
needAutoFind: boolean;
|
|
7
|
+
autoFindStatus: 'pending' | 'found' | 'not-found';
|
|
8
|
+
contentWrapperRef: RefObject<HTMLDivElement | null>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 合并嵌套滚动相关逻辑:Context、自动查找、startOffset 测量、scrollElement 解析
|
|
12
|
+
*/
|
|
13
|
+
export declare function useListNestedScroll(listType: 'default' | 'nested', scrollElement?: RefObject<HTMLElement | null>, startOffsetProp?: number, isHorizontal?: boolean): UseListNestedScrollResult;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ScrollElementContext } from '@tarojs/components-react';
|
|
2
|
+
import { useRef, useContext } from 'react';
|
|
3
|
+
import { isH5 } from '../utils.js';
|
|
4
|
+
import { useMeasureStartOffset } from './useMeasureStartOffset.js';
|
|
5
|
+
import { useScrollParentAutoFind } from './useScrollParentAutoFind.js';
|
|
6
|
+
|
|
7
|
+
const EMPTY_SCROLL_REF = { current: null };
|
|
8
|
+
/**
|
|
9
|
+
* 合并嵌套滚动相关逻辑:Context、自动查找、startOffset 测量、scrollElement 解析
|
|
10
|
+
*/
|
|
11
|
+
function useListNestedScroll(listType, scrollElement, startOffsetProp, isHorizontal = false) {
|
|
12
|
+
var _a, _b, _c;
|
|
13
|
+
const contentWrapperRef = useRef(null);
|
|
14
|
+
const scrollElementCtx = useContext(ScrollElementContext);
|
|
15
|
+
const ctxStart = scrollElementCtx === null || scrollElementCtx === void 0 ? void 0 : scrollElementCtx.startOffset;
|
|
16
|
+
const hasExplicitCtxStartOffset = ctxStart != null && ctxStart > 0;
|
|
17
|
+
const needAutoFind = listType === 'nested' &&
|
|
18
|
+
!scrollElement &&
|
|
19
|
+
!(scrollElementCtx === null || scrollElementCtx === void 0 ? void 0 : scrollElementCtx.scrollRef) &&
|
|
20
|
+
isH5;
|
|
21
|
+
const { scrollParentRef: autoFoundRef, status: autoFindStatus } = useScrollParentAutoFind(contentWrapperRef, { enabled: !!needAutoFind, isHorizontal });
|
|
22
|
+
const effectiveScrollElement = (_a = scrollElement !== null && scrollElement !== void 0 ? scrollElement : scrollElementCtx === null || scrollElementCtx === void 0 ? void 0 : scrollElementCtx.scrollRef) !== null && _a !== void 0 ? _a : (needAutoFind && autoFindStatus === 'found' ? autoFoundRef : null);
|
|
23
|
+
const needMeasure = listType === 'nested' &&
|
|
24
|
+
effectiveScrollElement &&
|
|
25
|
+
isH5 &&
|
|
26
|
+
startOffsetProp == null &&
|
|
27
|
+
!hasExplicitCtxStartOffset;
|
|
28
|
+
const measuredStartOffset = useMeasureStartOffset(effectiveScrollElement !== null && effectiveScrollElement !== void 0 ? effectiveScrollElement : EMPTY_SCROLL_REF, contentWrapperRef, { enabled: !!needMeasure, isHorizontal });
|
|
29
|
+
const effectiveStartOffset = (_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 : 0;
|
|
30
|
+
const useScrollElementMode = listType === 'nested' && !!(effectiveScrollElement && isH5);
|
|
31
|
+
if (listType === 'nested' && !effectiveScrollElement && isH5 && autoFindStatus === 'not-found') {
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.warn('[List] nestedScroll 模式但无 scrollElement(props/Context/自动查找),回退为 default');
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
effectiveScrollElement,
|
|
37
|
+
effectiveStartOffset,
|
|
38
|
+
useScrollElementMode,
|
|
39
|
+
needAutoFind,
|
|
40
|
+
autoFindStatus,
|
|
41
|
+
contentWrapperRef,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { useListNestedScroll };
|
|
46
|
+
//# sourceMappingURL=useListNestedScroll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useListNestedScroll.js","sources":["../../../../src/components/list/hooks/useListNestedScroll.ts"],"sourcesContent":["import { type ScrollElementContextValue, ScrollElementContext } from '@tarojs/components-react'\nimport { useContext, useRef } from 'react'\n\nimport { isH5 } from '../utils'\nimport { useMeasureStartOffset } from './useMeasureStartOffset'\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 useScrollElementMode: boolean\n needAutoFind: boolean\n autoFindStatus: 'pending' | 'found' | 'not-found'\n contentWrapperRef: RefObject<HTMLDivElement | null>\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 scrollElementCtx = useContext(ScrollElementContext) as ScrollElementContextValue | 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\n const { scrollParentRef: autoFoundRef, status: autoFindStatus } = useScrollParentAutoFind(\n contentWrapperRef,\n { enabled: !!needAutoFind, isHorizontal }\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 measuredStartOffset = useMeasureStartOffset(\n effectiveScrollElement ?? EMPTY_SCROLL_REF,\n contentWrapperRef,\n { enabled: !!needMeasure, isHorizontal }\n )\n\n const effectiveStartOffset =\n startOffsetProp ??\n (ctxStart != null && ctxStart > 0 ? ctxStart : null) ??\n measuredStartOffset ??\n 0\n\n const useScrollElementMode = listType === 'nested' && !!(effectiveScrollElement && isH5)\n\n if (listType === 'nested' && !effectiveScrollElement && isH5 && 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 useScrollElementMode,\n needAutoFind,\n autoFindStatus,\n contentWrapperRef,\n }\n}\n"],"names":[],"mappings":";;;;;;AASA,MAAM,gBAAgB,GAAG,EAAE,OAAO,EAAE,IAA0B,EAAE;AAWhE;;AAEG;AACG,SAAU,mBAAmB,CACjC,QAA8B,EAC9B,aAA6C,EAC7C,eAAwB,EACxB,YAAA,GAAwB,KAAK,EAAA;;AAE7B,IAAA,MAAM,iBAAiB,GAAG,MAAM,CAAiB,IAAI,CAAC;AACtD,IAAA,MAAM,gBAAgB,GAAG,UAAU,CAAC,oBAAoB,CAAqC;IAC7F,MAAM,QAAQ,GAAG,gBAAgB,KAAA,IAAA,IAAhB,gBAAgB,KAAhB,KAAA,CAAA,GAAA,KAAA,CAAA,GAAA,gBAAgB,CAAE,WAAW;IAC9C,MAAM,yBAAyB,GAAG,QAAQ,IAAI,IAAI,IAAI,QAAQ,GAAG,CAAC;AAElE,IAAA,MAAM,YAAY,GAChB,QAAQ,KAAK,QAAQ;AACrB,QAAA,CAAC,aAAa;QACd,EAAC,gBAAgB,KAAhB,IAAA,IAAA,gBAAgB,uBAAhB,gBAAgB,CAAE,SAAS,CAAA;AAC5B,QAAA,IAAI;IACN,MAAM,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,uBAAuB,CACvF,iBAAiB,EACjB,EAAE,OAAO,EAAE,CAAC,CAAC,YAAY,EAAE,YAAY,EAAE,CAC1C;AAED,IAAA,MAAM,sBAAsB,GAC1B,CAAA,EAAA,GAAA,aAAa,aAAb,aAAa,KAAA,KAAA,CAAA,GAAb,aAAa,GACb,gBAAgB,KAAA,IAAA,IAAhB,gBAAgB,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAhB,gBAAgB,CAAE,SAAS,MAC3B,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,IAAC,YAAY,IAAI,cAAc,KAAK,OAAO,GAAG,YAAY,GAAG,IAAI,CAAC;AAEpE,IAAA,MAAM,WAAW,GACf,QAAQ,KAAK,QAAQ;QACrB,sBAAsB;QACtB,IAAI;AACJ,QAAA,eAAe,IAAI,IAAI;AACvB,QAAA,CAAC,yBAAyB;IAC5B,MAAM,mBAAmB,GAAG,qBAAqB,CAC/C,sBAAsB,aAAtB,sBAAsB,KAAA,KAAA,CAAA,GAAtB,sBAAsB,GAAI,gBAAgB,EAC1C,iBAAiB,EACjB,EAAE,OAAO,EAAE,CAAC,CAAC,WAAW,EAAE,YAAY,EAAE,CACzC;AAED,IAAA,MAAM,oBAAoB,GACxB,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,eAAe,KAAA,IAAA,IAAf,eAAe,KAAA,KAAA,CAAA,GAAf,eAAe,IACd,QAAQ,IAAI,IAAI,IAAI,QAAQ,GAAG,CAAC,GAAG,QAAQ,GAAG,IAAI,CAAC,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,GACpD,mBAAmB,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,GACnB,CAAC;AAEH,IAAA,MAAM,oBAAoB,GAAG,QAAQ,KAAK,QAAQ,IAAI,CAAC,EAAE,sBAAsB,IAAI,IAAI,CAAC;AAExF,IAAA,IAAI,QAAQ,KAAK,QAAQ,IAAI,CAAC,sBAAsB,IAAI,IAAI,IAAI,cAAc,KAAK,WAAW,EAAE;;AAE9F,QAAA,OAAO,CAAC,IAAI,CAAC,wEAAwE,CAAC;;IAGxF,OAAO;QACL,sBAAsB;QACtB,oBAAoB;QACpB,oBAAoB;QACpB,YAAY;QACZ,cAAc;QACd,iBAAiB;KAClB;AACH;;;;"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
export interface ListScrollElementAttachRefs {
|
|
3
|
+
scrollCorrection: {
|
|
4
|
+
markUserScrolling: () => void;
|
|
5
|
+
};
|
|
6
|
+
onScroll: ((e: {
|
|
7
|
+
scrollTop: number;
|
|
8
|
+
scrollLeft: number;
|
|
9
|
+
}) => void) | undefined;
|
|
10
|
+
onScrollToUpper: (() => void) | undefined;
|
|
11
|
+
onScrollToLower: (() => void) | undefined;
|
|
12
|
+
threshold: {
|
|
13
|
+
upper: number;
|
|
14
|
+
lower: number;
|
|
15
|
+
};
|
|
16
|
+
listContentLength: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* scrollElement 模式下:监听外部滚动驱动 renderOffset,并从 scrollElement 获取 containerLength
|
|
20
|
+
*/
|
|
21
|
+
export declare function useListScrollElementAttach(enabled: boolean, effectiveScrollElement: RefObject<HTMLElement | null> | null, 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>): void;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useRef, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scrollElement 模式下:监听外部滚动驱动 renderOffset,并从 scrollElement 获取 containerLength
|
|
5
|
+
*/
|
|
6
|
+
function useListScrollElementAttach(enabled, effectiveScrollElement, effectiveStartOffset, isHorizontal, setContainerLength, updateRenderOffset, scrollRefProp, refsRef) {
|
|
7
|
+
const effectiveStartOffsetRef = useRef(effectiveStartOffset);
|
|
8
|
+
effectiveStartOffsetRef.current = effectiveStartOffset;
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!enabled || !effectiveScrollElement)
|
|
11
|
+
return;
|
|
12
|
+
const el = effectiveScrollElement.current;
|
|
13
|
+
if (!el || typeof ResizeObserver === 'undefined')
|
|
14
|
+
return;
|
|
15
|
+
const update = () => {
|
|
16
|
+
const measured = isHorizontal ? el.clientWidth : el.clientHeight;
|
|
17
|
+
if (measured > 0)
|
|
18
|
+
setContainerLength(measured);
|
|
19
|
+
if (scrollRefProp)
|
|
20
|
+
scrollRefProp.current = el;
|
|
21
|
+
};
|
|
22
|
+
update();
|
|
23
|
+
const ro = new ResizeObserver(update);
|
|
24
|
+
ro.observe(el);
|
|
25
|
+
return () => ro.disconnect();
|
|
26
|
+
}, [enabled, effectiveScrollElement, isHorizontal, scrollRefProp, setContainerLength]);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!enabled || !effectiveScrollElement)
|
|
29
|
+
return;
|
|
30
|
+
let cancelled = false;
|
|
31
|
+
let teardown = null;
|
|
32
|
+
const maxRetries = 20;
|
|
33
|
+
const tryAttach = (retryCount = 0) => {
|
|
34
|
+
if (cancelled)
|
|
35
|
+
return;
|
|
36
|
+
const target = effectiveScrollElement.current;
|
|
37
|
+
if (!target) {
|
|
38
|
+
if (retryCount < maxRetries) {
|
|
39
|
+
requestAnimationFrame(() => tryAttach(retryCount + 1));
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const refs = refsRef.current;
|
|
44
|
+
if (!refs)
|
|
45
|
+
return;
|
|
46
|
+
let inUpperZone = true;
|
|
47
|
+
let inLowerZone = false;
|
|
48
|
+
const handler = () => {
|
|
49
|
+
var _a, _b, _c;
|
|
50
|
+
const r = refsRef.current;
|
|
51
|
+
if (!r)
|
|
52
|
+
return;
|
|
53
|
+
const scrollPos = isHorizontal ? target.scrollLeft : target.scrollTop;
|
|
54
|
+
const adjustedPos = scrollPos - effectiveStartOffsetRef.current;
|
|
55
|
+
const effectiveAdjusted = Math.max(0, adjustedPos);
|
|
56
|
+
const clientSize = isHorizontal ? target.clientWidth : target.clientHeight;
|
|
57
|
+
r.scrollCorrection.markUserScrolling();
|
|
58
|
+
updateRenderOffset(effectiveAdjusted, false, 'scrollElement');
|
|
59
|
+
(_a = r.onScroll) === null || _a === void 0 ? void 0 : _a.call(r, { scrollTop: isHorizontal ? 0 : scrollPos, scrollLeft: isHorizontal ? scrollPos : 0 });
|
|
60
|
+
const { upper, lower } = r.threshold;
|
|
61
|
+
const innerContentLen = r.listContentLength;
|
|
62
|
+
const nowInUpper = effectiveAdjusted <= upper;
|
|
63
|
+
const nowInLower = innerContentLen > 0 && effectiveAdjusted + clientSize >= innerContentLen - lower;
|
|
64
|
+
if (nowInUpper && !inUpperZone)
|
|
65
|
+
(_b = r.onScrollToUpper) === null || _b === void 0 ? void 0 : _b.call(r);
|
|
66
|
+
if (nowInLower && !inLowerZone)
|
|
67
|
+
(_c = r.onScrollToLower) === null || _c === void 0 ? void 0 : _c.call(r);
|
|
68
|
+
inUpperZone = nowInUpper;
|
|
69
|
+
inLowerZone = nowInLower;
|
|
70
|
+
};
|
|
71
|
+
const initialScroll = isHorizontal ? target.scrollLeft : target.scrollTop;
|
|
72
|
+
const initialAdjusted = Math.max(0, initialScroll - effectiveStartOffsetRef.current);
|
|
73
|
+
updateRenderOffset(initialAdjusted, false, 'scrollElement');
|
|
74
|
+
target.addEventListener('scroll', handler, { passive: true });
|
|
75
|
+
teardown = () => target.removeEventListener('scroll', handler);
|
|
76
|
+
};
|
|
77
|
+
tryAttach();
|
|
78
|
+
return () => {
|
|
79
|
+
cancelled = true;
|
|
80
|
+
teardown === null || teardown === void 0 ? void 0 : teardown();
|
|
81
|
+
};
|
|
82
|
+
}, [enabled, effectiveScrollElement, isHorizontal, effectiveStartOffset, updateRenderOffset, refsRef]);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export { useListScrollElementAttach };
|
|
86
|
+
//# sourceMappingURL=useListScrollElementAttach.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useListScrollElementAttach.js","sources":["../../../../src/components/list/hooks/useListScrollElementAttach.ts"],"sourcesContent":["import { useEffect, useRef } from 'react'\n\nimport type { RefObject } from 'react'\n\nexport interface ListScrollElementAttachRefs {\n scrollCorrection: { markUserScrolling: () => void }\n onScroll: ((e: { scrollTop: number, scrollLeft: number }) => void) | undefined\n onScrollToUpper: (() => void) | undefined\n onScrollToLower: (() => void) | undefined\n threshold: { upper: number, lower: number }\n listContentLength: number\n}\n\n/**\n * scrollElement 模式下:监听外部滚动驱动 renderOffset,并从 scrollElement 获取 containerLength\n */\nexport function useListScrollElementAttach(\n enabled: boolean,\n effectiveScrollElement: RefObject<HTMLElement | null> | null,\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 const effectiveStartOffsetRef = useRef(effectiveStartOffset)\n effectiveStartOffsetRef.current = effectiveStartOffset\n\n useEffect(() => {\n if (!enabled || !effectiveScrollElement) return\n const el = effectiveScrollElement.current\n if (!el || typeof ResizeObserver === 'undefined') return\n\n const update = () => {\n const measured = isHorizontal ? el.clientWidth : el.clientHeight\n if (measured > 0) setContainerLength(measured)\n if (scrollRefProp) scrollRefProp.current = el\n }\n update()\n const ro = new ResizeObserver(update)\n ro.observe(el)\n return () => ro.disconnect()\n }, [enabled, effectiveScrollElement, isHorizontal, scrollRefProp, setContainerLength])\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\n if (!target) {\n if (retryCount < maxRetries) {\n requestAnimationFrame(() => tryAttach(retryCount + 1))\n }\n return\n }\n\n const refs = refsRef.current\n if (!refs) return\n\n let inUpperZone = true\n let inLowerZone = false\n\n const handler = () => {\n const r = refsRef.current\n if (!r) return\n const scrollPos = isHorizontal ? target.scrollLeft : target.scrollTop\n const adjustedPos = scrollPos - effectiveStartOffsetRef.current\n const effectiveAdjusted = Math.max(0, adjustedPos)\n const clientSize = isHorizontal ? target.clientWidth : target.clientHeight\n r.scrollCorrection.markUserScrolling()\n updateRenderOffset(effectiveAdjusted, false, 'scrollElement')\n r.onScroll?.({ scrollTop: isHorizontal ? 0 : scrollPos, scrollLeft: isHorizontal ? scrollPos : 0 })\n\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 const initialScroll = isHorizontal ? target.scrollLeft : target.scrollTop\n const initialAdjusted = Math.max(0, initialScroll - effectiveStartOffsetRef.current)\n updateRenderOffset(initialAdjusted, false, 'scrollElement')\n\n target.addEventListener('scroll', handler, { passive: true })\n teardown = () => target.removeEventListener('scroll', handler)\n }\n\n tryAttach()\n return () => {\n cancelled = true\n teardown?.()\n }\n }, [enabled, effectiveScrollElement, isHorizontal, effectiveStartOffset, updateRenderOffset, refsRef])\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { RefObject } from 'react';
|
|
2
2
|
/**
|
|
3
3
|
* 测量 scrollElement 内 content 节点之前所有兄弟的高度/宽度之和,作为 startOffset。
|
|
4
|
-
* 用于 scrollElement 模式下无 Context
|
|
5
|
-
*
|
|
4
|
+
* 用于 scrollElement 模式下无 Context/props 时自动计算上方内容高度。
|
|
5
|
+
* - 直接子节点:累加前兄弟尺寸(更稳定)
|
|
6
|
+
* - 非直接子节点:getBoundingClientRect 回退,并 warning 建议传 startOffset
|
|
7
|
+
* - ref 未就绪时自动重试,确保绑定到最外层滚动容器后能正确测量
|
|
6
8
|
*/
|
|
7
9
|
export declare function useMeasureStartOffset(scrollElRef: RefObject<HTMLElement | null>, contentRef: RefObject<HTMLElement | null>, options: {
|
|
8
10
|
enabled: boolean;
|
|
@@ -1,41 +1,82 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
1
|
+
import { useState, useRef, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
+
const WARNED_NESTED = new WeakSet();
|
|
4
|
+
const MAX_RETRY = 20;
|
|
5
|
+
const RETRY_DELAY = 50;
|
|
3
6
|
/**
|
|
4
7
|
* 测量 scrollElement 内 content 节点之前所有兄弟的高度/宽度之和,作为 startOffset。
|
|
5
|
-
* 用于 scrollElement 模式下无 Context
|
|
6
|
-
*
|
|
8
|
+
* 用于 scrollElement 模式下无 Context/props 时自动计算上方内容高度。
|
|
9
|
+
* - 直接子节点:累加前兄弟尺寸(更稳定)
|
|
10
|
+
* - 非直接子节点:getBoundingClientRect 回退,并 warning 建议传 startOffset
|
|
11
|
+
* - ref 未就绪时自动重试,确保绑定到最外层滚动容器后能正确测量
|
|
7
12
|
*/
|
|
8
13
|
function useMeasureStartOffset(scrollElRef, contentRef, options) {
|
|
9
14
|
const { enabled, isHorizontal = false } = options;
|
|
10
15
|
const [measuredStartOffset, setMeasuredStartOffset] = useState(0);
|
|
16
|
+
const [retryTrigger, setRetryTrigger] = useState(0);
|
|
17
|
+
const retryCountRef = useRef(0);
|
|
11
18
|
useEffect(() => {
|
|
12
|
-
if (!enabled)
|
|
19
|
+
if (!enabled) {
|
|
20
|
+
retryCountRef.current = 0;
|
|
13
21
|
return;
|
|
22
|
+
}
|
|
14
23
|
const scrollEl = scrollElRef.current;
|
|
15
24
|
const contentEl = contentRef.current;
|
|
16
|
-
|
|
25
|
+
// ref 未就绪时重试(如 ScrollView 与 WaterFlow 异步挂载)
|
|
26
|
+
if (!scrollEl || !contentEl) {
|
|
27
|
+
if (retryCountRef.current < MAX_RETRY) {
|
|
28
|
+
retryCountRef.current += 1;
|
|
29
|
+
const id = setTimeout(() => setRetryTrigger((t) => t + 1), RETRY_DELAY);
|
|
30
|
+
return () => clearTimeout(id);
|
|
31
|
+
}
|
|
17
32
|
return;
|
|
33
|
+
}
|
|
34
|
+
retryCountRef.current = 0;
|
|
18
35
|
const measure = () => {
|
|
19
36
|
const el = contentRef.current;
|
|
20
|
-
|
|
37
|
+
const scroll = scrollElRef.current;
|
|
38
|
+
if (!el || !scroll)
|
|
21
39
|
return;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
prev
|
|
40
|
+
if (el.parentElement === scroll) {
|
|
41
|
+
// 直接子节点:累加前兄弟尺寸
|
|
42
|
+
let offset = 0;
|
|
43
|
+
let prev = el.previousElementSibling;
|
|
44
|
+
while (prev) {
|
|
45
|
+
offset += isHorizontal ? prev.offsetWidth : prev.offsetHeight;
|
|
46
|
+
prev = prev.previousElementSibling;
|
|
47
|
+
}
|
|
48
|
+
setMeasuredStartOffset((prevVal) => (Math.abs(prevVal - offset) < 1 ? prevVal : offset));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
// 非直接子节点:getBoundingClientRect 回退
|
|
52
|
+
const scrollRect = scroll.getBoundingClientRect();
|
|
53
|
+
const contentRect = el.getBoundingClientRect();
|
|
54
|
+
const offset = isHorizontal
|
|
55
|
+
? contentRect.left - scrollRect.left + scroll.scrollLeft
|
|
56
|
+
: contentRect.top - scrollRect.top + scroll.scrollTop;
|
|
57
|
+
const value = Math.max(0, offset);
|
|
58
|
+
setMeasuredStartOffset((prevVal) => (Math.abs(prevVal - value) < 1 ? prevVal : value));
|
|
59
|
+
if (!WARNED_NESTED.has(el)) {
|
|
60
|
+
WARNED_NESTED.add(el);
|
|
61
|
+
// eslint-disable-next-line no-console
|
|
62
|
+
console.warn('[useMeasureStartOffset] content 不是 scrollElement 的直接子节点,使用 getBoundingClientRect 测量。' +
|
|
63
|
+
'建议通过 props 或 Context 传入 startOffset 以获得更稳定的布局。');
|
|
27
64
|
}
|
|
28
|
-
setMeasuredStartOffset((prevVal) => (Math.abs(prevVal - offset) < 1 ? prevVal : offset));
|
|
29
65
|
};
|
|
30
66
|
measure();
|
|
67
|
+
scrollEl.addEventListener('scroll', measure, { passive: true });
|
|
31
68
|
const ro = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(measure) : null;
|
|
32
69
|
if (ro) {
|
|
33
70
|
ro.observe(scrollEl);
|
|
34
|
-
// 同时观察 scrollEl 的直系子节点:图片加载、展开/收起等会改变兄弟高度,但 scrollEl 自身 clientHeight 不变,需观察子节点
|
|
35
71
|
Array.from(scrollEl.children).forEach((child) => ro.observe(child));
|
|
36
|
-
|
|
72
|
+
// 非直接子节点:content 自身或其祖先变化时也需重测
|
|
73
|
+
ro.observe(contentEl);
|
|
37
74
|
}
|
|
38
|
-
|
|
75
|
+
return () => {
|
|
76
|
+
scrollEl.removeEventListener('scroll', measure);
|
|
77
|
+
ro === null || ro === void 0 ? void 0 : ro.disconnect();
|
|
78
|
+
};
|
|
79
|
+
}, [enabled, scrollElRef, contentRef, isHorizontal, retryTrigger]);
|
|
39
80
|
return measuredStartOffset;
|
|
40
81
|
}
|
|
41
82
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMeasureStartOffset.js","sources":["../../../../src/components/list/hooks/useMeasureStartOffset.ts"],"sourcesContent":["import { useEffect, useState } from 'react'\n\nimport type { RefObject } from 'react'\n\n/**\n * 测量 scrollElement 内 content 节点之前所有兄弟的高度/宽度之和,作为 startOffset。\n * 用于 scrollElement 模式下无 Context
|
|
1
|
+
{"version":3,"file":"useMeasureStartOffset.js","sources":["../../../../src/components/list/hooks/useMeasureStartOffset.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\n\nimport type { RefObject } from 'react'\n\nconst WARNED_NESTED = new WeakSet<Element>()\nconst MAX_RETRY = 20\nconst RETRY_DELAY = 50\n\n/**\n * 测量 scrollElement 内 content 节点之前所有兄弟的高度/宽度之和,作为 startOffset。\n * 用于 scrollElement 模式下无 Context/props 时自动计算上方内容高度。\n * - 直接子节点:累加前兄弟尺寸(更稳定)\n * - 非直接子节点:getBoundingClientRect 回退,并 warning 建议传 startOffset\n * - ref 未就绪时自动重试,确保绑定到最外层滚动容器后能正确测量\n */\nexport function useMeasureStartOffset(\n scrollElRef: RefObject<HTMLElement | null>,\n contentRef: RefObject<HTMLElement | null>,\n options: { enabled: boolean, isHorizontal?: boolean }\n): number {\n const { enabled, isHorizontal = false } = options\n const [measuredStartOffset, setMeasuredStartOffset] = useState(0)\n const [retryTrigger, setRetryTrigger] = useState(0)\n const retryCountRef = useRef(0)\n\n useEffect(() => {\n if (!enabled) {\n retryCountRef.current = 0\n return\n }\n const scrollEl = scrollElRef.current\n const contentEl = contentRef.current\n\n // ref 未就绪时重试(如 ScrollView 与 WaterFlow 异步挂载)\n if (!scrollEl || !contentEl) {\n if (retryCountRef.current < MAX_RETRY) {\n retryCountRef.current += 1\n const id = setTimeout(() => setRetryTrigger((t) => t + 1), RETRY_DELAY)\n return () => clearTimeout(id)\n }\n return\n }\n retryCountRef.current = 0\n\n const measure = () => {\n const el = contentRef.current\n const scroll = scrollElRef.current\n if (!el || !scroll) return\n\n if (el.parentElement === scroll) {\n // 直接子节点:累加前兄弟尺寸\n let offset = 0\n let prev: Element | null = el.previousElementSibling\n while (prev) {\n offset += isHorizontal ? (prev as HTMLElement).offsetWidth : (prev as HTMLElement).offsetHeight\n prev = prev.previousElementSibling\n }\n setMeasuredStartOffset((prevVal) => (Math.abs(prevVal - offset) < 1 ? prevVal : offset))\n return\n }\n\n // 非直接子节点:getBoundingClientRect 回退\n const scrollRect = scroll.getBoundingClientRect()\n const contentRect = el.getBoundingClientRect()\n const offset = isHorizontal\n ? contentRect.left - scrollRect.left + scroll.scrollLeft\n : contentRect.top - scrollRect.top + scroll.scrollTop\n const value = Math.max(0, offset)\n setMeasuredStartOffset((prevVal) => (Math.abs(prevVal - value) < 1 ? prevVal : value))\n\n if (!WARNED_NESTED.has(el)) {\n WARNED_NESTED.add(el)\n // eslint-disable-next-line no-console\n console.warn(\n '[useMeasureStartOffset] content 不是 scrollElement 的直接子节点,使用 getBoundingClientRect 测量。' +\n '建议通过 props 或 Context 传入 startOffset 以获得更稳定的布局。'\n )\n }\n }\n\n measure()\n scrollEl.addEventListener('scroll', measure, { passive: true })\n const ro = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(measure) : null\n if (ro) {\n ro.observe(scrollEl)\n Array.from(scrollEl.children).forEach((child) => ro.observe(child))\n // 非直接子节点:content 自身或其祖先变化时也需重测\n ro.observe(contentEl)\n }\n return () => {\n scrollEl.removeEventListener('scroll', measure)\n ro?.disconnect()\n }\n }, [enabled, scrollElRef, contentRef, isHorizontal, retryTrigger])\n\n return measuredStartOffset\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
export type ScrollParentAutoFindStatus = 'pending' | 'found' | 'not-found';
|
|
3
|
+
/**
|
|
4
|
+
* 自动查找 content 元素的真实滚动父节点。
|
|
5
|
+
* 用于 nestedScroll 模式下无 scrollElement props/Context 时,作为第三优先级来源。
|
|
6
|
+
*
|
|
7
|
+
* - ref 未就绪时自动重试
|
|
8
|
+
* - 找到后通过 setState 触发 re-render,供 effectiveScrollElement 读取
|
|
9
|
+
* - status 用于 probe 阶段:pending=尚未完成,found=已找到,not-found=已尝试未找到
|
|
10
|
+
*/
|
|
11
|
+
export declare function useScrollParentAutoFind(contentRef: RefObject<HTMLElement | null>, options: {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
isHorizontal?: boolean;
|
|
14
|
+
}): {
|
|
15
|
+
scrollParentRef: RefObject<HTMLElement | null>;
|
|
16
|
+
status: ScrollParentAutoFindStatus;
|
|
17
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useRef, useState, useEffect } from 'react';
|
|
2
|
+
import { findScrollParent } from '../../../utils/scrollParent.js';
|
|
3
|
+
|
|
4
|
+
const MAX_RETRY = 20;
|
|
5
|
+
const RETRY_DELAY = 50;
|
|
6
|
+
/**
|
|
7
|
+
* 自动查找 content 元素的真实滚动父节点。
|
|
8
|
+
* 用于 nestedScroll 模式下无 scrollElement props/Context 时,作为第三优先级来源。
|
|
9
|
+
*
|
|
10
|
+
* - ref 未就绪时自动重试
|
|
11
|
+
* - 找到后通过 setState 触发 re-render,供 effectiveScrollElement 读取
|
|
12
|
+
* - status 用于 probe 阶段:pending=尚未完成,found=已找到,not-found=已尝试未找到
|
|
13
|
+
*/
|
|
14
|
+
function useScrollParentAutoFind(contentRef, options) {
|
|
15
|
+
const { enabled, isHorizontal = false } = options;
|
|
16
|
+
const scrollParentRef = useRef(null);
|
|
17
|
+
const [status, setStatus] = useState('pending');
|
|
18
|
+
const [retryTrigger, setRetryTrigger] = useState(0);
|
|
19
|
+
const retryCountRef = useRef(0);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!enabled) {
|
|
22
|
+
retryCountRef.current = 0;
|
|
23
|
+
scrollParentRef.current = null;
|
|
24
|
+
setStatus('pending');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const contentEl = contentRef.current;
|
|
28
|
+
if (!contentEl) {
|
|
29
|
+
if (retryCountRef.current < MAX_RETRY) {
|
|
30
|
+
retryCountRef.current += 1;
|
|
31
|
+
const id = setTimeout(() => setRetryTrigger((t) => t + 1), RETRY_DELAY);
|
|
32
|
+
return () => clearTimeout(id);
|
|
33
|
+
}
|
|
34
|
+
setStatus('not-found');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
retryCountRef.current = 0;
|
|
38
|
+
const found = findScrollParent(contentEl, !isHorizontal);
|
|
39
|
+
if (found) {
|
|
40
|
+
if (found !== scrollParentRef.current) {
|
|
41
|
+
scrollParentRef.current = found;
|
|
42
|
+
setStatus('found');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
scrollParentRef.current = null;
|
|
47
|
+
setStatus('not-found');
|
|
48
|
+
}
|
|
49
|
+
}, [enabled, contentRef, isHorizontal, retryTrigger]);
|
|
50
|
+
return { scrollParentRef, status };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { useScrollParentAutoFind };
|
|
54
|
+
//# sourceMappingURL=useScrollParentAutoFind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useScrollParentAutoFind.js","sources":["../../../../src/components/list/hooks/useScrollParentAutoFind.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\n\nimport { findScrollParent } from '../../../utils/scrollParent'\n\nimport type { RefObject } from 'react'\n\nconst MAX_RETRY = 20\nconst RETRY_DELAY = 50\n\nexport type ScrollParentAutoFindStatus = 'pending' | 'found' | 'not-found'\n\n/**\n * 自动查找 content 元素的真实滚动父节点。\n * 用于 nestedScroll 模式下无 scrollElement props/Context 时,作为第三优先级来源。\n *\n * - ref 未就绪时自动重试\n * - 找到后通过 setState 触发 re-render,供 effectiveScrollElement 读取\n * - status 用于 probe 阶段:pending=尚未完成,found=已找到,not-found=已尝试未找到\n */\nexport function useScrollParentAutoFind(\n contentRef: RefObject<HTMLElement | null>,\n options: { enabled: boolean, isHorizontal?: boolean }\n): { scrollParentRef: RefObject<HTMLElement | null>, status: ScrollParentAutoFindStatus } {\n const { enabled, isHorizontal = false } = options\n const scrollParentRef = useRef<HTMLElement | null>(null)\n const [status, setStatus] = useState<ScrollParentAutoFindStatus>('pending')\n const [retryTrigger, setRetryTrigger] = useState(0)\n const retryCountRef = useRef(0)\n\n useEffect(() => {\n if (!enabled) {\n retryCountRef.current = 0\n scrollParentRef.current = null\n setStatus('pending')\n return\n }\n\n const contentEl = contentRef.current\n if (!contentEl) {\n if (retryCountRef.current < MAX_RETRY) {\n retryCountRef.current += 1\n const id = setTimeout(() => setRetryTrigger((t) => t + 1), RETRY_DELAY)\n return () => clearTimeout(id)\n }\n setStatus('not-found')\n return\n }\n\n retryCountRef.current = 0\n const found = findScrollParent(contentEl, !isHorizontal)\n if (found) {\n if (found !== scrollParentRef.current) {\n scrollParentRef.current = found\n setStatus('found')\n }\n } else {\n scrollParentRef.current = null\n setStatus('not-found')\n }\n }, [enabled, contentRef, isHorizontal, retryTrigger])\n\n return { scrollParentRef, status }\n}\n"],"names":[],"mappings":";;;AAMA,MAAM,SAAS,GAAG,EAAE;AACpB,MAAM,WAAW,GAAG,EAAE;AAItB;;;;;;;AAOG;AACa,SAAA,uBAAuB,CACrC,UAAyC,EACzC,OAAqD,EAAA;IAErD,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,KAAK,EAAE,GAAG,OAAO;AACjD,IAAA,MAAM,eAAe,GAAG,MAAM,CAAqB,IAAI,CAAC;IACxD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAA6B,SAAS,CAAC;IAC3E,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;AACnD,IAAA,MAAM,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC;IAE/B,SAAS,CAAC,MAAK;QACb,IAAI,CAAC,OAAO,EAAE;AACZ,YAAA,aAAa,CAAC,OAAO,GAAG,CAAC;AACzB,YAAA,eAAe,CAAC,OAAO,GAAG,IAAI;YAC9B,SAAS,CAAC,SAAS,CAAC;YACpB;;AAGF,QAAA,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO;QACpC,IAAI,CAAC,SAAS,EAAE;AACd,YAAA,IAAI,aAAa,CAAC,OAAO,GAAG,SAAS,EAAE;AACrC,gBAAA,aAAa,CAAC,OAAO,IAAI,CAAC;gBAC1B,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC;AACvE,gBAAA,OAAO,MAAM,YAAY,CAAC,EAAE,CAAC;;YAE/B,SAAS,CAAC,WAAW,CAAC;YACtB;;AAGF,QAAA,aAAa,CAAC,OAAO,GAAG,CAAC;QACzB,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,EAAE,CAAC,YAAY,CAAC;QACxD,IAAI,KAAK,EAAE;AACT,YAAA,IAAI,KAAK,KAAK,eAAe,CAAC,OAAO,EAAE;AACrC,gBAAA,eAAe,CAAC,OAAO,GAAG,KAAK;gBAC/B,SAAS,CAAC,OAAO,CAAC;;;aAEf;AACL,YAAA,eAAe,CAAC,OAAO,GAAG,IAAI;YAC9B,SAAS,CAAC,WAAW,CAAC;;KAEzB,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;AAErD,IAAA,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE;AACpC;;;;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="@tarojs/components-react" />
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import ListItem from './ListItem';
|
|
3
4
|
import NoMore from './NoMore';
|
|
@@ -70,11 +71,11 @@ export interface ListProps {
|
|
|
70
71
|
dy?: number;
|
|
71
72
|
};
|
|
72
73
|
}) => void;
|
|
73
|
-
/**
|
|
74
|
-
|
|
75
|
-
/** 自定义滚动容器 ref,
|
|
74
|
+
/** 是否嵌套模式,与 plato 对齐;true=使用父级滚动,需配合 scrollElement 或 Context;不传或 false=default */
|
|
75
|
+
nestedScroll?: boolean;
|
|
76
|
+
/** 自定义滚动容器 ref,nestedScroll 模式下从 props 或 Context 获取 */
|
|
76
77
|
scrollElement?: React.RefObject<HTMLElement | null>;
|
|
77
|
-
/** 任务 2.3:暴露滚动容器 ref,供内层 List/WaterFlow(
|
|
78
|
+
/** 任务 2.3:暴露滚动容器 ref,供内层 List/WaterFlow(nestedScroll)使用 */
|
|
78
79
|
scrollRef?: React.MutableRefObject<HTMLElement | null>;
|
|
79
80
|
}
|
|
80
81
|
export interface ListHandle {
|
|
@@ -85,14 +86,8 @@ export interface ListHandle {
|
|
|
85
86
|
}
|
|
86
87
|
export declare function accumulate(arr: number[]): number[];
|
|
87
88
|
export declare function isShaking(diffList: number[]): boolean;
|
|
88
|
-
/**
|
|
89
|
-
export
|
|
90
|
-
scrollRef: React.MutableRefObject<HTMLElement | null>;
|
|
91
|
-
containerHeight: number;
|
|
92
|
-
startOffset: number;
|
|
93
|
-
/** 任务 4.2:内层 WaterFlow 报告 scrollHeight 时调用,List 会加上 header 估算后更新 sizeCache */
|
|
94
|
-
reportNestedHeightChange?: (scrollHeight: number) => void;
|
|
95
|
-
}>;
|
|
89
|
+
/** 向后兼容:ListScrollElementContext 已统一为 ScrollElementContext */
|
|
90
|
+
export { ScrollElementContext as ListScrollElementContext } from '@tarojs/components-react';
|
|
96
91
|
declare const List: React.ForwardRefExoticComponent<ListProps & React.RefAttributes<ListHandle>>;
|
|
97
92
|
export { List, ListItem, NoMore, StickyHeader, StickySection };
|
|
98
93
|
export default List;
|