use visual segmented control in party views
This commit is contained in:
parent
acf49c718c
commit
cc46a695d5
2 changed files with 58 additions and 153 deletions
|
|
@ -10,7 +10,9 @@
|
||||||
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 SearchSidebar from '$lib/components/panels/SearchSidebar.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 Dialog from '$lib/components/ui/Dialog.svelte'
|
import Dialog from '$lib/components/ui/Dialog.svelte'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -38,7 +40,7 @@
|
||||||
? initial
|
? initial
|
||||||
: defaultParty
|
: defaultParty
|
||||||
)
|
)
|
||||||
let activeTab = $state<'weapons' | 'summons' | 'characters'>('weapons')
|
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 pickerOpen = $state(false)
|
||||||
|
|
@ -212,20 +214,13 @@
|
||||||
return result.canEdit
|
return result.canEdit
|
||||||
})
|
})
|
||||||
|
|
||||||
// Tab configuration - use function to avoid state capture
|
|
||||||
const tabs = $derived([
|
|
||||||
{ key: 'weapons' as const, label: 'Weapons', count: (party?.weapons ?? []).length },
|
|
||||||
{ key: 'summons' as const, label: 'Summons', count: (party?.summons ?? []).length },
|
|
||||||
{ key: 'characters' as const, label: 'Characters', count: (party?.characters ?? []).length }
|
|
||||||
])
|
|
||||||
|
|
||||||
// Derived elements for character image logic
|
// Derived elements for character image logic
|
||||||
const mainWeapon = $derived(() => (party?.weapons ?? []).find(w => w?.mainhand || w?.position === -1))
|
const mainWeapon = $derived(() => (party?.weapons ?? []).find(w => w?.mainhand || w?.position === -1))
|
||||||
const mainWeaponElement = $derived(() => mainWeapon?.element ?? mainWeapon?.weapon?.element)
|
const mainWeaponElement = $derived(() => mainWeapon?.element ?? mainWeapon?.weapon?.element)
|
||||||
const partyElement = $derived(() => party?.element)
|
const partyElement = $derived(() => party?.element)
|
||||||
|
|
||||||
function selectTab(key: typeof tabs[number]['key']) {
|
function handleTabChange(tab: GridType) {
|
||||||
activeTab = key
|
activeTab = tab
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edit dialog functions
|
// Edit dialog functions
|
||||||
|
|
@ -334,16 +329,16 @@
|
||||||
let targetSlot = selectedSlot
|
let targetSlot = selectedSlot
|
||||||
|
|
||||||
// Call appropriate API based on current tab
|
// Call appropriate API based on current tab
|
||||||
if (activeTab === 'weapons') {
|
if (activeTab === GridType.Weapon) {
|
||||||
await apiClient.addWeapon(party.id, item.granblue_id, targetSlot, {
|
await apiClient.addWeapon(party.id, item.granblue_id, targetSlot, {
|
||||||
mainhand: targetSlot === -1
|
mainhand: targetSlot === -1
|
||||||
})
|
})
|
||||||
} else if (activeTab === 'summons') {
|
} else if (activeTab === GridType.Summon) {
|
||||||
await apiClient.addSummon(party.id, item.granblue_id, targetSlot, {
|
await apiClient.addSummon(party.id, item.granblue_id, targetSlot, {
|
||||||
main: targetSlot === -1,
|
main: targetSlot === -1,
|
||||||
friend: targetSlot === 6
|
friend: targetSlot === 6
|
||||||
})
|
})
|
||||||
} else if (activeTab === 'characters') {
|
} else if (activeTab === GridType.Character) {
|
||||||
await apiClient.addCharacter(party.id, item.granblue_id, targetSlot)
|
await apiClient.addCharacter(party.id, item.granblue_id, targetSlot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -354,7 +349,7 @@
|
||||||
// Find next empty slot for continuous adding
|
// Find next empty slot for continuous adding
|
||||||
let nextEmptySlot = -999 // sentinel value meaning no empty slot found
|
let nextEmptySlot = -999 // sentinel value meaning no empty slot found
|
||||||
|
|
||||||
if (activeTab === 'weapons') {
|
if (activeTab === GridType.Weapon) {
|
||||||
// Check mainhand first (position -1)
|
// Check mainhand first (position -1)
|
||||||
if (!party.weapons.find(w => w.position === -1 || w.mainhand)) {
|
if (!party.weapons.find(w => w.position === -1 || w.mainhand)) {
|
||||||
nextEmptySlot = -1
|
nextEmptySlot = -1
|
||||||
|
|
@ -367,7 +362,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (activeTab === 'summons') {
|
} else if (activeTab === GridType.Summon) {
|
||||||
// Check main summon first (position -1)
|
// Check main summon first (position -1)
|
||||||
if (!party.summons.find(s => s.position === -1 || s.main)) {
|
if (!party.summons.find(s => s.position === -1 || s.main)) {
|
||||||
nextEmptySlot = -1
|
nextEmptySlot = -1
|
||||||
|
|
@ -384,7 +379,7 @@
|
||||||
nextEmptySlot = 6
|
nextEmptySlot = 6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (activeTab === 'characters') {
|
} else if (activeTab === GridType.Character) {
|
||||||
// Check character slots 0-4
|
// Check character slots 0-4
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
if (!party.characters.find(c => c.position === i)) {
|
if (!party.characters.find(c => c.position === i)) {
|
||||||
|
|
@ -471,8 +466,8 @@
|
||||||
openPicker: (opts: { type: 'weapon' | 'summon' | 'character'; position: number; item?: any }) => {
|
openPicker: (opts: { type: 'weapon' | 'summon' | 'character'; position: number; item?: any }) => {
|
||||||
if (!canEdit()) return
|
if (!canEdit()) return
|
||||||
selectedSlot = opts.position
|
selectedSlot = opts.position
|
||||||
activeTab = opts.type === 'weapon' ? 'weapons' :
|
activeTab = opts.type === 'weapon' ? GridType.Weapon :
|
||||||
opts.type === 'summon' ? 'summons' : 'characters'
|
opts.type === 'summon' ? GridType.Summon : GridType.Character
|
||||||
pickerTitle = `Search ${opts.type}s`
|
pickerTitle = `Search ${opts.type}s`
|
||||||
pickerOpen = true
|
pickerOpen = true
|
||||||
}
|
}
|
||||||
|
|
@ -549,21 +544,12 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<nav class="party-tabs" aria-label="Party sections">
|
<PartySegmentedControl
|
||||||
{#each tabs as t}
|
selectedTab={activeTab}
|
||||||
<button
|
onTabChange={handleTabChange}
|
||||||
class="tab-btn"
|
party={party}
|
||||||
aria-pressed={activeTab === t.key}
|
class="party-tabs"
|
||||||
class:active={activeTab === t.key}
|
/>
|
||||||
onclick={() => selectTab(t.key)}
|
|
||||||
>
|
|
||||||
{t.label}
|
|
||||||
{#if t.count > 0}
|
|
||||||
<span class="count">({t.count})</span>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{#if error}
|
{#if error}
|
||||||
<div class="error-message" role="alert">
|
<div class="error-message" role="alert">
|
||||||
|
|
@ -572,14 +558,14 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="party-content">
|
<div class="party-content">
|
||||||
{#if activeTab === 'weapons'}
|
{#if activeTab === GridType.Weapon}
|
||||||
<WeaponGrid
|
<WeaponGrid
|
||||||
weapons={party.weapons}
|
weapons={party.weapons}
|
||||||
raidExtra={(party as any)?.raid?.group?.extra}
|
raidExtra={(party as any)?.raid?.group?.extra}
|
||||||
showGuidebooks={(party as any)?.raid?.group?.guidebooks}
|
showGuidebooks={(party as any)?.raid?.group?.guidebooks}
|
||||||
guidebooks={(party as any)?.guidebooks}
|
guidebooks={(party as any)?.guidebooks}
|
||||||
/>
|
/>
|
||||||
{:else if activeTab === 'summons'}
|
{:else if activeTab === GridType.Summon}
|
||||||
<SummonGrid summons={party.summons} />
|
<SummonGrid summons={party.summons} />
|
||||||
{:else}
|
{:else}
|
||||||
<CharacterGrid characters={party.characters} mainWeaponElement={mainWeaponElement} partyElement={partyElement} />
|
<CharacterGrid characters={party.characters} mainWeaponElement={mainWeaponElement} partyElement={partyElement} />
|
||||||
|
|
@ -590,8 +576,8 @@
|
||||||
</section>
|
</section>
|
||||||
<SearchSidebar
|
<SearchSidebar
|
||||||
open={pickerOpen}
|
open={pickerOpen}
|
||||||
type={activeTab === 'weapons' ? 'weapon' :
|
type={activeTab === GridType.Weapon ? 'weapon' :
|
||||||
activeTab === 'summons' ? 'summon' : 'character'}
|
activeTab === GridType.Summon ? 'summon' : 'character'}
|
||||||
onClose={() => (pickerOpen = false)}
|
onClose={() => (pickerOpen = false)}
|
||||||
onAddItems={handleAddItems}
|
onAddItems={handleAddItems}
|
||||||
canAddMore={true}
|
canAddMore={true}
|
||||||
|
|
@ -729,42 +715,7 @@
|
||||||
.raid-name {
|
.raid-name {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.party-tabs {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
border-bottom: 2px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn {
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn.active {
|
|
||||||
color: #3366ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn.active::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: -2px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 2px;
|
|
||||||
background: #3366ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn .count {
|
|
||||||
color: #999;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-message {
|
.error-message {
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
background: #fee;
|
background: #fee;
|
||||||
|
|
@ -773,23 +724,10 @@
|
||||||
color: #c00;
|
color: #c00;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.party-content {
|
.party-content {
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-placeholder {
|
|
||||||
padding: 2rem;
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-placeholder ul {
|
|
||||||
text-align: left;
|
|
||||||
max-width: 400px;
|
|
||||||
margin: 1rem auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Edit form styles */
|
/* Edit form styles */
|
||||||
.edit-form {
|
.edit-form {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
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 SearchSidebar from '$lib/components/panels/SearchSidebar.svelte'
|
||||||
|
import PartySegmentedControl from '$lib/components/party/PartySegmentedControl.svelte'
|
||||||
|
import { GridType } from '$lib/types/enums'
|
||||||
import { setContext } from 'svelte'
|
import { setContext } from 'svelte'
|
||||||
import type { SearchResult } from '$lib/api/resources/search'
|
import type { SearchResult } from '$lib/api/resources/search'
|
||||||
import { apiClient } from '$lib/api/client'
|
import { apiClient } from '$lib/api/client'
|
||||||
|
|
@ -17,15 +19,10 @@
|
||||||
const currentUser = $derived($page.data?.currentUser)
|
const currentUser = $derived($page.data?.currentUser)
|
||||||
|
|
||||||
// Local, client-only state for tab selection (Svelte 5 runes)
|
// Local, client-only state for tab selection (Svelte 5 runes)
|
||||||
let activeTab = $state<'weapons' | 'summons' | 'characters'>('weapons')
|
let activeTab = $state<GridType>(GridType.Weapon)
|
||||||
const tabs = [
|
|
||||||
{ key: 'weapons', label: 'Weapons' },
|
|
||||||
{ key: 'summons', label: 'Summons' },
|
|
||||||
{ key: 'characters', label: 'Characters' }
|
|
||||||
] as const
|
|
||||||
|
|
||||||
function selectTab(key: typeof tabs[number]['key']) {
|
function selectTab(gridType: GridType) {
|
||||||
activeTab = key
|
activeTab = gridType
|
||||||
sidebarOpen = true // Auto-open sidebar when switching tabs
|
sidebarOpen = true // Auto-open sidebar when switching tabs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,8 +63,8 @@
|
||||||
let isCharacterGridFull = $derived(characters.length >= 5) // 5 character slots
|
let isCharacterGridFull = $derived(characters.length >= 5) // 5 character slots
|
||||||
|
|
||||||
let canAddMore = $derived(
|
let canAddMore = $derived(
|
||||||
activeTab === 'weapons' ? !isWeaponGridFull :
|
activeTab === GridType.Weapon ? !isWeaponGridFull :
|
||||||
activeTab === 'summons' ? !isSummonGridFull :
|
activeTab === GridType.Summon ? !isSummonGridFull :
|
||||||
!isCharacterGridFull
|
!isCharacterGridFull
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -118,7 +115,7 @@
|
||||||
try {
|
try {
|
||||||
console.log('Adding item to party:', { partyId, itemId: firstItem.id, type: activeTab, position })
|
console.log('Adding item to party:', { partyId, itemId: firstItem.id, type: activeTab, position })
|
||||||
|
|
||||||
if (activeTab === 'weapons') {
|
if (activeTab === GridType.Weapon) {
|
||||||
// Use selectedSlot if available, otherwise default to mainhand
|
// Use selectedSlot if available, otherwise default to mainhand
|
||||||
if (selectedSlot === null) position = -1
|
if (selectedSlot === null) position = -1
|
||||||
const addResult = await apiClient.addWeapon(
|
const addResult = await apiClient.addWeapon(
|
||||||
|
|
@ -141,7 +138,7 @@
|
||||||
},
|
},
|
||||||
mainhand: position === -1
|
mainhand: position === -1
|
||||||
}]
|
}]
|
||||||
} else if (activeTab === 'summons') {
|
} else if (activeTab === GridType.Summon) {
|
||||||
// Use selectedSlot if available, otherwise default to main summon
|
// Use selectedSlot if available, otherwise default to main summon
|
||||||
if (selectedSlot === null) position = -1
|
if (selectedSlot === null) position = -1
|
||||||
const addResult = await apiClient.addSummon(
|
const addResult = await apiClient.addSummon(
|
||||||
|
|
@ -165,7 +162,7 @@
|
||||||
main: position === -1,
|
main: position === -1,
|
||||||
friend: position === 6
|
friend: position === 6
|
||||||
}]
|
}]
|
||||||
} else if (activeTab === 'characters') {
|
} else if (activeTab === GridType.Character) {
|
||||||
// Use selectedSlot if available, otherwise default to first slot
|
// Use selectedSlot if available, otherwise default to first slot
|
||||||
if (selectedSlot === null) position = 0
|
if (selectedSlot === null) position = 0
|
||||||
const addResult = await apiClient.addCharacter(
|
const addResult = await apiClient.addCharacter(
|
||||||
|
|
@ -251,7 +248,7 @@
|
||||||
const item = items[i]
|
const item = items[i]
|
||||||
let position = -1 // Default position
|
let position = -1 // Default position
|
||||||
|
|
||||||
if (activeTab === 'weapons') {
|
if (activeTab === GridType.Weapon) {
|
||||||
// Use selectedSlot for first item if available
|
// Use selectedSlot for first item if available
|
||||||
if (i === 0 && selectedSlot !== null && !weapons.find(w => w.position === selectedSlot)) {
|
if (i === 0 && selectedSlot !== null && !weapons.find(w => w.position === selectedSlot)) {
|
||||||
position = selectedSlot
|
position = selectedSlot
|
||||||
|
|
@ -283,7 +280,7 @@
|
||||||
},
|
},
|
||||||
mainhand: position === -1
|
mainhand: position === -1
|
||||||
}]
|
}]
|
||||||
} else if (activeTab === 'summons') {
|
} else if (activeTab === GridType.Summon) {
|
||||||
// Use selectedSlot for first item if available
|
// Use selectedSlot for first item if available
|
||||||
if (i === 0 && selectedSlot !== null && !summons.find(s => s.position === selectedSlot)) {
|
if (i === 0 && selectedSlot !== null && !summons.find(s => s.position === selectedSlot)) {
|
||||||
position = selectedSlot
|
position = selectedSlot
|
||||||
|
|
@ -316,7 +313,7 @@
|
||||||
main: position === -1,
|
main: position === -1,
|
||||||
friend: position === 6
|
friend: position === 6
|
||||||
}]
|
}]
|
||||||
} else if (activeTab === 'characters') {
|
} else if (activeTab === GridType.Character) {
|
||||||
// Use selectedSlot for first item if available
|
// Use selectedSlot for first item if available
|
||||||
if (i === 0 && selectedSlot !== null && !characters.find(c => c.position === selectedSlot)) {
|
if (i === 0 && selectedSlot !== null && !characters.find(c => c.position === selectedSlot)) {
|
||||||
position = selectedSlot
|
position = selectedSlot
|
||||||
|
|
@ -359,7 +356,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Original local-only adding logic (before party creation)
|
// Original local-only adding logic (before party creation)
|
||||||
if (activeTab === 'weapons') {
|
if (activeTab === GridType.Weapon) {
|
||||||
// Add weapons to empty slots
|
// Add weapons to empty slots
|
||||||
const emptySlots = Array.from({ length: 10 }, (_, i) => i - 1) // -1 for mainhand, 0-8 for grid
|
const emptySlots = Array.from({ length: 10 }, (_, i) => i - 1) // -1 for mainhand, 0-8 for grid
|
||||||
.filter(i => !weapons.find(w => w.position === i))
|
.filter(i => !weapons.find(w => w.position === i))
|
||||||
|
|
@ -391,7 +388,7 @@
|
||||||
weapons = [...weapons, newWeapon]
|
weapons = [...weapons, newWeapon]
|
||||||
})
|
})
|
||||||
console.log('Updated weapons array:', weapons)
|
console.log('Updated weapons array:', weapons)
|
||||||
} else if (activeTab === 'summons') {
|
} else if (activeTab === GridType.Summon) {
|
||||||
// Add summons to empty slots
|
// Add summons to empty slots
|
||||||
const emptySlots = [-1, 0, 1, 2, 3, 6] // main, 4 grid slots, friend
|
const emptySlots = [-1, 0, 1, 2, 3, 6] // main, 4 grid slots, friend
|
||||||
.filter(i => !summons.find(s => s.position === i))
|
.filter(i => !summons.find(s => s.position === i))
|
||||||
|
|
@ -421,7 +418,7 @@
|
||||||
friend: position === 6
|
friend: position === 6
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
} else if (activeTab === 'characters') {
|
} else if (activeTab === GridType.Character) {
|
||||||
// Add characters to empty slots
|
// Add characters to empty slots
|
||||||
const emptySlots = Array.from({ length: 5 }, (_, i) => i)
|
const emptySlots = Array.from({ length: 5 }, (_, i) => i)
|
||||||
.filter(i => !characters.find(c => c.position === i))
|
.filter(i => !characters.find(c => c.position === i))
|
||||||
|
|
@ -515,23 +512,23 @@
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<nav class="party-tabs" aria-label="Party sections">
|
<PartySegmentedControl
|
||||||
{#each tabs as t}
|
value={activeTab}
|
||||||
<button
|
onValueChange={selectTab}
|
||||||
class="tab-btn"
|
party={{
|
||||||
aria-pressed={activeTab === t.key}
|
element: 0,
|
||||||
class:active={activeTab === t.key}
|
job: undefined,
|
||||||
on:click={() => selectTab(t.key)}
|
characters,
|
||||||
>
|
weapons,
|
||||||
{t.label}
|
summons
|
||||||
</button>
|
}}
|
||||||
{/each}
|
userGender={currentUser?.gender}
|
||||||
</nav>
|
/>
|
||||||
|
|
||||||
<div class="party-content">
|
<div class="party-content">
|
||||||
{#if activeTab === 'weapons'}
|
{#if activeTab === GridType.Weapon}
|
||||||
<WeaponGrid {weapons} />
|
<WeaponGrid {weapons} />
|
||||||
{:else if activeTab === 'summons'}
|
{:else if activeTab === GridType.Summon}
|
||||||
<SummonGrid {summons} />
|
<SummonGrid {summons} />
|
||||||
{:else}
|
{:else}
|
||||||
<CharacterGrid {characters} />
|
<CharacterGrid {characters} />
|
||||||
|
|
@ -541,7 +538,7 @@
|
||||||
|
|
||||||
<SearchSidebar
|
<SearchSidebar
|
||||||
open={sidebarOpen}
|
open={sidebarOpen}
|
||||||
type={activeTab === 'weapons' ? 'weapon' : activeTab === 'summons' ? 'summon' : 'character'}
|
type={activeTab === GridType.Weapon ? 'weapon' : activeTab === GridType.Summon ? 'summon' : 'character'}
|
||||||
onClose={() => sidebarOpen = false}
|
onClose={() => sidebarOpen = false}
|
||||||
onAddItems={handleAddItems}
|
onAddItems={handleAddItems}
|
||||||
canAddMore={canAddMore}
|
canAddMore={canAddMore}
|
||||||
|
|
@ -629,36 +626,6 @@
|
||||||
background: #2857e0;
|
background: #2857e0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.party-tabs {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
border-bottom: 2px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn {
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn.active {
|
|
||||||
color: #3366ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn.active::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: -2px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 2px;
|
|
||||||
background: #3366ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.party-content {
|
.party-content {
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue