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:
parent
fc711a7a5d
commit
9ed6a00f5f
3 changed files with 65 additions and 45 deletions
|
|
@ -9,7 +9,7 @@
|
||||||
import WeaponGrid from '$lib/components/grids/WeaponGrid.svelte'
|
import WeaponGrid from '$lib/components/grids/WeaponGrid.svelte'
|
||||||
import SummonGrid from '$lib/components/grids/SummonGrid.svelte'
|
import SummonGrid from '$lib/components/grids/SummonGrid.svelte'
|
||||||
import CharacterGrid from '$lib/components/grids/CharacterGrid.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 PartySegmentedControl from '$lib/components/party/PartySegmentedControl.svelte'
|
||||||
import type { SearchResult } from '$lib/api/resources/search'
|
import type { SearchResult } from '$lib/api/resources/search'
|
||||||
import { GridType } from '$lib/types/enums'
|
import { GridType } from '$lib/types/enums'
|
||||||
|
|
@ -42,8 +42,6 @@
|
||||||
let activeTab = $state<GridType>(GridType.Weapon)
|
let activeTab = $state<GridType>(GridType.Weapon)
|
||||||
let loading = $state(false)
|
let loading = $state(false)
|
||||||
let error = $state<string | null>(null)
|
let error = $state<string | null>(null)
|
||||||
let pickerOpen = $state(false)
|
|
||||||
let pickerTitle = $state('Search')
|
|
||||||
let selectedSlot = $state<number>(0)
|
let selectedSlot = $state<number>(0)
|
||||||
let editDialogOpen = $state(false)
|
let editDialogOpen = $state(false)
|
||||||
let editingTitle = $state('')
|
let editingTitle = $state('')
|
||||||
|
|
@ -383,12 +381,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's another empty slot, update selectedSlot to it
|
// If there's another empty slot, update selectedSlot to it
|
||||||
// Otherwise close the picker (grid is full)
|
|
||||||
if (nextEmptySlot !== -999) {
|
if (nextEmptySlot !== -999) {
|
||||||
selectedSlot = nextEmptySlot
|
selectedSlot = nextEmptySlot
|
||||||
} else {
|
|
||||||
pickerOpen = false
|
|
||||||
}
|
}
|
||||||
|
// Note: Sidebar stays open for continuous adding
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
error = err.message || 'Failed to add item'
|
error = err.message || 'Failed to add item'
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -577,8 +573,13 @@
|
||||||
: opts.type === 'summon'
|
: opts.type === 'summon'
|
||||||
? GridType.Summon
|
? GridType.Summon
|
||||||
: GridType.Character
|
: 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)
|
setContext('drag-drop', dragContext)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="page-wrap" class:with-panel={pickerOpen}>
|
<div class="page-wrap">
|
||||||
<div class="track">
|
<div class="track">
|
||||||
<section class="party-container">
|
<section class="party-container">
|
||||||
<header class="party-header">
|
<header class="party-header">
|
||||||
|
|
@ -680,17 +681,6 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<SearchSidebar
|
|
||||||
open={pickerOpen}
|
|
||||||
type={activeTab === GridType.Weapon
|
|
||||||
? 'weapon'
|
|
||||||
: activeTab === GridType.Summon
|
|
||||||
? 'summon'
|
|
||||||
: 'character'}
|
|
||||||
onClose={() => (pickerOpen = false)}
|
|
||||||
onAddItems={handleAddItems}
|
|
||||||
canAddMore={true}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
import Sidebar from '$lib/components/ui/Sidebar.svelte'
|
import Sidebar from '$lib/components/ui/Sidebar.svelte'
|
||||||
import { sidebar } from '$lib/stores/sidebar.svelte'
|
import { sidebar } from '$lib/stores/sidebar.svelte'
|
||||||
import { Tooltip } from 'bits-ui'
|
import { Tooltip } from 'bits-ui'
|
||||||
|
import { beforeNavigate } from '$app/navigation'
|
||||||
|
|
||||||
// Get `data` and `children` from the router via $props()
|
// Get `data` and `children` from the router via $props()
|
||||||
// Use a more flexible type that allows additional properties from child pages
|
// 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
|
data: any // Allow any data to pass through from child pages
|
||||||
children: () => any
|
children: () => any
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
// Close sidebar when navigating to a different page
|
||||||
|
beforeNavigate(() => {
|
||||||
|
sidebar.close()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
import WeaponGrid from '$lib/components/grids/WeaponGrid.svelte'
|
import WeaponGrid from '$lib/components/grids/WeaponGrid.svelte'
|
||||||
import SummonGrid from '$lib/components/grids/SummonGrid.svelte'
|
import SummonGrid from '$lib/components/grids/SummonGrid.svelte'
|
||||||
import CharacterGrid from '$lib/components/grids/CharacterGrid.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 PartySegmentedControl from '$lib/components/party/PartySegmentedControl.svelte'
|
||||||
import { GridType } from '$lib/types/enums'
|
import { GridType } from '$lib/types/enums'
|
||||||
import { setContext } from 'svelte'
|
import { setContext } from 'svelte'
|
||||||
|
|
@ -21,16 +21,45 @@
|
||||||
// Local, client-only state for tab selection (Svelte 5 runes)
|
// Local, client-only state for tab selection (Svelte 5 runes)
|
||||||
let activeTab = $state<GridType>(GridType.Weapon)
|
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) {
|
function selectTab(gridType: GridType) {
|
||||||
activeTab = 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
|
// Grid state
|
||||||
let weapons = $state<any[]>([])
|
let weapons = $state<any[]>([])
|
||||||
let summons = $state<any[]>([])
|
let summons = $state<any[]>([])
|
||||||
let characters = $state<any[]>([])
|
let characters = $state<any[]>([])
|
||||||
let sidebarOpen = $state(true) // Start with sidebar open
|
|
||||||
let selectedSlot = $state<number | null>(null)
|
let selectedSlot = $state<number | null>(null)
|
||||||
let isFirstItemForSlot = false // Track if this is the first item after clicking empty cell
|
let isFirstItemForSlot = false // Track if this is the first item after clicking empty cell
|
||||||
|
|
||||||
|
|
@ -45,17 +74,6 @@
|
||||||
let errorMessage = $state('')
|
let errorMessage = $state('')
|
||||||
let errorDetails = $state<string[]>([])
|
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
|
// Calculate if grids are full
|
||||||
let isWeaponGridFull = $derived(weapons.length >= 10) // 1 mainhand + 9 grid slots
|
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 }) => {
|
openPicker: (opts: { type: 'weapon' | 'summon' | 'character'; position: number; item?: any }) => {
|
||||||
selectedSlot = opts.position
|
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>
|
</script>
|
||||||
|
|
@ -507,8 +533,14 @@
|
||||||
<h1>Create a new team</h1>
|
<h1>Create a new team</h1>
|
||||||
<p class="description">Search and click items to add them to your grid</p>
|
<p class="description">Search and click items to add them to your grid</p>
|
||||||
</div>
|
</div>
|
||||||
<button class="toggle-sidebar" on:click={() => sidebarOpen = !sidebarOpen}>
|
<button class="toggle-sidebar" on:click={() => openSearchSidebar({
|
||||||
{sidebarOpen ? 'Hide' : 'Show'} Search
|
type: activeTab === GridType.Weapon ? 'weapon' :
|
||||||
|
activeTab === GridType.Summon ? 'summon' :
|
||||||
|
'character',
|
||||||
|
onAddItems: handleAddItems,
|
||||||
|
canAddMore: !isGridFull(activeTab)
|
||||||
|
})}>
|
||||||
|
Open Search
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
@ -535,14 +567,6 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<SearchSidebar
|
|
||||||
open={sidebarOpen}
|
|
||||||
type={activeTab === GridType.Weapon ? 'weapon' : activeTab === GridType.Summon ? 'summon' : 'character'}
|
|
||||||
onClose={() => sidebarOpen = false}
|
|
||||||
onAddItems={handleAddItems}
|
|
||||||
canAddMore={canAddMore}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue