From be6b974150e4571698c6c7fa4d760a389756bdea Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Sun, 7 Jun 2026 10:54:58 +0200 Subject: [PATCH] feat(frontend): mobile catalog with sheet filters + sort (0.57.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 2 of the mobile redesign: the catalog at `/` adapts to phone viewports without disrupting the desktop layout. The same filter form renders inline on desktop and inside a bottom sheet on mobile, and a dedicated sort sheet replaces the inline ` + Any + + + + + + +
+ Genres (all must match) +
+ {#each allGenres as g (g.id)} + {@const on = selectedGenres.some((x) => x.id === g.id)} + + {/each} +
+
+ +
+ Tags (all must match) + {#if selectedTags.length > 0} +
+ {#each selectedTags as t (t.id)} + removeTag(t)} + removeLabel={`Remove tag ${t.name}`} + testid={`tag-filter-chip-${t.name}`} + /> + {/each} +
+ {/if} + +
+ + {#if activeFilterCount > 0} + + {/if} +{/snippet} +

Mangas

{activeFilterCount} {/if} + - {#if filtersOpen} -
-
- Status -
- - - -
-
-
- Genres (all must match) -
- {#each allGenres as g (g.id)} - {@const on = selectedGenres.some((x) => x.id === g.id)} - - {/each} -
-
- -
- Tags (all must match) - {#if selectedTags.length > 0} -
- {#each selectedTags as t (t.id)} - removeTag(t)} - removeLabel={`Remove tag ${t.name}`} - testid={`tag-filter-chip-${t.name}`} - /> - {/each} -
- {/if} - -
- - {#if activeFilterCount > 0} - + {#if activeFilterCount > 0} +
+ {#if statusFilter} + {/if} + {#each selectedGenres as g (g.id)} + toggleGenre(g)} + removeLabel={`Remove genre ${g.name}`} + testid={`active-filter-genre-${g.name}`} + /> + {/each} + {#each selectedTags as t (t.id)} + removeTag(t)} + removeLabel={`Remove tag ${t.name}`} + testid={`active-filter-tag-${t.name}`} + /> + {/each}
{/if} -
+ + {#if filtersOpen && !isMobileViewport} +
+ {@render filterFormContent()} +
+ {/if} + +
+ +
+ + {#if loading}

Loading…

{:else if error} @@ -476,6 +610,29 @@ text-align: center; } + .sort-chip { + display: inline-flex; + align-items: center; + gap: var(--space-2); + padding: 0 var(--space-3); + height: 36px; + background: var(--surface); + border: 1px solid var(--border-strong); + color: var(--text); + cursor: pointer; + } + + .sort-chip:hover { + background: var(--surface-elevated); + border-color: var(--primary); + } + + .active-filters { + display: flex; + flex-wrap: wrap; + gap: var(--space-2); + } + .filters-panel { display: flex; flex-direction: column; @@ -611,6 +768,20 @@ width: auto; } + .sort-options { + display: flex; + flex-direction: column; + gap: var(--space-3); + } + + .sort-options label { + display: flex; + align-items: center; + gap: var(--space-3); + padding: var(--space-2) 0; + cursor: pointer; + } + .icon-btn { display: inline-flex; align-items: center; @@ -653,4 +824,33 @@ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--space-4); } + + /* Mobile-only helpers. Defaults flip below the 640px breakpoint + so the same chrome doesn't render twice. */ + .mobile-only { + display: none; + } + + @media (max-width: 640px) { + .desktop-only { + display: none; + } + + .mobile-only { + display: inline-flex; + } + + .search { + /* Catalog search should fill the available width on phones — + the 28rem cap is desktop-only. */ + max-width: none; + } + + .manga-grid { + /* Lock to two columns on phones; the auto-fill default + packs 2-3 unpredictably at narrow widths. */ + grid-template-columns: repeat(2, 1fr); + gap: var(--space-3); + } + }