mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-08 17:51:20 +02:00
Refactor viewer
This commit is contained in:
parent
be251d271d
commit
934cd01268
@ -184,8 +184,7 @@ const Viewer = ({
|
|||||||
}, [previewFile, pdfFile]);
|
}, [previewFile, pdfFile]);
|
||||||
|
|
||||||
const scrollAreaRef = useRef<HTMLDivElement>(null);
|
const scrollAreaRef = useRef<HTMLDivElement>(null);
|
||||||
const userInitiatedRef = useRef(false);
|
const isManualNavigationRef = useRef(false);
|
||||||
const suppressScrollRef = useRef(false);
|
|
||||||
const pdfDocRef = useRef<any>(null);
|
const pdfDocRef = useRef<any>(null);
|
||||||
const renderingPagesRef = useRef<Set<number>>(new Set());
|
const renderingPagesRef = useRef<Set<number>>(new Set());
|
||||||
const currentArrayBufferRef = useRef<ArrayBuffer | null>(null);
|
const currentArrayBufferRef = useRef<ArrayBuffer | null>(null);
|
||||||
@ -264,70 +263,44 @@ const Viewer = ({
|
|||||||
preloadingRef.current = false;
|
preloadingRef.current = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Listen for hash changes and update currentPage
|
// Initialize current page when PDF loads
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleHashChange() {
|
if (numPages > 0 && !currentPage) {
|
||||||
if (window.location.hash.startsWith("#page=")) {
|
setCurrentPage(1);
|
||||||
const page = parseInt(window.location.hash.replace("#page=", ""), 10);
|
|
||||||
if (!isNaN(page) && page >= 1 && page <= numPages) {
|
|
||||||
setCurrentPage(page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
userInitiatedRef.current = false;
|
|
||||||
}
|
}
|
||||||
window.addEventListener("hashchange", handleHashChange);
|
}, [numPages, currentPage]);
|
||||||
handleHashChange(); // Run on mount
|
|
||||||
return () => window.removeEventListener("hashchange", handleHashChange);
|
|
||||||
}, [numPages]);
|
|
||||||
|
|
||||||
// Scroll to the current page when it changes
|
// Scroll to the current page when manually navigated
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentPage && pageRefs.current[currentPage - 1]) {
|
if (currentPage && pageRefs.current[currentPage - 1] && isManualNavigationRef.current) {
|
||||||
suppressScrollRef.current = true;
|
|
||||||
const el = pageRefs.current[currentPage - 1];
|
const el = pageRefs.current[currentPage - 1];
|
||||||
el?.scrollIntoView({ behavior: "smooth", block: "center" });
|
el?.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||||
|
|
||||||
// Try to use scrollend if supported
|
// Reset manual navigation flag after a delay
|
||||||
const viewport = scrollAreaRef.current;
|
const timeout = setTimeout(() => {
|
||||||
let timeout: NodeJS.Timeout | null = null;
|
isManualNavigationRef.current = false;
|
||||||
let scrollEndHandler: (() => void) | null = null;
|
}, 1000);
|
||||||
|
|
||||||
if (viewport && "onscrollend" in viewport) {
|
return () => clearTimeout(timeout);
|
||||||
scrollEndHandler = () => {
|
|
||||||
suppressScrollRef.current = false;
|
|
||||||
viewport.removeEventListener("scrollend", scrollEndHandler!);
|
|
||||||
};
|
|
||||||
viewport.addEventListener("scrollend", scrollEndHandler);
|
|
||||||
} else {
|
|
||||||
// Fallback for non-Chromium browsers
|
|
||||||
timeout = setTimeout(() => {
|
|
||||||
suppressScrollRef.current = false;
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (viewport && scrollEndHandler) {
|
|
||||||
viewport.removeEventListener("scrollend", scrollEndHandler);
|
|
||||||
}
|
|
||||||
if (timeout) clearTimeout(timeout);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}, [currentPage, pageImages]);
|
}, [currentPage, pageImages]);
|
||||||
|
|
||||||
// Detect visible page on scroll and update hash
|
// Detect visible page on scroll (only update display, don't trigger navigation)
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
if (suppressScrollRef.current) return;
|
if (isManualNavigationRef.current) return;
|
||||||
const scrollArea = scrollAreaRef.current;
|
const scrollArea = scrollAreaRef.current;
|
||||||
if (!scrollArea || !pageRefs.current.length) return;
|
if (!scrollArea || !pageRefs.current.length) return;
|
||||||
|
|
||||||
const areaRect = scrollArea.getBoundingClientRect();
|
const areaRect = scrollArea.getBoundingClientRect();
|
||||||
|
const viewportCenter = areaRect.top + areaRect.height / 2;
|
||||||
let closestIdx = 0;
|
let closestIdx = 0;
|
||||||
let minDist = Infinity;
|
let minDist = Infinity;
|
||||||
|
|
||||||
pageRefs.current.forEach((img, idx) => {
|
pageRefs.current.forEach((img, idx) => {
|
||||||
if (img) {
|
if (img) {
|
||||||
const imgRect = img.getBoundingClientRect();
|
const imgRect = img.getBoundingClientRect();
|
||||||
const dist = Math.abs(imgRect.top - areaRect.top);
|
const imgCenter = imgRect.top + imgRect.height / 2;
|
||||||
|
const dist = Math.abs(imgCenter - viewportCenter);
|
||||||
if (dist < minDist) {
|
if (dist < minDist) {
|
||||||
minDist = dist;
|
minDist = dist;
|
||||||
closestIdx = idx;
|
closestIdx = idx;
|
||||||
@ -335,11 +308,9 @@ const Viewer = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Only update if the page actually changed and we're not in manual navigation
|
||||||
if (currentPage !== closestIdx + 1) {
|
if (currentPage !== closestIdx + 1) {
|
||||||
setCurrentPage(closestIdx + 1);
|
setCurrentPage(closestIdx + 1);
|
||||||
if (window.location.hash !== `#page=${closestIdx + 1}`) {
|
|
||||||
window.location.hash = `#page=${closestIdx + 1}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -417,7 +388,7 @@ const Viewer = ({
|
|||||||
}, [pageImages]);
|
}, [pageImages]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box style={{ position: 'relative', height: '100%', display: 'flex', flexDirection: 'column' }}>
|
<Box style={{ position: 'relative', height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
||||||
{/* Close Button - Only show in preview mode */}
|
{/* Close Button - Only show in preview mode */}
|
||||||
{onClose && previewFile && (
|
{onClose && previewFile && (
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
@ -467,7 +438,7 @@ const Viewer = ({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<ScrollArea
|
<ScrollArea
|
||||||
style={{ flex: 1, height: "100vh", position: "relative"}}
|
style={{ flex: 1, position: "relative"}}
|
||||||
viewportRef={scrollAreaRef}
|
viewportRef={scrollAreaRef}
|
||||||
>
|
>
|
||||||
<Stack gap="xl" align="center" >
|
<Stack gap="xl" align="center" >
|
||||||
@ -552,7 +523,8 @@ const Viewer = ({
|
|||||||
px={8}
|
px={8}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.location.hash = `#page=1`;
|
isManualNavigationRef.current = true;
|
||||||
|
setCurrentPage(1);
|
||||||
}}
|
}}
|
||||||
disabled={currentPage === 1}
|
disabled={currentPage === 1}
|
||||||
style={{ minWidth: 36 }}
|
style={{ minWidth: 36 }}
|
||||||
@ -566,7 +538,8 @@ const Viewer = ({
|
|||||||
px={8}
|
px={8}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.location.hash = `#page=${Math.max(1, (currentPage || 1) - 1)}`;
|
isManualNavigationRef.current = true;
|
||||||
|
setCurrentPage(Math.max(1, (currentPage || 1) - 1));
|
||||||
}}
|
}}
|
||||||
disabled={currentPage === 1}
|
disabled={currentPage === 1}
|
||||||
style={{ minWidth: 36 }}
|
style={{ minWidth: 36 }}
|
||||||
@ -578,7 +551,8 @@ const Viewer = ({
|
|||||||
onChange={value => {
|
onChange={value => {
|
||||||
const page = Number(value);
|
const page = Number(value);
|
||||||
if (!isNaN(page) && page >= 1 && page <= numPages) {
|
if (!isNaN(page) && page >= 1 && page <= numPages) {
|
||||||
window.location.hash = `#page=${page}`;
|
isManualNavigationRef.current = true;
|
||||||
|
setCurrentPage(page);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
min={1}
|
min={1}
|
||||||
@ -598,7 +572,8 @@ const Viewer = ({
|
|||||||
px={8}
|
px={8}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.location.hash = `#page=${Math.min(numPages, (currentPage || 1) + 1)}`;
|
isManualNavigationRef.current = true;
|
||||||
|
setCurrentPage(Math.min(numPages, (currentPage || 1) + 1));
|
||||||
}}
|
}}
|
||||||
disabled={currentPage === numPages}
|
disabled={currentPage === numPages}
|
||||||
style={{ minWidth: 36 }}
|
style={{ minWidth: 36 }}
|
||||||
@ -612,7 +587,8 @@ const Viewer = ({
|
|||||||
px={8}
|
px={8}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.location.hash = `#page=${numPages}`;
|
isManualNavigationRef.current = true;
|
||||||
|
setCurrentPage(numPages);
|
||||||
}}
|
}}
|
||||||
disabled={currentPage === numPages}
|
disabled={currentPage === numPages}
|
||||||
style={{ minWidth: 36 }}
|
style={{ minWidth: 36 }}
|
||||||
|
@ -169,7 +169,7 @@ export default function HomePage() {
|
|||||||
/>
|
/>
|
||||||
{/* Main content area */}
|
{/* Main content area */}
|
||||||
<Box
|
<Box
|
||||||
className="flex-1 min-h-0 margin-top-200 relative z-10"
|
className="flex-1 min-h-0 relative z-10"
|
||||||
style={{
|
style={{
|
||||||
transition: 'opacity 0.15s ease-in-out',
|
transition: 'opacity 0.15s ease-in-out',
|
||||||
}}
|
}}
|
||||||
|
Loading…
Reference in New Issue
Block a user