From a9bbd1ee2ef59f3db3da7a2cb81a61b332432ddb Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 15 Mar 2026 21:02:58 -0400 Subject: [PATCH] refactor(bookshelf): migrate from DOM manipulation to Alpine.js reactive state Replace direct DOM manipulation with Alpine.js reactive state variables: - Add isLoading and hasBooks state to bookshelf component - Convert loadBookshelf() to update isLoading state instead of toggling DOM visibility - Convert renderBookshelf() to use reactive state for empty state handling - Remove redundant getElementById() calls for loading/empty-state elements This change improves maintainability by: - Centralizing UI state in the Alpine component - Eliminating direct DOM manipulation scattered across functions - Making the component's state more explicit and trackable - Following Alpine.js reactive programming patterns The UI will now respond to state changes automatically rather than requiring manual DOM updates throughout the lifecycle methods. --- web/src/bookshelf.ts | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/web/src/bookshelf.ts b/web/src/bookshelf.ts index ab7996f..cd73744 100644 --- a/web/src/bookshelf.ts +++ b/web/src/bookshelf.ts @@ -33,13 +33,7 @@ async function loadBookshelf(libraryId: string): Promise { const token = localStorage.getItem("token"); if (!token || !libraryId) return; - const loading = document.getElementById("loading"); - const booksGrid = document.getElementById("books-grid"); - const emptyState = document.getElementById("empty-state"); - - if (loading) loading.style.display = "block"; - if (booksGrid) booksGrid.classList.add("hidden"); - if (emptyState) emptyState.classList.add("hidden"); + this.isLoading = true; try { const response = await fetch( @@ -60,8 +54,7 @@ async function loadBookshelf(libraryId: string): Promise { } catch (error) { console.error("Error loading bookshelf:", error); showToast("Error loading books", "error"); - } finally { - if (loading) loading.style.display = "none"; + this.isLoading = false; } } @@ -76,21 +69,14 @@ function showEmptyState(): void { } function renderBookshelf(): void { - const booksGrid = document.getElementById("books-grid"); - const emptyState = document.getElementById("empty-state"); - const loading = document.getElementById("loading"); - - if (!booksGrid) return; - - if (loading) loading.style.display = "none"; - if (emptyState) emptyState.classList.add("hidden"); - if (!mediaItems || mediaItems.length === 0) { - showEmptyState(); + this.isLoading = false; + this.hasBooks = false; return; } - booksGrid.classList.remove("hidden"); + const booksGrid = document.getElementById("books-grid"); + if (!booksGrid) return; const booksPerShelf = 6; const shelves: unknown[][] = []; @@ -199,6 +185,11 @@ export { }; Alpine.data("bookshelf", () => ({ + // State Variables + isLoading: true, + hasBooks: false, + + // Methods changePage, initBookshelf, loadBookshelf,