98 lines
3.2 KiB
TypeScript
98 lines
3.2 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect } from "react";
|
|
import type { Dispatch, MutableRefObject, SetStateAction } from "react";
|
|
import { isEditableTarget } from "@/features/entries/components/entries-panel.utils";
|
|
|
|
type UseInfiniteVisibleCountParams = {
|
|
enabled: boolean;
|
|
hasMore: boolean;
|
|
totalCount: number;
|
|
pageSize: number;
|
|
sentinelRef: MutableRefObject<HTMLDivElement | null>;
|
|
setVisibleCount: Dispatch<SetStateAction<number>>;
|
|
};
|
|
|
|
export default function useInfiniteVisibleCount({
|
|
enabled,
|
|
hasMore,
|
|
totalCount,
|
|
pageSize,
|
|
sentinelRef,
|
|
setVisibleCount
|
|
}: UseInfiniteVisibleCountParams) {
|
|
useEffect(() => {
|
|
if (!enabled || !hasMore) return;
|
|
|
|
let touchY: number | null = null;
|
|
let lastLoadAt = 0;
|
|
let lastScrollY = window.scrollY;
|
|
|
|
function shouldLoadMore() {
|
|
const sentinel = sentinelRef.current;
|
|
if (!sentinel) return false;
|
|
const rect = sentinel.getBoundingClientRect();
|
|
return rect.top <= window.innerHeight + 48;
|
|
}
|
|
|
|
function tryLoadMore() {
|
|
if (!shouldLoadMore()) return;
|
|
const now = Date.now();
|
|
if (now - lastLoadAt < 150) return;
|
|
lastLoadAt = now;
|
|
setVisibleCount(prev => {
|
|
if (prev >= totalCount) return prev;
|
|
return Math.min(prev + pageSize, totalCount);
|
|
});
|
|
}
|
|
|
|
function onWheel(event: WheelEvent) {
|
|
if (event.deltaY <= 0) return;
|
|
tryLoadMore();
|
|
}
|
|
|
|
function onScroll() {
|
|
const nextY = window.scrollY;
|
|
if (nextY <= lastScrollY) {
|
|
lastScrollY = nextY;
|
|
return;
|
|
}
|
|
lastScrollY = nextY;
|
|
tryLoadMore();
|
|
}
|
|
|
|
function onKeyDown(event: KeyboardEvent) {
|
|
if (isEditableTarget(event.target)) return;
|
|
if (event.key === "ArrowDown" || event.key === "PageDown" || event.key === "End" || (event.key === " " && !event.shiftKey)) {
|
|
tryLoadMore();
|
|
}
|
|
}
|
|
|
|
function onTouchStart(event: TouchEvent) {
|
|
touchY = event.touches[0]?.clientY ?? null;
|
|
}
|
|
|
|
function onTouchMove(event: TouchEvent) {
|
|
const nextY = event.touches[0]?.clientY;
|
|
if (touchY == null || nextY == null) return;
|
|
const delta = touchY - nextY;
|
|
touchY = nextY;
|
|
if (delta > 10) tryLoadMore();
|
|
}
|
|
|
|
window.addEventListener("wheel", onWheel, { passive: true });
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
window.addEventListener("keydown", onKeyDown);
|
|
window.addEventListener("touchstart", onTouchStart, { passive: true });
|
|
window.addEventListener("touchmove", onTouchMove, { passive: true });
|
|
|
|
return () => {
|
|
window.removeEventListener("wheel", onWheel);
|
|
window.removeEventListener("scroll", onScroll);
|
|
window.removeEventListener("keydown", onKeyDown);
|
|
window.removeEventListener("touchstart", onTouchStart);
|
|
window.removeEventListener("touchmove", onTouchMove);
|
|
};
|
|
}, [enabled, hasMore, pageSize, sentinelRef, setVisibleCount, totalCount]);
|
|
}
|