Run code formatting (prettier/eslint) on existing reader format files that were not migrated to the new structure. This ensures consistent code style across the codebase. ## Changes ### Formatting Applied - Added newlines at end of files - Fixed line length and indentation - Updated import statements for consistency - Applied code style rules uniformly ### Files Affected - **Comic**: background-color, chapter-markers, page-order, panel-gap - **Ebook**: font-loader, search, view-modes - **Manga**: vertical-scroll-mode - **PDF**: annotation-layer, pdf-navigation, pdf-text-selection ## Purpose These files will be removed in subsequent commits as they are replaced by the new modular structure. The formatting ensures consistency during the transition period and maintains code quality standards. Note: These are non-functional formatting changes only. No logic or behavior was modified in this commit.
216 lines
5.5 KiB
TypeScript
216 lines
5.5 KiB
TypeScript
// PDF navigation: page turning, zoom, fit modes
|
|
// Feature Registration Pattern implementation
|
|
|
|
import { ReaderContext } from "../core/reader-context";
|
|
|
|
export function init(context: ReaderContext): void {
|
|
let navState: PDFNavigationState | null = null;
|
|
|
|
context.events.on(
|
|
"reader:loaded",
|
|
(detail: { container: HTMLElement; totalPages: number }) => {
|
|
navState = {
|
|
currentPage: 1,
|
|
totalPages: detail.totalPages,
|
|
currentScale: 1.0,
|
|
fitMode: "fit-width",
|
|
scrollContainer:
|
|
detail.container.querySelector(".pdf-scroll-container") ||
|
|
detail.container,
|
|
};
|
|
setupPDFKeyboardNav(context, navState);
|
|
setupPDFScrollTracking(context, navState);
|
|
},
|
|
);
|
|
|
|
context.events.on("pdf:navigate:to-page", (detail: { page: number }) => {
|
|
if (navState) {
|
|
goToPDFPage(navState, detail.page, context);
|
|
}
|
|
});
|
|
|
|
context.events.on("pdf:navigate:next", () => {
|
|
if (navState) {
|
|
nextPDFPage(navState, context);
|
|
}
|
|
});
|
|
|
|
context.events.on("pdf:navigate:previous", () => {
|
|
if (navState) {
|
|
previousPDFPage(navState, context);
|
|
}
|
|
});
|
|
|
|
context.events.on("pdf:zoom:set", (detail: { scale: number }) => {
|
|
if (navState) {
|
|
setPDFZoom(navState, detail.scale, context);
|
|
}
|
|
});
|
|
|
|
context.events.on("pdf:zoom:in", () => {
|
|
if (navState) {
|
|
zoomPDFIn(navState, context);
|
|
}
|
|
});
|
|
|
|
context.events.on("pdf:zoom:out", () => {
|
|
if (navState) {
|
|
zoomPDFOut(navState, context);
|
|
}
|
|
});
|
|
|
|
context.events.on(
|
|
"pdf:fit:set",
|
|
(detail: { mode: "fit-width" | "fit-page" | "fit-height" | "none" }) => {
|
|
if (navState) {
|
|
setPDFFitMode(navState, detail.mode, context);
|
|
}
|
|
},
|
|
);
|
|
|
|
context.events.on("reader:unload", () => {
|
|
navState = null;
|
|
});
|
|
}
|
|
|
|
type PageFitMode = "fit-width" | "fit-page" | "fit-height" | "none";
|
|
|
|
interface PDFNavigationState {
|
|
currentPage: number;
|
|
totalPages: number;
|
|
currentScale: number;
|
|
fitMode: PageFitMode;
|
|
scrollContainer: HTMLElement | null;
|
|
}
|
|
|
|
function goToPDFPage(
|
|
state: PDFNavigationState,
|
|
pageNumber: number,
|
|
context: ReaderContext,
|
|
): void {
|
|
if (pageNumber < 1 || pageNumber > state.totalPages) return;
|
|
|
|
state.currentPage = pageNumber;
|
|
scrollToPDFPage(state, pageNumber);
|
|
context.events.emit("pdf:page-changed", { page: pageNumber });
|
|
}
|
|
|
|
function nextPDFPage(state: PDFNavigationState, context: ReaderContext): void {
|
|
if (state.currentPage < state.totalPages) {
|
|
goToPDFPage(state, state.currentPage + 1, context);
|
|
}
|
|
}
|
|
|
|
function previousPDFPage(
|
|
state: PDFNavigationState,
|
|
context: ReaderContext,
|
|
): void {
|
|
if (state.currentPage > 1) {
|
|
goToPDFPage(state, state.currentPage - 1, context);
|
|
}
|
|
}
|
|
|
|
function scrollToPDFPage(state: PDFNavigationState, pageNumber: number): void {
|
|
if (!state.scrollContainer) return;
|
|
|
|
const pageElement = state.scrollContainer.querySelector(
|
|
`[data-page-number="${pageNumber}"]`,
|
|
);
|
|
if (pageElement) {
|
|
pageElement.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
}
|
|
}
|
|
|
|
function setPDFZoom(
|
|
state: PDFNavigationState,
|
|
scale: number,
|
|
context: ReaderContext,
|
|
): void {
|
|
state.currentScale = scale;
|
|
state.fitMode = "none";
|
|
updatePDFZoom(state);
|
|
context.events.emit("pdf:zoom-changed", { scale });
|
|
}
|
|
|
|
function setPDFFitMode(
|
|
state: PDFNavigationState,
|
|
mode: PageFitMode,
|
|
context: ReaderContext,
|
|
): void {
|
|
state.fitMode = mode;
|
|
updatePDFZoom(state);
|
|
context.events.emit("pdf:fit-changed", { mode });
|
|
}
|
|
|
|
function zoomPDFIn(state: PDFNavigationState, context: ReaderContext): void {
|
|
setPDFZoom(state, state.currentScale * 1.2, context);
|
|
}
|
|
|
|
function zoomPDFOut(state: PDFNavigationState, context: ReaderContext): void {
|
|
setPDFZoom(state, state.currentScale / 1.2, context);
|
|
}
|
|
|
|
function updatePDFZoom(state: PDFNavigationState): void {
|
|
const event = new CustomEvent("pdf-update-zoom", {
|
|
detail: {
|
|
scale: state.currentScale,
|
|
fitMode: state.fitMode,
|
|
},
|
|
});
|
|
window.dispatchEvent(event);
|
|
}
|
|
|
|
function setupPDFKeyboardNav(
|
|
context: ReaderContext,
|
|
state: PDFNavigationState,
|
|
): void {
|
|
document.addEventListener("keydown", (e) => {
|
|
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
|
|
nextPDFPage(state, context);
|
|
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
|
|
previousPDFPage(state, context);
|
|
} else if (e.key === "+" || e.key === "=") {
|
|
zoomPDFIn(state, context);
|
|
} else if (e.key === "-" || e.key === "_") {
|
|
zoomPDFOut(state, context);
|
|
}
|
|
});
|
|
}
|
|
|
|
function setupPDFScrollTracking(
|
|
context: ReaderContext,
|
|
state: PDFNavigationState,
|
|
): void {
|
|
if (!state.scrollContainer) return;
|
|
|
|
state.scrollContainer.addEventListener("scroll", () => {
|
|
const currentPage = getCurrentPDFPage(state);
|
|
if (currentPage !== state.currentPage) {
|
|
state.currentPage = currentPage;
|
|
context.events.emit("pdf:page-changed", { page: currentPage });
|
|
}
|
|
});
|
|
}
|
|
|
|
function getCurrentPDFPage(state: PDFNavigationState): number {
|
|
if (!state.scrollContainer) return state.currentPage;
|
|
|
|
const containerRect = state.scrollContainer.getBoundingClientRect();
|
|
const viewportMiddle = containerRect.top + containerRect.height / 2;
|
|
|
|
for (let i = 1; i <= state.totalPages; i++) {
|
|
const pageElement = state.scrollContainer.querySelector(
|
|
`[data-page-number="${i}"]`,
|
|
);
|
|
if (pageElement) {
|
|
const rect = pageElement.getBoundingClientRect();
|
|
if (rect.top <= viewportMiddle && rect.bottom >= viewportMiddle) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return state.currentPage;
|
|
}
|
|
|