Fix scroll danh sách cuối station
This commit is contained in:
parent
c532e78093
commit
1be935a805
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue