feat: auto-close sidebar on navigation

- Added beforeNavigate hook to close sidebar when navigating between pages
- Prevents search sidebar from staying open when browsing different sections
- Ensures clean UI state transitions between pages
This commit is contained in:
Justin Edmund 2025-09-20 21:36:49 -07:00
parent fc711a7a5d
commit 9ed6a00f5f
3 changed files with 65 additions and 45 deletions

View file

@ -9,7 +9,7 @@
import WeaponGrid from '$lib/components/grids/WeaponGrid.svelte'
import SummonGrid from '$lib/components/grids/SummonGrid.svelte'
import CharacterGrid from '$lib/components/grids/CharacterGrid.svelte'
import SearchSidebar from '$lib/components/panels/SearchSidebar.svelte'
import { openSearchSidebar } from '$lib/features/search/openSearchSidebar.svelte'
import PartySegmentedControl from '$lib/components/party/PartySegmentedControl.svelte'
import type { SearchResult } from '$lib/api/resources/search'
import { GridType } from '$lib/types/enums'
@ -42,8 +42,6 @@
let activeTab = $state<GridType>(GridType.Weapon)
let loading = $state(false)
let error = $state<string | null>(null)
let pickerOpen = $state(false)
let pickerTitle = $state('Search')
let selectedSlot = $state<number>(0)
let editDialogOpen = $state(false)
let editingTitle = $state('')
@ -383,12 +381,10 @@
}
// If there's another empty slot, update selectedSlot to it
// Otherwise close the picker (grid is full)
if (nextEmptySlot !== -999) {
selectedSlot = nextEmptySlot
} else {
pickerOpen = false
}
// Note: Sidebar stays open for continuous adding
} catch (err: any) {
error = err.message || 'Failed to add item'
} finally {
@ -577,8 +573,13 @@
: opts.type === 'summon'
? GridType.Summon
: GridType.Character
pickerTitle = `Search ${opts.type}s`
pickerOpen = true
// Open the search sidebar with the appropriate type
openSearchSidebar({
type: opts.type,
onAddItems: handleAddItems,
canAddMore: true
})
}
})
@ -586,7 +587,7 @@
setContext('drag-drop', dragContext)
</script>
<div class="page-wrap" class:with-panel={pickerOpen}>
<div class="page-wrap">
<div class="track">
<section class="party-container">
<header class="party-header">
@ -680,17 +681,6 @@
{/if}
</div>
</section>
<SearchSidebar
open={pickerOpen}
type={activeTab === GridType.Weapon
? 'weapon'
: activeTab === GridType.Summon
? 'summon'
: 'character'}
onClose={() => (pickerOpen = false)}
onAddItems={handleAddItems}
canAddMore={true}
/>
</div>
</div>

View file

@ -7,6 +7,7 @@
import Sidebar from '$lib/components/ui/Sidebar.svelte'
import { sidebar } from '$lib/stores/sidebar.svelte'
import { Tooltip } from 'bits-ui'
import { beforeNavigate } from '$app/navigation'
// Get `data` and `children` from the router via $props()
// Use a more flexible type that allows additional properties from child pages
@ -14,6 +15,11 @@
data: any // Allow any data to pass through from child pages
children: () => any
}>()
// Close sidebar when navigating to a different page
beforeNavigate(() => {
sidebar.close()
})
</script>
<svelte:head>

View file

@ -4,7 +4,7 @@
import WeaponGrid from '$lib/components/grids/WeaponGrid.svelte'
import SummonGrid from '$lib/components/grids/SummonGrid.svelte'
import CharacterGrid from '$lib/components/grids/CharacterGrid.svelte'
import SearchSidebar from '$lib/components/panels/SearchSidebar.svelte'
import { openSearchSidebar, closeSearchSidebar } from '$lib/features/search/openSearchSidebar.svelte'
import PartySegmentedControl from '$lib/components/party/PartySegmentedControl.svelte'
import { GridType } from '$lib/types/enums'
import { setContext } from 'svelte'
@ -21,16 +21,45 @@
// Local, client-only state for tab selection (Svelte 5 runes)
let activeTab = $state<GridType>(GridType.Weapon)
// Open search sidebar on mount
let hasOpenedSidebar = $state(false)
$effect(() => {
if (!hasOpenedSidebar) {
hasOpenedSidebar = true
// Small delay to let the page render first
setTimeout(() => {
openSearchSidebar({
type: 'weapon',
onAddItems: handleAddItems,
canAddMore: true
})
}, 100)
}
})
function selectTab(gridType: GridType) {
activeTab = gridType
sidebarOpen = true // Auto-open sidebar when switching tabs
// Open sidebar when switching tabs
openSearchSidebar({
type: gridType === GridType.Weapon ? 'weapon' :
gridType === GridType.Summon ? 'summon' :
'character',
onAddItems: handleAddItems,
canAddMore: !isGridFull(gridType)
})
}
// Helper to check if a grid is full
function isGridFull(gridType: GridType): boolean {
if (gridType === GridType.Weapon) return weapons.length >= 10
if (gridType === GridType.Summon) return summons.length >= 6
return characters.length >= 5
}
// Grid state
let weapons = $state<any[]>([])
let summons = $state<any[]>([])
let characters = $state<any[]>([])
let sidebarOpen = $state(true) // Start with sidebar open
let selectedSlot = $state<number | null>(null)
let isFirstItemForSlot = false // Track if this is the first item after clicking empty cell
@ -45,17 +74,6 @@
let errorMessage = $state('')
let errorDetails = $state<string[]>([])
// Update CSS variable when sidebar opens/closes
$effect(() => {
if (typeof document !== 'undefined') {
const root = document.documentElement
if (sidebarOpen) {
root.style.setProperty('--main-max-width', '1140px')
} else {
root.style.setProperty('--main-max-width', '820px')
}
}
})
// Calculate if grids are full
let isWeaponGridFull = $derived(weapons.length >= 10) // 1 mainhand + 9 grid slots
@ -494,7 +512,15 @@
},
openPicker: (opts: { type: 'weapon' | 'summon' | 'character'; position: number; item?: any }) => {
selectedSlot = opts.position
sidebarOpen = true
openSearchSidebar({
type: opts.type,
onAddItems: handleAddItems,
canAddMore: !isGridFull(
opts.type === 'weapon' ? GridType.Weapon :
opts.type === 'summon' ? GridType.Summon :
GridType.Character
)
})
}
})
</script>
@ -507,8 +533,14 @@
<h1>Create a new team</h1>
<p class="description">Search and click items to add them to your grid</p>
</div>
<button class="toggle-sidebar" on:click={() => sidebarOpen = !sidebarOpen}>
{sidebarOpen ? 'Hide' : 'Show'} Search
<button class="toggle-sidebar" on:click={() => openSearchSidebar({
type: activeTab === GridType.Weapon ? 'weapon' :
activeTab === GridType.Summon ? 'summon' :
'character',
onAddItems: handleAddItems,
canAddMore: !isGridFull(activeTab)
})}>
Open Search
</button>
</header>
@ -535,14 +567,6 @@
{/if}
</div>
</section>
<SearchSidebar
open={sidebarOpen}
type={activeTab === GridType.Weapon ? 'weapon' : activeTab === GridType.Summon ? 'summon' : 'character'}
onClose={() => sidebarOpen = false}
onAddItems={handleAddItems}
canAddMore={canAddMore}
/>
</div>
</main>