91 lines
2.7 KiB
TypeScript
91 lines
2.7 KiB
TypeScript
'use client'
|
||
|
||
import React, { useEffect, useRef, forwardRef } from 'react'
|
||
|
||
interface LoadMoreTriggerProps {
|
||
hasMore: boolean
|
||
isLoading: boolean
|
||
onLoadMore: () => void
|
||
themeColor?: string
|
||
displayedCount?: number // 当前显示数量
|
||
totalCount?: number // 已加载总数量
|
||
showEndMessage?: boolean // 是否显示到底提示
|
||
}
|
||
|
||
/**
|
||
* 加载更多触发器组件
|
||
* 使用 IntersectionObserver 实现无限滚动
|
||
*/
|
||
const LoadMoreTrigger = forwardRef<HTMLDivElement, LoadMoreTriggerProps>(function LoadMoreTrigger({
|
||
hasMore,
|
||
isLoading,
|
||
onLoadMore,
|
||
themeColor = '#1f2937',
|
||
displayedCount,
|
||
totalCount,
|
||
showEndMessage = true,
|
||
}, ref) {
|
||
const internalRef = useRef<HTMLDivElement>(null)
|
||
const loadMoreRef = ref || internalRef
|
||
|
||
// 判断是否需要显示加载更多
|
||
const shouldShowLoadMore = hasMore || (displayedCount !== undefined && totalCount !== undefined && displayedCount < totalCount)
|
||
|
||
useEffect(() => {
|
||
const element = typeof loadMoreRef === 'function' ? null : loadMoreRef.current
|
||
if (!element) return
|
||
if (isLoading) return
|
||
if (!shouldShowLoadMore) return
|
||
|
||
const observer = new IntersectionObserver(
|
||
(entries) => {
|
||
if (entries[0].isIntersecting && !isLoading) {
|
||
onLoadMore()
|
||
}
|
||
},
|
||
{ threshold: 0.1, rootMargin: '100px' }
|
||
)
|
||
|
||
observer.observe(element)
|
||
|
||
return () => observer.disconnect()
|
||
}, [loadMoreRef, hasMore, isLoading, onLoadMore, shouldShowLoadMore])
|
||
|
||
// 显示加载更多
|
||
if (shouldShowLoadMore) {
|
||
return (
|
||
<div
|
||
ref={typeof loadMoreRef === 'function' ? undefined : loadMoreRef}
|
||
className="w-full h-20 flex items-center justify-center mt-8"
|
||
>
|
||
{isLoading ? (
|
||
<div className="text-[#6b7075] text-sm flex items-center gap-2">
|
||
<svg className="animate-spin h-5 w-5" viewBox="0 0 24 24">
|
||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none" />
|
||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
|
||
</svg>
|
||
加载中...
|
||
</div>
|
||
) : (
|
||
<p className="text-[#86898e] text-sm">
|
||
向下滚动加载更多
|
||
</p>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
// 显示到底提示
|
||
if (showEndMessage && !hasMore && (displayedCount === undefined || displayedCount >= (totalCount || 0))) {
|
||
return (
|
||
<div className="w-full py-6 text-center text-sm text-[#86898e] mt-8">
|
||
已经到底啦~
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return null
|
||
})
|
||
|
||
export default LoadMoreTrigger
|