feat(reader): wire up TOC, bookmarks, and navigator panels with Alpine.js bindings
Replace dead data-action attributes with Alpine.js @click handlers and x-ref references across all reader panels: - TOC panel: replaced static <nav> with x-for loop over tocItems array, added goToTOCItem() click handler, window-shade toggle via .tocPanel - Bookmarks panel: replaced data-action with @click.prevent handlers, added goToBookmarkTarget() using data-cfi attributes for navigation, window-shade toggle via .bookmarksPanel - Navigator panel: replaced data-action with @click window-shade toggle via .navigatorPanel - Added goToBookmarkTarget() and toggleWindowShade() methods to reader.ts - Removed unused panel-lock buttons (lock feature not yet implemented) - Regenerated reader_templ.go, rebuilt CSS and JS bundles
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import "foliate-js/view.js";
|
||||
import { Alpine } from "../alpine";
|
||||
import { loadSettings } from "./settings-manager";
|
||||
import { loadSettings, saveSettings } from "./settings-manager";
|
||||
import { getToken } from "../storage";
|
||||
const getCSS = ({
|
||||
spacing,
|
||||
@@ -79,6 +79,15 @@ document.addEventListener("alpine:init", () => {
|
||||
justify: true,
|
||||
hyphenate: true,
|
||||
},
|
||||
tocOpen: false,
|
||||
settingsOpen: false,
|
||||
bookmarksOpen: false,
|
||||
navigatorOpen: false,
|
||||
tocItems: [] as any[],
|
||||
readingTheme: "dark" as string,
|
||||
readingFont: "literata" as string,
|
||||
fontSize: 16 as number,
|
||||
lineHeight: 1.6 as number,
|
||||
async initReader(config: {
|
||||
mediaItemId: string;
|
||||
fileUrl: string;
|
||||
@@ -87,8 +96,14 @@ document.addEventListener("alpine:init", () => {
|
||||
mangaType: string;
|
||||
}) {
|
||||
this.settings = await loadSettings();
|
||||
if (this.settings?.reading_theme) {
|
||||
document.body.classList.add(`theme-${this.settings.reading_theme}`);
|
||||
if (this.settings) {
|
||||
this.readingTheme = this.settings.reading_theme || "dark";
|
||||
this.readingFont = this.settings.reading_font || "literata";
|
||||
this.fontSize = this.settings.font_size || 16;
|
||||
this.lineHeight = this.settings.line_height || 1.6;
|
||||
if (this.settings.reading_theme) {
|
||||
document.body.classList.add(`theme-${this.settings.reading_theme}`);
|
||||
}
|
||||
}
|
||||
this.view = document.getElementById("reader-view") as any;
|
||||
const resp = await fetch(config.fileUrl, {
|
||||
@@ -200,6 +215,87 @@ document.addEventListener("alpine:init", () => {
|
||||
goToFraction(value: string) {
|
||||
this.view?.goToFraction?.(parseFloat(value));
|
||||
},
|
||||
toggleTOC() {
|
||||
this.tocOpen = !this.tocOpen;
|
||||
if (this.tocOpen && this.tocItems.length === 0 && this.book?.toc) {
|
||||
this.tocItems = this.flattenTOC(this.book.toc);
|
||||
}
|
||||
},
|
||||
toggleSettings() {
|
||||
this.settingsOpen = !this.settingsOpen;
|
||||
},
|
||||
toggleBookmarks() {
|
||||
this.bookmarksOpen = !this.bookmarksOpen;
|
||||
},
|
||||
toggleNavigator() {
|
||||
this.navigatorOpen = !this.navigatorOpen;
|
||||
},
|
||||
toggleWindowShade(panelEl: HTMLElement) {
|
||||
panelEl.classList.toggle("panel-collapsed");
|
||||
},
|
||||
flattenTOC(items: any[], depth = 0): any[] {
|
||||
const result: any[] = [];
|
||||
for (const item of items) {
|
||||
result.push({ ...item, depth });
|
||||
if (item.subitems?.length) {
|
||||
result.push(...this.flattenTOC(item.subitems, depth + 1));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
goToTOCItem(item: any) {
|
||||
if (this.view && item.href) {
|
||||
this.view.goTo(item.href);
|
||||
this.tocOpen = false;
|
||||
}
|
||||
},
|
||||
goToBookmarkTarget(cfi: string) {
|
||||
if (this.view && cfi) {
|
||||
this.view.goTo(cfi);
|
||||
this.bookmarksOpen = false;
|
||||
}
|
||||
},
|
||||
applyTheme() {
|
||||
document.body.className = document.body.className
|
||||
.replace(/theme-(?!tokyo|dracula|nord|solarized|monokai|one-dark|material|catppuccin)[\w-]+/g, "")
|
||||
.trim();
|
||||
document.body.classList.add(`theme-${this.readingTheme}`);
|
||||
this.applyStyles();
|
||||
saveSettings({ reading_theme: this.readingTheme });
|
||||
},
|
||||
applyFont() {
|
||||
this.applyStyles();
|
||||
saveSettings({
|
||||
reading_font: this.readingFont,
|
||||
font_size: this.fontSize,
|
||||
line_height: this.lineHeight,
|
||||
});
|
||||
},
|
||||
applyStyles() {
|
||||
if (!this.renderer?.setStyles) return;
|
||||
this.renderer.setStyles(getCSS(this.style));
|
||||
},
|
||||
async addBookmark() {
|
||||
const token = getToken();
|
||||
if (!token || !this.view) return;
|
||||
const location = this.view.lastLocation;
|
||||
if (!location) return;
|
||||
try {
|
||||
await fetch("/readers/bookmarks", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
title: `Bookmark at ${this.progressText}`,
|
||||
position: JSON.stringify(location),
|
||||
}),
|
||||
});
|
||||
} catch (_e) {
|
||||
/* ignore bookmark errors for now */
|
||||
}
|
||||
},
|
||||
handleKeydown(event: KeyboardEvent) {
|
||||
const k = event.key;
|
||||
if (k === "ArrowLeft" || k === "h") this.goLeft();
|
||||
|
||||
Reference in New Issue
Block a user