diff --git a/templates/bookshelf.templ b/templates/bookshelf.templ index 307969b..a38b3d5 100644 --- a/templates/bookshelf.templ +++ b/templates/bookshelf.templ @@ -247,6 +247,7 @@ templ BookShelf(

Saved Filters

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "
= 2) fetchAuthorValues($el)\">
= 2) fetchTagValues($el)\">
= 2) fetchSeriesValues($el)\">
= 2) fetchLanguageValues($el)\">

Saved Filters

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -140,7 +140,7 @@ func BookShelf( var templ_7745c5c3_Var6 string templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(uuidToString(filter.ID)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 275, Col: 173} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 277, Col: 173} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) if templ_7745c5c3_Err != nil { @@ -153,7 +153,7 @@ func BookShelf( var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(filter.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 277, Col: 27} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 279, Col: 27} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -194,7 +194,7 @@ func BookShelf( var templ_7745c5c3_Var8 string templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(errorMessage) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 311, Col: 59} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 313, Col: 59} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { @@ -227,7 +227,7 @@ func BookShelf( var templ_7745c5c3_Var9 string templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(offset/limit + 1) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 330, Col: 32} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 332, Col: 32} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) if templ_7745c5c3_Err != nil { diff --git a/web/src/bookshelf.ts b/web/src/bookshelf.ts index 3412db4..d76b17c 100644 --- a/web/src/bookshelf.ts +++ b/web/src/bookshelf.ts @@ -80,6 +80,8 @@ Alpine.data("bookshelf", () => ({ showSaveModal: false, showFiltersDropdown: false, hasCoverState: null as boolean | null, + dropdownAlign: "right" as "left" | "right", + resizeTimeout: null as number | null, // Standalone function references (don't access component state) clearFilters, @@ -89,9 +91,52 @@ Alpine.data("bookshelf", () => ({ showSaveFilterModal() { this.showSaveModal = true; }, - // Toggle the filters dropdown + // Helper method to calculate alignment + calculateAlignment() { + const button = document.querySelector("[data-load-filter-btn]"); + if (!button) return; + + const rect = button.getBoundingClientRect(); + const viewportWidth = window.innerWidth; + const dropdownWidth = 320; + + const spaceOnLeft = rect.left; + const spaceOnRight = viewportWidth - rect.right; + + if (spaceOnRight >= dropdownWidth) { + this.dropdownAlign = "left"; + } else if (spaceOnLeft >= dropdownWidth) { + this.dropdownAlign = "right"; + } else { + this.dropdownAlign = spaceOnLeft > spaceOnRight ? "right" : "left"; + } + }, + toggleFiltersDropdown() { this.showFiltersDropdown = !this.showFiltersDropdown; + + if (this.showFiltersDropdown) { + this.$nextTick(() => { + this.calculateAlignment(); + }); + } + }, + // Alpine lifecycle hook - runs when component initializes + init() { + let rafId: number | null = null; + + window.addEventListener("resize", () => { + if (rafId !== null) { + cancelAnimationFrame(rafId); + } + + rafId = requestAnimationFrame(() => { + if (this.showFiltersDropdown) { + this.calculateAlignment(); + } + rafId = null; + }); + }); }, // Cycle through has_cover states: null -> true -> false -> null cycleHasCover(): void {