sidebar: refactor details components

This commit is contained in:
Justin Edmund 2025-11-30 20:06:31 -08:00
parent 47885b1429
commit a858877545
6 changed files with 307 additions and 463 deletions

View file

@ -1,6 +1,8 @@
<script lang="ts"> <script lang="ts">
import type { GridCharacter, GridWeapon, GridSummon } from '$lib/types/api/party' import type { GridCharacter, GridWeapon, GridSummon } from '$lib/types/api/party'
import { detectModifications } from '$lib/utils/modificationDetector' import { detectModifications, canWeaponBeModified } from '$lib/utils/modificationDetector'
import { partyStore } from '$lib/stores/partyStore.svelte'
import { sidebar } from '$lib/stores/sidebar.svelte'
import DetailsSidebarSegmentedControl from './modifications/DetailsSidebarSegmentedControl.svelte' import DetailsSidebarSegmentedControl from './modifications/DetailsSidebarSegmentedControl.svelte'
import ItemHeader from './details/ItemHeader.svelte' import ItemHeader from './details/ItemHeader.svelte'
import BasicInfoSection from './details/BasicInfoSection.svelte' import BasicInfoSection from './details/BasicInfoSection.svelte'
@ -13,11 +15,42 @@
item: GridCharacter | GridWeapon | GridSummon item: GridCharacter | GridWeapon | GridSummon
} }
let { type, item }: Props = $props() let { type, item: initialItem }: Props = $props()
// Derive item from partyStore for reactivity, fall back to prop if not in store
// This ensures the sidebar updates when party data changes (e.g., uncap level)
let item = $derived.by(() => {
const activeId = sidebar.activeItemId
if (activeId && partyStore.party) {
const storeItem = partyStore.getItem(type, activeId)
if (storeItem) return storeItem
}
return initialItem
})
let selectedView = $state<'canonical' | 'user'>('user')
let modificationStatus = $derived(detectModifications(type, item)) let modificationStatus = $derived(detectModifications(type, item))
// For weapons, only show segmented control if the weapon can be modified
const showSegmentedControl = $derived(
type === 'weapon'
? canWeaponBeModified(item as GridWeapon)
: modificationStatus.hasModifications
)
// Track selected view - updated reactively based on modifiability
let selectedView = $state<'canonical' | 'user'>('user')
// Update view when switching to items with different modifiability
$effect(() => {
if (!showSegmentedControl) {
// Force canonical view for non-modifiable items
selectedView = 'canonical'
} else if (showSegmentedControl && selectedView === 'canonical') {
// Switch to user view when selecting a modifiable item
selectedView = 'user'
}
})
// Helper to get the actual item data // Helper to get the actual item data
function getItemData() { function getItemData() {
if (type === 'character') { if (type === 'character') {
@ -54,7 +87,7 @@
<ItemHeader {type} {item} {itemData} {gridUncapLevel} {gridTranscendence} /> <ItemHeader {type} {item} {itemData} {gridUncapLevel} {gridTranscendence} />
<DetailsSidebarSegmentedControl <DetailsSidebarSegmentedControl
hasModifications={modificationStatus.hasModifications} hasModifications={showSegmentedControl}
bind:selectedView bind:selectedView
/> />
@ -87,6 +120,6 @@
display: flex; display: flex;
position: relative; position: relative;
flex-direction: column; flex-direction: column;
gap: spacing.$unit-2x; gap: spacing.$unit-4x;
} }
</style> </style>

View file

@ -1,9 +1,12 @@
<script lang="ts"> <script lang="ts">
import { getElementLabel } from '$lib/utils/element'
import { getRarityLabel } from '$lib/utils/rarity' import { getRarityLabel } from '$lib/utils/rarity'
import { getProficiencyLabel } from '$lib/utils/proficiency'
import { getRaceLabel } from '$lib/utils/race' import { getRaceLabel } from '$lib/utils/race'
import { getGenderLabel } from '$lib/utils/gender' import { getGenderLabel } from '$lib/utils/gender'
import DetailsSection from './DetailsSection.svelte'
import DetailRow from './DetailRow.svelte'
import UncapIndicator from '$lib/components/uncap/UncapIndicator.svelte'
import ElementLabel from '$lib/components/labels/ElementLabel.svelte'
import ProficiencyLabel from '$lib/components/labels/ProficiencyLabel.svelte'
interface Props { interface Props {
type: 'character' | 'weapon' | 'summon' type: 'character' | 'weapon' | 'summon'
@ -11,97 +14,69 @@
} }
let { type, itemData }: Props = $props() let { type, itemData }: Props = $props()
// Calculate max uncap level (all stars filled)
const maxUncapLevel = $derived.by(() => {
const flb = itemData?.uncap?.flb ?? false
const ulb = itemData?.uncap?.ulb ?? false
const transcendence = itemData?.uncap?.transcendence ?? false
const special = type === 'character' && (itemData?.rarity ?? 3) < 3
if (type === 'character') {
if (special) {
return ulb ? 5 : flb ? 4 : 3
} else {
// Regular characters: transcendence star is separate
return flb ? 5 : 4
}
} else {
// Weapons and summons
return transcendence ? 5 : ulb ? 5 : flb ? 4 : 3
}
})
const special = $derived(type === 'character' && (itemData?.rarity ?? 3) < 3)
</script> </script>
<div class="details-section"> <DetailsSection title="Basic Information">
<h3>Basic Information</h3> <DetailRow label="Rarity" value={getRarityLabel(itemData?.rarity)} />
<div class="detail-row"> <DetailRow label="Element">
<span class="label">Rarity</span> <ElementLabel element={itemData?.element} size="medium" />
<span class="value">{getRarityLabel(itemData?.rarity)}</span> </DetailRow>
</div>
<div class="detail-row">
<span class="label">Element</span>
<span class="value">{getElementLabel(itemData?.element)}</span>
</div>
{#if type === 'character'} {#if type === 'character'}
{#if itemData?.race && itemData.race.length > 0} {#if itemData?.race && itemData.race.length > 0}
<div class="detail-row"> <DetailRow
<span class="label">Race</span> label="Race"
<span class="value"> value={itemData.race
{itemData.race
.map((r: any) => getRaceLabel(r)) .map((r: any) => getRaceLabel(r))
.filter(Boolean) .filter(Boolean)
.join(', ') || '—'} .join(', ') || '—'}
</span> />
</div>
{/if} {/if}
<div class="detail-row"> <DetailRow label="Gender" value={getGenderLabel(itemData?.gender)} />
<span class="label">Gender</span>
<span class="value">{getGenderLabel(itemData?.gender)}</span>
</div>
{#if itemData?.proficiency && itemData.proficiency.length > 0} {#if itemData?.proficiency && itemData.proficiency.length > 0}
<div class="detail-row"> <DetailRow label="Proficiencies">
<span class="label">Proficiencies</span> {#each itemData.proficiency as prof}
<span class="value"> <ProficiencyLabel proficiency={prof} size="medium" />
{itemData.proficiency {/each}
.map((p: any) => getProficiencyLabel(p)) </DetailRow>
.filter(Boolean)
.join(', ') || '—'}
</span>
</div>
{/if} {/if}
{:else if type === 'weapon'} {:else if type === 'weapon'}
<div class="detail-row"> <DetailRow label="Proficiency">
<span class="label">Proficiency</span> <ProficiencyLabel proficiency={itemData?.proficiency} size="medium" />
<span class="value">{getProficiencyLabel(itemData?.proficiency)}</span> </DetailRow>
</div>
{/if} {/if}
</div>
<style lang="scss"> <DetailRow label="Max Uncap">
@use '$src/themes/colors' as colors; <UncapIndicator
@use '$src/themes/layout' as layout; {type}
@use '$src/themes/spacing' as spacing; uncapLevel={maxUncapLevel}
@use '$src/themes/typography' as typography; transcendenceStage={itemData?.uncap?.transcendence ? 5 : 0}
flb={itemData?.uncap?.flb ?? false}
.details-section { ulb={itemData?.uncap?.ulb ?? false}
padding: 0 spacing.$unit; transcendence={itemData?.uncap?.transcendence ?? false}
{special}
h3 { />
margin: 0 0 calc(spacing.$unit * 1.5) 0; </DetailRow>
font-size: typography.$font-name; </DetailsSection>
font-weight: typography.$medium;
color: var(--text-primary);
padding: 0 spacing.$unit;
}
}
.detail-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: calc(spacing.$unit * 1.5) spacing.$unit;
&:hover {
background: var(--page-hover);
border-radius: layout.$item-corner;
}
&:last-child {
border-bottom: none;
}
.label {
font-size: typography.$font-regular;
color: var(--text-secondary, colors.$grey-50);
}
.value {
font-size: typography.$font-regular;
color: var(--text-primary, colors.$grey-10);
font-weight: typography.$medium;
text-align: right;
}
}
</style>

View file

@ -62,6 +62,9 @@
} }
return '—' return '—'
} }
// Special characters have different star counts (SR characters, etc.)
const special = $derived(type === 'character' && (itemData?.rarity ?? 3) < 3)
</script> </script>
<div class="item-header-container"> <div class="item-header-container">
@ -74,6 +77,7 @@
flb={itemData?.uncap?.flb} flb={itemData?.uncap?.flb}
ulb={itemData?.uncap?.ulb} ulb={itemData?.uncap?.ulb}
transcendence={itemData?.uncap?.transcendence} transcendence={itemData?.uncap?.transcendence}
{special}
editable={false} editable={false}
/> />
</div> </div>

View file

@ -1,4 +1,7 @@
<script lang="ts"> <script lang="ts">
import DetailsSection from './DetailsSection.svelte'
import DetailRow from './DetailRow.svelte'
interface Props { interface Props {
itemData: any itemData: any
gridUncapLevel: number | null gridUncapLevel: number | null
@ -8,169 +11,34 @@
let { itemData, gridUncapLevel, gridTranscendence }: Props = $props() let { itemData, gridUncapLevel, gridTranscendence }: Props = $props()
</script> </script>
<div class="details-section"> {#if itemData?.hp}
{#if itemData?.hp && itemData?.atk} <DetailsSection title="HP">
<div class="stats-grid"> <DetailRow label="Base" value={itemData.hp.minHp} />
<div class="grid-header empty"></div> <DetailRow label="MLB" value={itemData.hp.maxHp} />
<div class="grid-header">HP</div> {#if itemData.uncap?.flb && itemData.hp.maxHpFlb}
<div class="grid-header">ATK</div> <DetailRow label="FLB" value={itemData.hp.maxHpFlb} />
<div class="grid-label">Base</div>
<div class="grid-value">{itemData.hp.minHp ?? '—'}</div>
<div class="grid-value">{itemData.atk.minAtk ?? '—'}</div>
<div class="grid-label">MLB</div>
<div class="grid-value">{itemData.hp.maxHp ?? '—'}</div>
<div class="grid-value">{itemData.atk.maxAtk ?? '—'}</div>
{#if itemData.uncap?.flb && (itemData.hp.maxHpFlb || itemData.atk.maxAtkFlb)}
<div class="grid-label">FLB</div>
<div class="grid-value">{itemData.hp.maxHpFlb ?? '—'}</div>
<div class="grid-value">{itemData.atk.maxAtkFlb ?? '—'}</div>
{/if} {/if}
{#if itemData.uncap?.ulb && itemData.hp.maxHpUlb}
{#if itemData.uncap?.ulb && (itemData.hp.maxHpUlb || itemData.atk.maxAtkUlb)} <DetailRow label="ULB" value={itemData.hp.maxHpUlb} />
<div class="grid-label">ULB</div>
<div class="grid-value">{itemData.hp.maxHpUlb ?? '—'}</div>
<div class="grid-value">{itemData.atk.maxAtkUlb ?? '—'}</div>
{/if} {/if}
{#if gridTranscendence && gridTranscendence > 0} {#if gridTranscendence && gridTranscendence > 0}
<div class="grid-label">T5</div> <DetailRow label="T5" value={itemData.hp.maxHpXlb} />
<div class="grid-value">{itemData.hp.maxHpXlb ?? '—'}</div>
<div class="grid-value">{itemData.atk.maxAtkXlb ?? '—'}</div>
{/if} {/if}
</div> </DetailsSection>
{/if} {/if}
</div>
<style lang="scss"> {#if itemData?.atk}
@use '$src/themes/colors' as colors; <DetailsSection title="ATK">
@use '$src/themes/effects' as effects; <DetailRow label="Base" value={itemData.atk.minAtk} />
@use '$src/themes/layout' as layout; <DetailRow label="MLB" value={itemData.atk.maxAtk} />
@use '$src/themes/spacing' as spacing; {#if itemData.uncap?.flb && itemData.atk.maxAtkFlb}
@use '$src/themes/typography' as typography; <DetailRow label="FLB" value={itemData.atk.maxAtkFlb} />
{/if}
.details-section { {#if itemData.uncap?.ulb && itemData.atk.maxAtkUlb}
padding: 0 spacing.$unit; <DetailRow label="ULB" value={itemData.atk.maxAtkUlb} />
{/if}
h3 { {#if gridTranscendence && gridTranscendence > 0}
margin: 0 0 calc(spacing.$unit * 1.5) 0; <DetailRow label="T5" value={itemData.atk.maxAtkXlb} />
font-size: typography.$font-name; {/if}
font-weight: typography.$medium; </DetailsSection>
color: var(--text-primary); {/if}
padding: 0 spacing.$unit;
}
h4 {
margin: calc(spacing.$unit * 1.5) 0 spacing.$unit 0;
font-size: typography.$font-small;
font-weight: typography.$medium;
color: var(--text-secondary, colors.$grey-50);
}
}
.stats-grid {
display: grid;
grid-template-columns: 0.5fr 1fr 1fr;
background: var(--page-bg);
gap: spacing.$unit-half;
border: 1px solid colors.$grey-80;
border-radius: layout.$card-corner;
box-shadow: effects.$page-elevation;
overflow: hidden;
padding: spacing.$unit;
.grid-header {
padding: spacing.$unit;
background: var(--button-bg-hover);
font-weight: typography.$medium;
color: var(--menu-text);
border-bottom: 1px solid var(--border-primary);
display: flex;
align-items: center;
justify-content: center;
border-radius: layout.$item-corner;
&.empty {
background: transparent;
}
&:not(:first-child) {
border-left: 1px solid var(--border-primary);
}
}
.grid-label {
padding: spacing.$unit;
background: var(--button-bg-hover);
font-weight: typography.$medium;
color: var(--menu-text);
border-radius: layout.$item-corner;
justify-content: center;
display: flex;
align-items: center;
}
.grid-value {
padding: spacing.$unit;
border-radius: layout.$item-corner;
text-align: right;
font-weight: typography.$medium;
font-size: typography.$font-regular;
font-variant-numeric: tabular-nums;
color: var(--menu-text);
border-bottom: 1px solid var(--border-primary);
border-left: 1px solid var(--border-primary);
display: flex;
align-items: center;
justify-content: flex-end;
&:hover {
background: var(--button-bg-hover);
}
}
// Remove border from last row
.grid-label:last-of-type,
.grid-value:last-of-type {
border-bottom: none;
}
}
.detail-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: calc(spacing.$unit * 1.5) spacing.$unit;
&:hover {
background: var(--page-hover);
border-radius: layout.$item-corner;
}
&:last-child {
border-bottom: none;
}
.label {
font-size: typography.$font-regular;
color: var(--text-secondary, colors.$grey-50);
}
.value {
font-size: typography.$font-regular;
color: var(--text-primary, colors.$grey-10);
font-weight: typography.$medium;
text-align: right;
}
}
.stats-group {
margin-bottom: spacing.$unit-2x;
&:last-child {
margin-bottom: 0;
}
}
</style>

View file

@ -1,21 +1,13 @@
<script lang="ts"> <script lang="ts">
import type { GridCharacter, GridWeapon, GridSummon } from '$lib/types/api/party' import type { GridCharacter, GridWeapon, GridSummon } from '$lib/types/api/party'
import ModificationSection from '../modifications/ModificationSection.svelte' import DetailsSection from './DetailsSection.svelte'
import UncapStatusDisplay from '../modifications/UncapStatusDisplay.svelte' import DetailRow from './DetailRow.svelte'
import AwakeningDisplay from '../modifications/AwakeningDisplay.svelte' import AwakeningDisplay from '../modifications/AwakeningDisplay.svelte'
import MasteryDisplay from '../modifications/MasteryDisplay.svelte' import MasteryDisplay from '../modifications/MasteryDisplay.svelte'
import StatModifierItem from '../modifications/StatModifierItem.svelte'
import WeaponKeysList from '../modifications/WeaponKeysList.svelte' import WeaponKeysList from '../modifications/WeaponKeysList.svelte'
import { getRarityLabel } from '$lib/utils/rarity' import { formatAxSkill, getWeaponKeyTitle } from '$lib/utils/modificationFormatters'
import { getElementLabel } from '$lib/utils/element' import ElementLabel from '$lib/components/labels/ElementLabel.svelte'
import { getRaceLabel } from '$lib/utils/race' import UncapIndicator from '$lib/components/uncap/UncapIndicator.svelte'
import { getGenderLabel } from '$lib/utils/gender'
import { getProficiencyLabel } from '$lib/utils/proficiency'
import {
formatAxSkill,
getWeaponKeyTitle,
getElementName
} from '$lib/utils/modificationFormatters'
interface Props { interface Props {
type: 'character' | 'weapon' | 'summon' type: 'character' | 'weapon' | 'summon'
@ -28,36 +20,54 @@
let { type, item, itemData, gridUncapLevel, gridTranscendence, modificationStatus }: Props = let { type, item, itemData, gridUncapLevel, gridTranscendence, modificationStatus }: Props =
$props() $props()
// Get uncap capabilities from item data based on type
let uncapCaps = $derived.by(() => {
if (type === 'character') {
const char = item as GridCharacter
const uncap = char.character?.uncap
return { flb: uncap?.flb, ulb: uncap?.ulb, transcendence: false }
} else if (type === 'weapon') {
const weapon = item as GridWeapon
const uncap = weapon.weapon?.uncap
return { flb: uncap?.flb, ulb: uncap?.ulb, transcendence: uncap?.transcendence }
} else {
const summon = item as GridSummon
const uncap = summon.summon?.uncap
return { flb: uncap?.flb, ulb: uncap?.ulb, transcendence: uncap?.transcendence }
}
})
</script> </script>
<div class="team-view"> <div class="team-view">
<ModificationSection title="Uncap & Transcendence" visible={true}> <DetailsSection title="Uncap & Transcendence">
<UncapStatusDisplay <DetailRow label="Uncap Level">
<UncapIndicator
{type} {type}
uncapLevel={gridUncapLevel} uncapLevel={gridUncapLevel}
transcendenceStep={gridTranscendence} transcendenceStage={gridTranscendence}
special={itemData?.special} flb={uncapCaps?.flb}
flb={itemData?.uncap?.flb} ulb={uncapCaps?.ulb}
ulb={itemData?.uncap?.ulb} transcendence={uncapCaps?.transcendence}
transcendence={itemData?.uncap?.transcendence}
/> />
</ModificationSection> </DetailRow>
</DetailsSection>
{#if type === 'character'} {#if type === 'character'}
{@const char = item as GridCharacter} {@const char = item as GridCharacter}
{#if modificationStatus.hasAwakening} {#if modificationStatus.hasAwakening}
<ModificationSection title="Awakening" visible={true}> <DetailsSection title="Awakening">
<AwakeningDisplay <AwakeningDisplay
{...(char.awakening ? { awakening: char.awakening } : {})} {...(char.awakening ? { awakening: char.awakening } : {})}
size="medium" size="medium"
showLevel={true} showLevel={true}
/> />
</ModificationSection> </DetailsSection>
{/if} {/if}
{#if modificationStatus.hasRings || modificationStatus.hasEarring} {#if modificationStatus.hasRings || modificationStatus.hasEarring}
<ModificationSection title="Mastery" visible={true}> <DetailsSection title="Mastery">
<MasteryDisplay <MasteryDisplay
rings={char.overMastery} rings={char.overMastery}
earring={char.aetherialMastery} earring={char.aetherialMastery}
@ -65,95 +75,69 @@
variant="detailed" variant="detailed"
showIcons={true} showIcons={true}
/> />
</ModificationSection> </DetailsSection>
{/if} {/if}
{#if modificationStatus.hasPerpetuity} {#if modificationStatus.hasPerpetuity}
<ModificationSection title="Status" visible={true}> <DetailsSection title="Status">
<StatModifierItem label="Perpetuity" value="Active" variant="max" /> <DetailRow label="Perpetuity Ring" value="Active" />
</ModificationSection> </DetailsSection>
{/if} {/if}
{:else if type === 'weapon'} {:else if type === 'weapon'}
{@const weapon = item as GridWeapon} {@const weapon = item as GridWeapon}
{#if modificationStatus.hasAwakening && weapon.awakening} {#if modificationStatus.hasAwakening && weapon.awakening}
<ModificationSection title="Awakening" visible={true}> <DetailsSection title="Awakening">
<AwakeningDisplay awakening={weapon.awakening} size="medium" showLevel={true} /> <AwakeningDisplay awakening={weapon.awakening} size="medium" showLevel={true} />
</ModificationSection> </DetailsSection>
{/if} {/if}
{#if modificationStatus.hasWeaponKeys} {#if modificationStatus.hasWeaponKeys}
<ModificationSection title={getWeaponKeyTitle(weapon.weapon?.series)} visible={true}> <DetailsSection title={getWeaponKeyTitle(weapon.weapon?.series)}>
<WeaponKeysList weaponKeys={weapon.weaponKeys} weaponData={weapon.weapon} layout="list" /> <WeaponKeysList weaponKeys={weapon.weaponKeys} weaponData={weapon.weapon} layout="list" />
</ModificationSection> </DetailsSection>
{/if} {/if}
{#if modificationStatus.hasAxSkills && weapon.ax} {#if modificationStatus.hasAxSkills && weapon.ax}
<ModificationSection title="AX Skills" visible={true}> <DetailsSection title="AX Skills">
{#each weapon.ax as axSkill} {#each weapon.ax as axSkill}
<StatModifierItem <DetailRow
label={formatAxSkill(axSkill).split('+')[0]?.trim() ?? ''} label={formatAxSkill(axSkill).split('+')[0]?.trim() ?? ''}
value={`+${axSkill.strength}`} value={`+${axSkill.strength}${axSkill.modifier <= 2 ? '' : '%'}`}
suffix={axSkill.modifier <= 2 ? '' : '%'}
variant="enhanced"
/> />
{/each} {/each}
</ModificationSection> </DetailsSection>
{/if} {/if}
{#if modificationStatus.hasElement && weapon.element} {#if modificationStatus.hasElement && weapon.element}
<ModificationSection title="Element Override" visible={true}> <DetailsSection title="Element Override">
<StatModifierItem label="Instance Element" value={getElementName(weapon.element)} /> <DetailRow label="Weapon Element">
</ModificationSection> <ElementLabel element={weapon.element} size="medium" />
</DetailRow>
</DetailsSection>
{/if} {/if}
{:else if type === 'summon'} {:else if type === 'summon'}
{@const summon = item as GridSummon} {@const summon = item as GridSummon}
{#if modificationStatus.hasQuickSummon || modificationStatus.hasFriendSummon} {#if modificationStatus.hasQuickSummon || modificationStatus.hasFriendSummon}
<ModificationSection title="Summon Status" visible={true}> <DetailsSection title="Summon Status">
{#if summon.quickSummon} {#if summon.quickSummon}
<StatModifierItem label="Quick Summon" value="Enabled" variant="enhanced" /> <DetailRow label="Quick Summon" value="Enabled" />
{/if} {/if}
{#if summon.friend} {#if summon.friend}
<StatModifierItem label="Friend Summon" value="Yes" /> <DetailRow label="Friend Summon" value="Yes" />
{/if} {/if}
</ModificationSection> </DetailsSection>
{/if} {/if}
{/if} {/if}
</div> </div>
<style lang="scss"> <style lang="scss">
@use '$src/themes/colors' as colors;
@use '$src/themes/spacing' as spacing; @use '$src/themes/spacing' as spacing;
@use '$src/themes/typography' as typography;
.team-view { .team-view {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: spacing.$unit-2x; gap: spacing.$unit-4x;
}
.detail-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: calc(spacing.$unit * 0.75) 0;
border-bottom: 1px solid rgba(colors.$grey-70, 0.5);
&:last-child {
border-bottom: none;
}
.label {
font-size: typography.$font-regular;
color: var(--text-secondary, colors.$grey-50);
}
.value {
font-size: typography.$font-regular;
color: var(--text-primary, colors.$grey-10);
font-weight: typography.$medium;
text-align: right;
}
} }
</style> </style>

View file

@ -14,11 +14,7 @@
layout?: 'list' | 'grid' layout?: 'list' | 'grid'
} }
let { let { weaponKeys, weaponData, layout = 'list' }: Props = $props()
weaponKeys,
weaponData,
layout = 'list'
}: Props = $props()
let keyImages = $derived( let keyImages = $derived(
getWeaponKeyImages( getWeaponKeyImages(
@ -37,19 +33,7 @@
} }
function getSlotLabel(slot: number, series?: number): string { function getSlotLabel(slot: number, series?: number): string {
if (series === 2) { return `Skill ${slot + 1}`
return slot === 0 ? 'Alpha Pendulum' : 'Pendulum'
}
if (series === 3 || series === 34) {
return `Teluma ${slot + 1}`
}
if (series === 17) {
return slot === 0 ? 'Gauph Key' : `Ultima Key`
}
if (series === 22) {
return `Emblem Slot ${slot + 1}`
}
return `Slot ${slot + 1}`
} }
</script> </script>
@ -59,11 +43,7 @@
{@const imageData = keyImages[index]} {@const imageData = keyImages[index]}
<div class="weapon-key-item"> <div class="weapon-key-item">
{#if imageData} {#if imageData}
<img <img src={imageData.url} alt={imageData.alt} class="weapon-key-icon" />
src={imageData.url}
alt={imageData.alt}
class="weapon-key-icon"
/>
{/if} {/if}
<div class="weapon-key-info"> <div class="weapon-key-info">
<span class="slot-label"> <span class="slot-label">