feat(ui): persist library selection across page navigation

Save the selected library to localStorage when the user changes the
dropdown on the bookshelf page, and restore it on every page load via
a new restoreLibrarySelection() call in the header Alpine component.

This ensures that when a user navigates between dashboard, bookshelf,
collections, etc., their last-chosen library filter is automatically
re-applied rather than resetting to the default.

Changes:
- web/src/bookshelf.ts: listen for change events on #library-select
  and persist the value to localStorage
- web/src/header.ts: add restoreLibrarySelection() which checks
  localStorage and sets the matching dropdown option on page load
- templates/header.templ: call restoreLibrarySelection() in x-init
- templates/header_templ.go: regenerated from templ source
This commit is contained in:
2026-04-26 21:21:40 -04:00
parent 37f84dd3ea
commit bc58103c62
4 changed files with 35 additions and 3 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
package templates
templ Header(user User, currentPath string) {
<nav x-data="header" x-init="initializeSearch(); initializeTheme(); loadWoodPaneling(); updateWoodPanelingIndicators()" class="border-b header-nav" style="border-color: var(--border); background-color: var(--bg-secondary);">
<nav x-data="header" x-init="initializeSearch(); initializeTheme(); loadWoodPaneling(); updateWoodPanelingIndicators(); restoreLibrarySelection()" class="border-b header-nav" style="border-color: var(--border); background-color: var(--bg-secondary);">
<div x-data="{}" x-init="console.log('Alpine is working!', $el)"></div>
<div class="w-full px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
File diff suppressed because one or more lines are too long
+3
View File
@@ -415,6 +415,9 @@ function initBookshelf(): void {
const librarySelect = document.getElementById(
"library-select",
) as HTMLSelectElement;
librarySelect?.addEventListener("change", () => {
localStorage.setItem("selectedLibrary", librarySelect.value);
});
if (librarySelect && librarySelect.value) {
const filterForm = document.getElementById(
"filter-form",
+30 -1
View File
@@ -9,6 +9,7 @@ import {
loadWoodPaneling,
updateWoodPanelingIndicators,
} from "./theme";
import { getSelectedLibrary } from "./storage";
const logout = (): void => {
localStorage.removeItem("token");
@@ -16,7 +17,34 @@ const logout = (): void => {
window.location.href = "/";
};
export { logout };
// Looks for saved library in localStorage and selects it in current page's dropdown
const restoreLibrarySelection = (): void => {
const savedLibrary = getSelectedLibrary();
if (!savedLibrary) return;
// Common library dropdown IDs across pages
const dropdownSelectors = [
"#library-select", // dashboard, bookshelf
"#selected-library", // alternate naming
"#library_id", // form field
];
for (const selector of dropdownSelectors) {
const dropdown = document.querySelector(selector) as HTMLSelectElement;
if (dropdown) {
const option = dropdown.querySelector(`option[value="${savedLibrary}"]`);
if (option) {
dropdown.value = savedLibrary;
// Trigger any htmx/change handlers
dropdown.dispatchEvent(new Event("change", { bubbles: true }));
console.log("Restored library selection:", savedLibrary);
return;
}
}
}
};
export { logout, restoreLibrarySelection };
Alpine.data("header", () => ({
logout,
@@ -26,4 +54,5 @@ Alpine.data("header", () => ({
changeWoodPaneling,
loadWoodPaneling,
updateWoodPanelingIndicators,
restoreLibrarySelection,
}));