Skip to content

Commit 13a8144

Browse files
author
zhuqingan.3
committed
feat: list暂存动态高度+下拉刷新修改+新增refresher组件
1 parent 3f909cf commit 13a8144

File tree

13 files changed

+1798
-122
lines changed

13 files changed

+1798
-122
lines changed

packages/taro-components-advanced/src/components/list/ListItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { View } from '@tarojs/components'
1+
import { View } from '@tarojs/components-react'
22
import React from 'react'
33

44
export interface ListItemProps {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react'
2+
3+
/**
4+
* NoMore 组件 - 底部"没有更多"提示
5+
*
6+
* 这是一个标记组件,不直接渲染内容,仅用于向 List 组件传递配置。
7+
* 实际渲染由 List 组件内部处理。
8+
*
9+
* @example
10+
* ```tsx
11+
* <List>
12+
* <ListItem>Item 1</ListItem>
13+
* <NoMore visible={!hasMore} text="没有更多了" />
14+
* </List>
15+
* ```
16+
*/
17+
18+
export interface NoMoreProps {
19+
/** 是否显示(默认 true) */
20+
visible?: boolean
21+
22+
/** 提示文字(默认 "没有更多了") */
23+
text?: string
24+
25+
/** 自定义样式 */
26+
style?: React.CSSProperties
27+
28+
/** 自定义内容(优先级高于 text) */
29+
children?: React.ReactNode
30+
31+
/** NoMore 区域高度(用于动态高度计算,默认 60) */
32+
height?: number
33+
}
34+
35+
const NoMore: React.FC<NoMoreProps> = () => {
36+
// 标记组件,不实际渲染
37+
// 实际渲染由 List 组件内部的 renderNoMoreContent() 处理
38+
return null
39+
}
40+
41+
// 设置 displayName 便于调试
42+
NoMore.displayName = 'NoMore'
43+
44+
export { NoMore }
45+
export default NoMore

packages/taro-components-advanced/src/components/list/StickyHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { View } from '@tarojs/components'
1+
import { View } from '@tarojs/components-react'
22
import React from 'react'
33

44
export interface StickyHeaderProps {

packages/taro-components-advanced/src/components/list/StickySection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { View } from '@tarojs/components'
1+
import { View } from '@tarojs/components-react'
22
import React from 'react'
33

44
export interface StickySectionProps {
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { useCallback, useRef } from 'react'
2+
3+
/**
4+
* useItemSizeCache Hook - 动态尺寸缓存管理
5+
*
6+
* 支持垂直滚动(高度)和水平滚动(宽度)两种模式
7+
*/
8+
9+
/** 单个项的尺寸缓存 */
10+
interface ItemMeasureCache {
11+
measuredSize: number | null // 实际测量尺寸
12+
estimatedSize: number // 估算尺寸
13+
isMeasured: boolean // 是否已测量
14+
}
15+
16+
interface UseItemSizeCacheOptions {
17+
isHorizontal: boolean // 是否水平滚动
18+
estimatedItemSize: number // 估算尺寸
19+
itemCount: number // 项总数
20+
}
21+
22+
interface UseItemSizeCacheReturn {
23+
/** 获取项的尺寸(高度或宽度) */
24+
getItemSize: (index: number) => number
25+
26+
/** 设置项的尺寸 */
27+
setItemSize: (index: number, size: number) => void
28+
29+
/** 获取指定索引的累积偏移 */
30+
getItemOffset: (index: number) => number
31+
32+
/** 清空缓存 */
33+
clearCache: () => void
34+
35+
/** 获取缓存统计信息 */
36+
getCacheStats: () => {
37+
totalItems: number
38+
measuredItems: number
39+
estimatedItems: number
40+
}
41+
42+
/** 维度名称('height' 或 'width') */
43+
dimension: 'height' | 'width'
44+
}
45+
46+
export function useItemSizeCache(
47+
options: UseItemSizeCacheOptions
48+
): UseItemSizeCacheReturn {
49+
const { isHorizontal, estimatedItemSize, itemCount } = options
50+
51+
// 缓存 Map:key = 索引,value = 尺寸信息
52+
const cacheRef = useRef<Map<number, ItemMeasureCache>>(new Map())
53+
54+
// 维度名称
55+
const dimension: 'height' | 'width' = isHorizontal ? 'width' : 'height'
56+
57+
/**
58+
* 获取项的尺寸
59+
* 优先返回实际测量值,否则返回估算值
60+
*/
61+
const getItemSize = useCallback((index: number): number => {
62+
const cached = cacheRef.current.get(index)
63+
64+
if (cached?.isMeasured && cached.measuredSize !== null) {
65+
return cached.measuredSize
66+
}
67+
68+
return estimatedItemSize
69+
}, [estimatedItemSize])
70+
71+
/**
72+
* 设置项的尺寸(实际测量后调用)
73+
*/
74+
const setItemSize = useCallback((index: number, size: number) => {
75+
const cached = cacheRef.current.get(index)
76+
77+
// 尺寸变化小于 1px,忽略(避免微小抖动)
78+
if (cached?.measuredSize && Math.abs(cached.measuredSize - size) < 1) {
79+
return
80+
}
81+
82+
cacheRef.current.set(index, {
83+
measuredSize: size,
84+
estimatedSize: estimatedItemSize,
85+
isMeasured: true
86+
})
87+
}, [estimatedItemSize])
88+
89+
/**
90+
* 获取指定索引的累积偏移
91+
* 计算从 0 到 index-1 的所有项尺寸之和
92+
*/
93+
const getItemOffset = useCallback((index: number): number => {
94+
let offset = 0
95+
96+
for (let i = 0; i < index; i++) {
97+
offset += getItemSize(i)
98+
}
99+
100+
return offset
101+
}, [getItemSize])
102+
103+
/**
104+
* 清空缓存
105+
*/
106+
const clearCache = useCallback(() => {
107+
cacheRef.current.clear()
108+
}, [])
109+
110+
/**
111+
* 获取缓存统计信息
112+
*/
113+
const getCacheStats = useCallback(() => {
114+
let measuredItems = 0
115+
let estimatedItems = 0
116+
117+
for (let i = 0; i < itemCount; i++) {
118+
const cached = cacheRef.current.get(i)
119+
if (cached?.isMeasured) {
120+
measuredItems++
121+
} else {
122+
estimatedItems++
123+
}
124+
}
125+
126+
return {
127+
totalItems: itemCount,
128+
measuredItems,
129+
estimatedItems
130+
}
131+
}, [itemCount])
132+
133+
return {
134+
getItemSize,
135+
setItemSize,
136+
getItemOffset,
137+
clearCache,
138+
getCacheStats,
139+
dimension
140+
}
141+
}

0 commit comments

Comments
 (0)