|
|
|
|
@@ -1,5 +1,6 @@
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
import { enhance } from '$app/forms';
|
|
|
|
|
import { goto } from '$app/navigation';
|
|
|
|
|
import { navigating } from '$app/state';
|
|
|
|
|
import { untrack } from 'svelte';
|
|
|
|
|
import type { PageData, ActionData } from './$types';
|
|
|
|
|
@@ -7,6 +8,29 @@
|
|
|
|
|
|
|
|
|
|
let { data, form }: { data: PageData; form: ActionData } = $props();
|
|
|
|
|
|
|
|
|
|
// ── Local filter state (mirrors URL params) ──────────────────────────────
|
|
|
|
|
// These are separate from data.* so we can bind them to selects and keep
|
|
|
|
|
// the DOM in sync. They sync back from data whenever a navigation completes.
|
|
|
|
|
let filterSort = $state(untrack(() => data.sort));
|
|
|
|
|
let filterGenre = $state(untrack(() => data.genre));
|
|
|
|
|
let filterStatus = $state(untrack(() => data.status));
|
|
|
|
|
|
|
|
|
|
// Keep local state in sync whenever SvelteKit re-runs the load (URL changed).
|
|
|
|
|
$effect(() => {
|
|
|
|
|
filterSort = data.sort;
|
|
|
|
|
filterGenre = data.genre;
|
|
|
|
|
filterStatus = data.status;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function navigateWithFilters(overrides: { sort?: string; genre?: string; status?: string }) {
|
|
|
|
|
const params = new URLSearchParams();
|
|
|
|
|
params.set('sort', overrides.sort ?? filterSort);
|
|
|
|
|
params.set('genre', overrides.genre ?? filterGenre);
|
|
|
|
|
params.set('status', overrides.status ?? filterStatus);
|
|
|
|
|
params.set('page', '1');
|
|
|
|
|
goto(`/catalogue?${params.toString()}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Track which novel card is currently being navigated to
|
|
|
|
|
let loadingSlug = $state<string | null>(null);
|
|
|
|
|
|
|
|
|
|
@@ -389,11 +413,12 @@
|
|
|
|
|
<select
|
|
|
|
|
id="filter-sort"
|
|
|
|
|
name="sort"
|
|
|
|
|
value={data.sort}
|
|
|
|
|
bind:value={filterSort}
|
|
|
|
|
onchange={() => navigateWithFilters({ sort: filterSort })}
|
|
|
|
|
class="bg-zinc-900 border border-zinc-700 text-zinc-200 text-sm rounded px-3 py-2 focus:outline-none focus:border-amber-400 w-full"
|
|
|
|
|
>
|
|
|
|
|
{#each sorts as s}
|
|
|
|
|
<option value={s.value}>{s.label}</option>
|
|
|
|
|
<option value={s.value} selected={s.value === filterSort}>{s.label}</option>
|
|
|
|
|
{/each}
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
@@ -403,12 +428,13 @@
|
|
|
|
|
<select
|
|
|
|
|
id="filter-genre"
|
|
|
|
|
name="genre"
|
|
|
|
|
value={data.genre}
|
|
|
|
|
bind:value={filterGenre}
|
|
|
|
|
onchange={() => navigateWithFilters({ genre: filterGenre })}
|
|
|
|
|
disabled={isRankView}
|
|
|
|
|
class="bg-zinc-900 border border-zinc-700 text-zinc-200 text-sm rounded px-3 py-2 focus:outline-none focus:border-amber-400 disabled:opacity-40 disabled:cursor-not-allowed w-full"
|
|
|
|
|
>
|
|
|
|
|
{#each genres as g}
|
|
|
|
|
<option value={g.value}>{g.label}</option>
|
|
|
|
|
<option value={g.value} selected={g.value === filterGenre}>{g.label}</option>
|
|
|
|
|
{/each}
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
@@ -418,12 +444,13 @@
|
|
|
|
|
<select
|
|
|
|
|
id="filter-status"
|
|
|
|
|
name="status"
|
|
|
|
|
value={data.status}
|
|
|
|
|
bind:value={filterStatus}
|
|
|
|
|
onchange={() => navigateWithFilters({ status: filterStatus })}
|
|
|
|
|
disabled={isRankView}
|
|
|
|
|
class="bg-zinc-900 border border-zinc-700 text-zinc-200 text-sm rounded px-3 py-2 focus:outline-none focus:border-amber-400 disabled:opacity-40 disabled:cursor-not-allowed w-full"
|
|
|
|
|
>
|
|
|
|
|
{#each statuses as st}
|
|
|
|
|
<option value={st.value}>{st.label}</option>
|
|
|
|
|
<option value={st.value} selected={st.value === filterStatus}>{st.label}</option>
|
|
|
|
|
{/each}
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
@@ -437,13 +464,6 @@
|
|
|
|
|
<a href="/catalogue" class="px-4 py-2 rounded bg-zinc-700 text-zinc-300 text-sm hover:bg-zinc-600 transition-colors">
|
|
|
|
|
Reset
|
|
|
|
|
</a>
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
onclick={() => (filtersOpen = false)}
|
|
|
|
|
class="px-4 py-2 rounded bg-amber-400 text-zinc-900 text-sm font-semibold hover:bg-amber-300 transition-colors"
|
|
|
|
|
>
|
|
|
|
|
Apply
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
{/if}
|
|
|
|
|
|