@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.
Files changed (36) hide show
  1. package/dist/components/index.js +2 -1
  2. package/dist/components/index.js.map +1 -1
  3. package/dist/components/list/hooks/useListNestedScroll.d.ts +13 -0
  4. package/dist/components/list/hooks/useListNestedScroll.js +46 -0
  5. package/dist/components/list/hooks/useListNestedScroll.js.map +1 -0
  6. package/dist/components/list/hooks/useListScrollElementAttach.d.ts +21 -0
  7. package/dist/components/list/hooks/useListScrollElementAttach.js +86 -0
  8. package/dist/components/list/hooks/useListScrollElementAttach.js.map +1 -0
  9. package/dist/components/list/hooks/useMeasureStartOffset.d.ts +4 -2
  10. package/dist/components/list/hooks/useMeasureStartOffset.js +56 -15
  11. package/dist/components/list/hooks/useMeasureStartOffset.js.map +1 -1
  12. package/dist/components/list/hooks/useScrollParentAutoFind.d.ts +17 -0
  13. package/dist/components/list/hooks/useScrollParentAutoFind.js +54 -0
  14. package/dist/components/list/hooks/useScrollParentAutoFind.js.map +1 -0
  15. package/dist/components/list/index.d.ts +7 -12
  16. package/dist/components/list/index.js +53 -119
  17. package/dist/components/list/index.js.map +1 -1
  18. package/dist/components/water-flow/interface.d.ts +3 -3
  19. package/dist/components/water-flow/root.d.ts +8 -0
  20. package/dist/components/water-flow/root.js +33 -17
  21. package/dist/components/water-flow/root.js.map +1 -1
  22. package/dist/components/water-flow/section.d.ts +1 -1
  23. package/dist/components/water-flow/section.js +2 -2
  24. package/dist/components/water-flow/section.js.map +1 -1
  25. package/dist/components/water-flow/water-flow.d.ts +1 -0
  26. package/dist/components/water-flow/water-flow.js +79 -43
  27. package/dist/components/water-flow/water-flow.js.map +1 -1
  28. package/dist/index.js +3 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/utils/index.d.ts +1 -0
  31. package/dist/utils/index.js +1 -0
  32. package/dist/utils/index.js.map +1 -1
  33. package/dist/utils/scrollParent.d.ts +22 -0
  34. package/dist/utils/scrollParent.js +55 -0
  35. package/dist/utils/scrollParent.js.map +1 -0
  36. package/package.json +9 -9
@@ -1,9 +1,12 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { View, ScrollView } from '@tarojs/components';
3
+ import { ScrollElementContext } from '@tarojs/components-react';
4
+ export { ScrollElementContext as ListScrollElementContext } from '@tarojs/components-react';
3
5
  import Taro from '@tarojs/taro';
4
- import React, { useContext } from 'react';
6
+ import React from 'react';
5
7
  import { useItemSizeCache } from './hooks/useItemSizeCache.js';
6
- import { useMeasureStartOffset } from './hooks/useMeasureStartOffset.js';
8
+ import { useListNestedScroll } from './hooks/useListNestedScroll.js';
9
+ import { useListScrollElementAttach } from './hooks/useListScrollElementAttach.js';
7
10
  import { useRefresher, DEFAULT_REFRESHER_HEIGHT } from './hooks/useRefresher.js';
8
11
  import { useResizeObserver } from './hooks/useResizeObserver.js';
9
12
  import { useScrollCorrection } from './hooks/useScrollCorrection.js';
@@ -11,7 +14,7 @@ import { ListItem } from './ListItem.js';
11
14
  import { NoMore } from './NoMore.js';
12
15
  import { StickyHeader } from './StickyHeader.js';
13
16
  import { StickySection } from './StickySection.js';
14
- import { isH5, isWeapp, supportsNativeRefresher } from './utils.js';
17
+ import { isWeapp, isH5, supportsNativeRefresher } from './utils.js';
15
18
 
16
19
  // 工具:累加数组
17
20
  function accumulate(arr) {
@@ -43,35 +46,33 @@ function shouldMeasureWeappItem(index, measuredSet) {
43
46
  }
44
47
  // 小程序端暂不外抛 onItemSizeChange,避免父层重渲染导致 List remount 引发回顶或空白
45
48
  function weappDeferItemSizeChange(_index, _size, _onItemSizeChange) { }
46
- const EMPTY_SCROLL_REF = { current: null };
47
- /** 任务 4.1/4.2:List 内嵌 WaterFlow/List 时,向子组件提供 scrollRef、containerHeight、startOffset、reportNestedHeightChange */
48
- const ListScrollElementContext = React.createContext(null);
49
- /** 抽离 scrollElement 模式逻辑,降低 InnerList 圈复杂度 */
50
- function useListScrollElement(listType, scrollElement,
51
- /** Context 时自动测量的 startOffset(上方内容高度),与下方 DOM stacking 对称 */
52
- measuredStartOffset) {
53
- var _a, _b;
54
- const listScrollCtx = useContext(ListScrollElementContext);
55
- const effectiveScrollElement = scrollElement !== null && scrollElement !== void 0 ? scrollElement : listScrollCtx === null || listScrollCtx === void 0 ? void 0 : listScrollCtx.scrollRef;
56
- const effectiveStartOffset = (_b = (_a = listScrollCtx === null || listScrollCtx === void 0 ? void 0 : listScrollCtx.startOffset) !== null && _a !== void 0 ? _a : measuredStartOffset) !== null && _b !== void 0 ? _b : 0;
57
- const useScrollElementMode = listType === 'nested' && !!(effectiveScrollElement && isH5);
58
- if (listType === 'nested' && !effectiveScrollElement && isH5) {
59
- // eslint-disable-next-line no-console
60
- console.warn('[List] type=nested 但无 scrollElement(props 或 Context),回退为 default');
49
+ /** scroll 选项解析目标偏移量 */
50
+ function resolveScrollTargetOffset(options, isHorizontal) {
51
+ const opts = options !== null && options !== void 0 ? options : {};
52
+ const top = typeof opts.top === 'number' ? opts.top : undefined;
53
+ const left = typeof opts.left === 'number' ? opts.left : undefined;
54
+ let result = 0;
55
+ if (isHorizontal) {
56
+ if (typeof left === 'number')
57
+ result = left;
58
+ else if (typeof top === 'number')
59
+ result = top;
61
60
  }
62
- return { effectiveScrollElement, effectiveStartOffset, useScrollElementMode, hasListScrollCtx: !!listScrollCtx };
61
+ else {
62
+ if (typeof top === 'number')
63
+ result = top;
64
+ else if (typeof left === 'number')
65
+ result = left;
66
+ }
67
+ return Number.isFinite(result) ? result : 0;
63
68
  }
69
+ // eslint-disable-next-line complexity -- List 多端/多模式逻辑集中,已抽离 useListNestedScroll、useListScrollElementAttach、resolveScrollTargetOffset 等
64
70
  const InnerList = (props, ref) => {
65
- var _a, _b, _c, _d;
66
- const { stickyHeader = false, space = 0, height = 400, width = '100%', showScrollbar = true, scrollTop: controlledScrollTop, scrollX = false, scrollY = true, onScroll, onScrollToUpper, onScrollToLower, onScrollStart, onScrollEnd, upperThreshold = 50, lowerThreshold = 50, cacheCount = 2, cacheExtent, enableBackToTop, className, style, children, type: listType = 'default', scrollElement, scrollRef: scrollRefProp, } = props;
71
+ var _a, _b, _c;
72
+ const { stickyHeader = false, space = 0, height = 400, width = '100%', showScrollbar = true, scrollTop: controlledScrollTop, scrollX = false, scrollY = true, onScroll, onScrollToUpper, onScrollToLower, onScrollStart, onScrollEnd, upperThreshold = 50, lowerThreshold = 50, cacheCount = 2, cacheExtent, enableBackToTop, className, style, children, nestedScroll, scrollElement, scrollRef: scrollRefProp, } = props;
67
73
  const isHorizontal = scrollX === true;
68
- const contentWrapperRef = React.useRef(null);
69
- const result1 = useListScrollElement(listType, scrollElement, 0);
70
- const measuredStartOffset = useMeasureStartOffset((_a = result1.effectiveScrollElement) !== null && _a !== void 0 ? _a : EMPTY_SCROLL_REF, contentWrapperRef, {
71
- enabled: result1.useScrollElementMode && !result1.hasListScrollCtx && !!result1.effectiveScrollElement && isH5,
72
- isHorizontal,
73
- });
74
- const { effectiveScrollElement, effectiveStartOffset, useScrollElementMode } = useListScrollElement(listType, scrollElement, measuredStartOffset);
74
+ const listType = nestedScroll === true ? 'nested' : 'default';
75
+ const { effectiveScrollElement, effectiveStartOffset, useScrollElementMode, needAutoFind, autoFindStatus, contentWrapperRef, } = useListNestedScroll(listType, scrollElement, undefined, isHorizontal);
75
76
  const DEFAULT_ITEM_WIDTH = 120;
76
77
  const DEFAULT_ITEM_HEIGHT = 40;
77
78
  // 滚动状态管理
@@ -172,31 +173,7 @@ const InnerList = (props, ref) => {
172
173
  // 暴露给外部的实例方法:通过 ref.scroll({ top / left }) 进行程序性滚动
173
174
  React.useImperativeHandle(ref, () => ({
174
175
  scroll(options) {
175
- const opts = options !== null && options !== void 0 ? options : {};
176
- const isHorizontal = scrollX === true;
177
- const top = typeof opts.top === 'number' ? opts.top : undefined;
178
- const left = typeof opts.left === 'number' ? opts.left : undefined;
179
- let targetOffset = 0;
180
- if (isHorizontal) {
181
- if (typeof left === 'number') {
182
- targetOffset = left;
183
- }
184
- else if (typeof top === 'number') {
185
- targetOffset = top;
186
- }
187
- }
188
- else {
189
- if (typeof top === 'number') {
190
- targetOffset = top;
191
- }
192
- else if (typeof left === 'number') {
193
- targetOffset = left;
194
- }
195
- }
196
- if (!Number.isFinite(targetOffset)) {
197
- targetOffset = 0;
198
- }
199
- // 任务 2.2:scrollElement 模式下直接操作外部容器(内嵌时需加上 startOffset)
176
+ const targetOffset = resolveScrollTargetOffset(options, isHorizontal);
200
177
  const el = effectiveScrollElement === null || effectiveScrollElement === void 0 ? void 0 : effectiveScrollElement.current;
201
178
  if (el && isH5) {
202
179
  const scrollTarget = targetOffset + effectiveStartOffset;
@@ -338,7 +315,7 @@ const InnerList = (props, ref) => {
338
315
  }, [children]);
339
316
  // 动态尺寸管理
340
317
  const defaultEstimatedSize = isHorizontal ? DEFAULT_ITEM_WIDTH : DEFAULT_ITEM_HEIGHT;
341
- const estimatedSize = (_b = props.estimatedItemSize) !== null && _b !== void 0 ? _b : defaultEstimatedSize;
318
+ const estimatedSize = (_a = props.estimatedItemSize) !== null && _a !== void 0 ? _a : defaultEstimatedSize;
342
319
  // 计算总 item 数量(跨所有 section)
343
320
  const totalItemCount = React.useMemo(() => {
344
321
  return sections.reduce((sum, section) => sum + section.items.length, 0);
@@ -616,73 +593,15 @@ const InnerList = (props, ref) => {
616
593
  }
617
594
  onScrollEnd === null || onScrollEnd === void 0 ? void 0 : onScrollEnd();
618
595
  }, [isWeapp, onScrollEnd]);
619
- // scrollElement 模式:containerLength 从 scrollElement 获取(任务 1.6)
620
- React.useEffect(() => {
621
- if (!effectiveScrollElement || !isH5)
622
- return;
623
- const el = effectiveScrollElement.current;
624
- if (!el || typeof ResizeObserver === 'undefined')
625
- return;
626
- const update = () => {
627
- const measured = isHorizontal ? el.clientWidth : el.clientHeight;
628
- if (measured > 0) {
629
- setContainerLength(measured);
630
- }
631
- // scrollElement 模式下 useLayoutEffect 可能早于 ref 挂载,ResizeObserver 作为兜底确保 scrollRef 正确
632
- if (scrollRefProp)
633
- scrollRefProp.current = el;
634
- };
635
- update();
636
- const ro = new ResizeObserver(update);
637
- ro.observe(el);
638
- return () => ro.disconnect();
639
- }, [effectiveScrollElement, isH5, isHorizontal]);
640
- // scrollElement 模式:监听外部滚动,驱动 renderOffset
641
- // 使用 ref 保存 scrollCorrection/onScroll,避免每次渲染 deps 变化导致 effect 反复执行形成循环
642
- const effectiveStartOffsetRef = React.useRef(effectiveStartOffset);
643
- effectiveStartOffsetRef.current = effectiveStartOffset;
644
- React.useEffect(() => {
645
- if (!effectiveScrollElement || !isH5)
646
- return;
647
- const el = effectiveScrollElement.current;
648
- if (!el)
649
- return;
650
- const handler = () => {
651
- var _a, _b, _c;
652
- const scrollPos = isHorizontal ? el.scrollLeft : el.scrollTop;
653
- const adjustedPos = scrollPos - effectiveStartOffsetRef.current;
654
- const clientSize = isHorizontal ? el.clientWidth : el.clientHeight;
655
- const scrollSize = isHorizontal ? el.scrollWidth : el.scrollHeight;
656
- scrollCorrectionRef.current.markUserScrolling();
657
- updateRenderOffset(adjustedPos, false, 'scrollElement');
658
- (_a = onScrollRef.current) === null || _a === void 0 ? void 0 : _a.call(onScrollRef, {
659
- scrollTop: isHorizontal ? 0 : scrollPos,
660
- scrollLeft: isHorizontal ? scrollPos : 0
661
- });
662
- // 任务 2.1:onScrollToUpper / onScrollToLower(scrollElement 模式用 adjustedPos,使有上方混排时以 List 顶为基准)
663
- const { upper, lower } = thresholdRef.current;
664
- const effectiveAdjusted = Math.max(0, adjustedPos);
665
- if (effectiveAdjusted <= upper) {
666
- (_b = onScrollToUpperRef.current) === null || _b === void 0 ? void 0 : _b.call(onScrollToUpperRef);
667
- }
668
- if (scrollPos + clientSize >= scrollSize - lower) {
669
- (_c = onScrollToLowerRef.current) === null || _c === void 0 ? void 0 : _c.call(onScrollToLowerRef);
670
- }
671
- };
672
- // 同步初始 scroll 位置(内嵌时需减去 startOffset)
673
- const initialScroll = isHorizontal ? el.scrollLeft : el.scrollTop;
674
- updateRenderOffset(initialScroll - effectiveStartOffsetRef.current, false, 'scrollElement');
675
- el.addEventListener('scroll', handler, { passive: true });
676
- return () => el.removeEventListener('scroll', handler);
677
- }, [effectiveScrollElement, isH5, isHorizontal, updateRenderOffset]);
678
596
  // 任务 2.3:暴露 scrollRef 给父组件(供内层 List/WaterFlow 传入 scrollElement)
679
597
  // 收敛到此处统一赋值,不再在 ResizeObserver 中重复
680
598
  React.useLayoutEffect(() => {
681
599
  if (!scrollRefProp)
682
600
  return;
683
601
  const el = useScrollElementMode ? effectiveScrollElement === null || effectiveScrollElement === void 0 ? void 0 : effectiveScrollElement.current : containerRef.current;
684
- if (el)
602
+ if (el) {
685
603
  scrollRefProp.current = el;
604
+ }
686
605
  }, [scrollRefProp, useScrollElementMode, effectiveScrollElement]);
687
606
  // controlledScrollTop 变化时同步到 ScrollView
688
607
  React.useEffect(() => {
@@ -872,6 +791,19 @@ const InnerList = (props, ref) => {
872
791
  const noMoreHeight = (noMoreConfig === null || noMoreConfig === void 0 ? void 0 : noMoreConfig.visible) ? (noMoreConfig.height || 60) : 0;
873
792
  const listContentLength = sectionOffsets[sectionOffsets.length - 1] + noMoreHeight;
874
793
  const totalLength = listContentLength;
794
+ // scrollElement 模式下 onScrollToLower 需用内层内容高度判断,供 scroll handler 读取
795
+ const listContentLengthRef = React.useRef(0);
796
+ listContentLengthRef.current = listContentLength;
797
+ const scrollAttachRefsRef = React.useRef(null);
798
+ scrollAttachRefsRef.current = {
799
+ scrollCorrection: scrollCorrectionRef.current,
800
+ onScroll: onScrollRef.current,
801
+ onScrollToUpper: onScrollToUpperRef.current,
802
+ onScrollToLower: onScrollToLowerRef.current,
803
+ threshold: thresholdRef.current,
804
+ listContentLength: listContentLengthRef.current,
805
+ };
806
+ useListScrollElementAttach(useScrollElementMode && isH5, effectiveScrollElement, effectiveStartOffset, isHorizontal, setContainerLength, updateRenderOffset, scrollRefProp, scrollAttachRefsRef);
875
807
  // 吸顶/吸左 header
876
808
  const stickyHeaderNode = React.useMemo(() => {
877
809
  if (!stickyHeader)
@@ -1152,7 +1084,7 @@ const InnerList = (props, ref) => {
1152
1084
  const itemNode = React.createElement(View, outerItemProps, React.createElement(View, innerProps, section.items[i]));
1153
1085
  // 任务 4.1:当 List 暴露 scrollRef 时,为每个 item 提供 Context,供内层 WaterFlow 使用
1154
1086
  const itemWithContext = scrollRefProp && !useScrollElementMode
1155
- ? React.createElement(ListScrollElementContext.Provider, {
1087
+ ? React.createElement(ScrollElementContext.Provider, {
1156
1088
  key: outerItemProps.key,
1157
1089
  value: {
1158
1090
  scrollRef: scrollRefProp,
@@ -1189,7 +1121,7 @@ const InnerList = (props, ref) => {
1189
1121
  else {
1190
1122
  const itemNode = React.createElement(View, outerItemProps, section.items[i]);
1191
1123
  const itemWithContext = scrollRefProp && !useScrollElementMode
1192
- ? React.createElement(ListScrollElementContext.Provider, {
1124
+ ? React.createElement(ScrollElementContext.Provider, {
1193
1125
  key: outerItemProps.key,
1194
1126
  value: {
1195
1127
  scrollRef: scrollRefProp,
@@ -1269,7 +1201,9 @@ const InnerList = (props, ref) => {
1269
1201
  transform: `translateY(${h5RefresherTranslateY}px)`,
1270
1202
  }, children: renderRefresherContent() }), jsxs(View, { style: Object.assign(Object.assign(Object.assign({}, listWrapperStyle), pullTranslate), { zIndex: 1, background: (_b = (_a = style === null || style === void 0 ? void 0 : style.background) !== null && _a !== void 0 ? _a : style === null || style === void 0 ? void 0 : style.backgroundColor) !== null && _b !== void 0 ? _b : '#fff' }), children: [stickyHeaderNode, renderSections(), renderNoMoreContent()] })] })) : (jsxs(Fragment, { children: [!supportsNativeRefresher && renderRefresherContent(), stickyHeaderNode, renderSections(), renderNoMoreContent()] })) }));
1271
1203
  };
1272
- if (useScrollElementMode) {
1204
+ // useScrollElementMode 或 needAutoFind&&pending:渲染 View 以便监听外部滚动或 probe 阶段查找滚动父节点
1205
+ const renderView = useScrollElementMode || (!!needAutoFind && autoFindStatus === 'pending');
1206
+ if (renderView) {
1273
1207
  // 任务 2.4:恢复 refresher DOM 结构(refresher 层 + listWrapperStyle)
1274
1208
  return (jsx(View, { ref: contentWrapperRef, style: contentWrapperStyle, children: refresherHeightForH5 > 0 ? (jsxs(Fragment, { children: [jsx(View, { style: {
1275
1209
  position: 'absolute',
@@ -1279,11 +1213,11 @@ const InnerList = (props, ref) => {
1279
1213
  height: refresherHeightForH5,
1280
1214
  zIndex: 0,
1281
1215
  transform: `translateY(${h5RefresherTranslateY}px)`,
1282
- }, children: renderRefresherContent() }), jsxs(View, { style: Object.assign(Object.assign(Object.assign({}, listWrapperStyle), pullTranslate), { zIndex: 1, background: (_d = (_c = style === null || style === void 0 ? void 0 : style.background) !== null && _c !== void 0 ? _c : style === null || style === void 0 ? void 0 : style.backgroundColor) !== null && _d !== void 0 ? _d : '#fff' }), children: [stickyHeaderNode, renderSections(), renderNoMoreContent()] })] })) : (jsxs(Fragment, { children: [stickyHeaderNode, renderSections(), renderNoMoreContent()] })) }));
1216
+ }, children: renderRefresherContent() }), jsxs(View, { style: Object.assign(Object.assign(Object.assign({}, listWrapperStyle), pullTranslate), { zIndex: 1, background: (_c = (_b = style === null || style === void 0 ? void 0 : style.background) !== null && _b !== void 0 ? _b : style === null || style === void 0 ? void 0 : style.backgroundColor) !== null && _c !== void 0 ? _c : '#fff' }), children: [stickyHeaderNode, renderSections(), renderNoMoreContent()] })] })) : (jsxs(Fragment, { children: [stickyHeaderNode, renderSections(), renderNoMoreContent()] })) }));
1283
1217
  }
1284
1218
  return (jsxs(ScrollView, Object.assign({ ref: containerRef }, scrollViewProps, { id: listId, children: [supportsNativeRefresher && renderRefresherContent(), renderContentArea()] })));
1285
1219
  };
1286
1220
  const List = React.forwardRef(InnerList);
1287
1221
 
1288
- export { List, ListItem, ListScrollElementContext, NoMore, StickyHeader, StickySection, accumulate, List as default, isShaking };
1222
+ export { List, ListItem, NoMore, StickyHeader, StickySection, accumulate, List as default, isShaking };
1289
1223
  //# sourceMappingURL=index.js.map