Fix scroll danh sách cuối station

This commit is contained in:
Truong Vo 2025-12-02 09:42:19 +07:00
parent c532e78093
commit 1be935a805
1 changed files with 131 additions and 3 deletions

View File

@ -56,6 +56,8 @@ function ModalHistory({
const stationRefs = useRef<Map<number, HTMLDivElement>>(new Map()); const stationRefs = useRef<Map<number, HTMLDivElement>>(new Map());
const stationContentRefs = useRef<Map<number, HTMLDivElement>>(new Map()); const stationContentRefs = useRef<Map<number, HTMLDivElement>>(new Map());
const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null); const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const lastScrollTopRef = useRef<number>(0);
const isBlockingScrollRef = useRef<boolean>(false);
useEffect(() => { useEffect(() => {
if (!socket || !opened) return; if (!socket || !opened) return;
@ -93,6 +95,8 @@ function ModalHistory({
setActiveTimePeriod("current"); setActiveTimePeriod("current");
stationRefs.current.clear(); stationRefs.current.clear();
stationContentRefs.current.clear(); stationContentRefs.current.clear();
isBlockingScrollRef.current = false;
lastScrollTopRef.current = 0;
} }
return () => { return () => {
@ -102,6 +106,42 @@ function ModalHistory({
}; };
}, [opened]); }, [opened]);
// Prevent wheel scroll when at last station with no data
useEffect(() => {
if (!scrollViewportRef.current || !opened) return;
const scrollContainer = scrollViewportRef.current;
const handleWheel = (e: WheelEvent) => {
if (!isBlockingScrollRef.current) return;
const scrollHeight = scrollContainer.scrollHeight;
const clientHeight = scrollContainer.clientHeight;
const currentScrollTop = scrollContainer.scrollTop;
const maxScrollTop = scrollHeight - clientHeight;
const isAtBottom = Math.abs(currentScrollTop + clientHeight - scrollHeight) < 20;
// If at bottom and trying to scroll down, prevent it
if (isAtBottom && e.deltaY > 0) {
e.preventDefault();
e.stopPropagation();
// Ensure we stay at bottom
if (currentScrollTop < maxScrollTop - 5) {
scrollContainer.scrollTo({
top: maxScrollTop,
behavior: "auto",
});
}
}
};
scrollContainer.addEventListener("wheel", handleWheel, { passive: false });
return () => {
scrollContainer.removeEventListener("wheel", handleWheel);
};
}, [opened, historyData, activeTimePeriod, activeStation]);
// Scroll to station when activeStation changes (only when clicked, not when auto-detected) // Scroll to station when activeStation changes (only when clicked, not when auto-detected)
const isManualScrollRef = useRef(false); const isManualScrollRef = useRef(false);
@ -385,6 +425,88 @@ function ModalHistory({
const scrollContainer = scrollViewportRef.current; const scrollContainer = scrollViewportRef.current;
const containerRect = scrollContainer.getBoundingClientRect(); const containerRect = scrollContainer.getBoundingClientRect();
const topThreshold = containerRect.top + 10; // Sát top với threshold 10px const topThreshold = containerRect.top + 10; // Sát top với threshold 10px
// Check if scrolled to bottom
const scrollHeight = scrollContainer.scrollHeight;
const clientHeight = scrollContainer.clientHeight;
const currentScrollTop = scrollContainer.scrollTop;
const isAtBottom = Math.abs(currentScrollTop + clientHeight - scrollHeight) < 20;
// Check if currently at last station with no data - prevent scrolling
if (allGroupedStations.length > 0) {
const lastStation = allGroupedStations[allGroupedStations.length - 1];
const lastStationHasNoData = lastStation.groupedHistory.size === 0;
const lastStationIdStr = String(lastStation.stationId);
// If at bottom and last station has no data, prevent scrolling
if (isAtBottom && lastStationHasNoData) {
if (lastStationIdStr !== activeStation) {
setActiveStation(lastStationIdStr);
}
// Block scroll if trying to scroll down
isBlockingScrollRef.current = true;
const maxScrollTop = scrollHeight - clientHeight;
// If trying to scroll down (scrollTop increased), reset to bottom
if (currentScrollTop > lastScrollTopRef.current && currentScrollTop < maxScrollTop - 5) {
scrollContainer.scrollTo({
top: maxScrollTop,
behavior: "auto", // Use auto for instant reset
});
lastScrollTopRef.current = maxScrollTop;
return;
}
// Keep scroll at bottom
lastScrollTopRef.current = maxScrollTop;
return;
}
// If currently viewing last station with no data
const currentStation = allGroupedStations.find(
(s) => s.stationId.toString() === activeStation
);
const isLastStation = currentStation?.stationId === lastStation.stationId;
const hasNoData = currentStation?.groupedHistory.size === 0;
if (isLastStation && hasNoData) {
isBlockingScrollRef.current = true;
const maxScrollTop = scrollHeight - clientHeight;
// If trying to scroll down past bottom, reset to bottom
if (currentScrollTop > lastScrollTopRef.current && currentScrollTop < maxScrollTop - 5) {
scrollContainer.scrollTo({
top: maxScrollTop,
behavior: "auto",
});
lastScrollTopRef.current = maxScrollTop;
return;
}
// Keep scroll at bottom if already there
if (isAtBottom) {
lastScrollTopRef.current = maxScrollTop;
return;
}
} else {
isBlockingScrollRef.current = false;
}
} else {
isBlockingScrollRef.current = false;
}
// Update last scroll position
lastScrollTopRef.current = currentScrollTop;
// If at bottom, select the last station
if (isAtBottom && allGroupedStations.length > 0) {
const lastStation = allGroupedStations[allGroupedStations.length - 1];
const lastStationIdStr = String(lastStation.stationId);
if (lastStationIdStr !== activeStation) {
setActiveStation(lastStationIdStr);
}
}
// Find which station header is closest to top // Find which station header is closest to top
// Ưu tiên: header đã vượt qua top threshold (ở trên) > header chưa đến (ở dưới) // Ưu tiên: header đã vượt qua top threshold (ở trên) > header chưa đến (ở dưới)
@ -427,7 +549,13 @@ function ModalHistory({
{allGroupedStations.length > 0 ? ( {allGroupedStations.length > 0 ? (
<> <>
{allGroupedStations.map((station) => ( {allGroupedStations.map((station) => (
<Box key={`station-${station.stationId}`} style={{ marginBottom: "24px" }}> <Box
key={`station-${station.stationId}`}
style={{
marginBottom: "24px",
minHeight: station.groupedHistory.size === 0 ? "200px" : "auto" // Đảm bảo station không có content vẫn có height
}}
>
{/* Station Title */} {/* Station Title */}
<Box <Box
ref={(el) => { ref={(el) => {
@ -579,8 +707,8 @@ function ModalHistory({
</Box> </Box>
))} ))}
{/* Spacer để đảm bảo có thể scroll ngay cả khi content ít */} {/* Spacer để đảm bảo có thể scroll đến tất cả stations, kể cả cuối cùng */}
<Box style={{ height: "700px" }} /> <Box style={{ height: "1000px" }} />
</> </>
) : ( ) : (
<Flex <Flex