add URL-based tab routing with pushState sync

This commit is contained in:
Justin Edmund 2025-12-01 04:29:21 -08:00
parent 5b0d41a020
commit af27f0fbbc
3 changed files with 42 additions and 4 deletions

View file

@ -1,5 +1,7 @@
<script lang="ts">
import { onMount, getContext, setContext, onDestroy } from 'svelte'
import { pushState } from '$app/navigation'
import { page } from '$app/state'
import type { Party, GridCharacter, GridWeapon, GridSummon } from '$lib/types/api/party'
import { partyStore } from '$lib/stores/partyStore.svelte'
@ -65,9 +67,10 @@
party?: Party
canEdit?: boolean
authUserId?: string
initialTab?: GridType
}
let { party: initial, canEdit: canEditServer = false, authUserId }: Props = $props()
let { party: initial, canEdit: canEditServer = false, authUserId, initialTab }: Props = $props()
// Per-route local state using Svelte 5 runes
const defaultParty: Party = {
@ -95,7 +98,24 @@
partyStore.clear()
})
let activeTab = $state<GridType>(GridType.Weapon)
let activeTab = $state<GridType>(initialTab ?? GridType.Weapon)
// Map URL segment to GridType for back/forward navigation
const tabMap: Record<string, GridType> = {
weapons: GridType.Weapon,
summons: GridType.Summon,
characters: GridType.Character
}
// Sync activeTab with URL when user navigates back/forward
$effect(() => {
const urlTab = page.params.tab as string | undefined
const expectedTab = urlTab ? (tabMap[urlTab] ?? GridType.Weapon) : GridType.Weapon
if (activeTab !== expectedTab) {
activeTab = expectedTab
}
})
let loading = $state(false)
let error = $state<string | null>(null)
let selectedSlot = $state<number>(0)
@ -259,6 +279,12 @@
function handleTabChange(tab: GridType) {
activeTab = tab
// Update URL with push state (adds to browser history)
const basePath = `/teams/${party.shortcode}`
const newPath = tab === GridType.Weapon ? basePath : `${basePath}/${tab}s`
pushState(newPath, {})
// Update selectedSlot to the first valid empty slot for this tab
const nextEmpty = findNextEmptySlot(party, tab)
if (nextEmpty !== SLOT_NOT_FOUND) {

View file

@ -23,6 +23,7 @@ export const load: PageServerLoad = async ({ params, locals }) => {
party: party ? structuredClone(party) : null,
canEdit: Boolean(canEdit),
partyFound,
authUserId: authUserId || null
authUserId: authUserId || null,
tab: params.tab || null
}
}

View file

@ -4,9 +4,20 @@
import { createQuery } from '@tanstack/svelte-query'
import { partyQueries } from '$lib/api/queries/party.queries'
import { withInitialData } from '$lib/query/ssr'
import { GridType } from '$lib/types/enums'
let { data }: { data: PageData } = $props()
// Map URL segment to GridType
const tabMap: Record<string, GridType> = {
weapons: GridType.Weapon,
summons: GridType.Summon,
characters: GridType.Character
}
// Initialize from URL or default to Weapon
const initialTab = data.tab ? (tabMap[data.tab] ?? GridType.Weapon) : GridType.Weapon
/**
* TanStack Query v6 SSR Integration Example
*
@ -33,7 +44,7 @@
</script>
{#if party}
<Party party={party} canEdit={data.canEdit || false} authUserId={data.authUserId} />
<Party party={party} canEdit={data.canEdit || false} authUserId={data.authUserId} {initialTab} />
{:else}
<div>
<h1>Party not found</h1>