hensei-web/src/lib/components/reps/WeaponRep.svelte
Justin Edmund 1a6a112efd fix: adapter initialization and image loading
- Add centralized adapter config with proper baseURL
- Fix API response transformation (object -> weapon/character/summon)
- Update components to use image service and correct property names
- Fix Svelte 5 $derived syntax and missing PartyAdapter.list method

Images now display correctly in grids.
2025-09-20 01:54:40 -07:00

121 lines
3 KiB
Svelte

<script lang="ts">
import type { Party, GridWeapon } from '$lib/types/api/party'
import { getWeaponImage } from '$lib/features/database/detail/image'
interface Props {
party?: Party
weapons?: GridWeapon[]
}
let { party, weapons: directWeapons }: Props = $props()
// Use direct weapons if provided, otherwise get from party (note: API returns weapons after transformation)
const weapons = $derived(directWeapons || party?.weapons || [])
const mainhand = $derived(weapons.find((w: GridWeapon) => w?.mainhand || w?.position === -1))
const grid = $derived(
Array.from({ length: 9 }, (_, i) => weapons.find((w: GridWeapon) => w?.position === i))
)
function weaponImageUrl(w?: GridWeapon, isMain = false): string {
const variant = isMain ? 'main' : 'grid'
// For weapons with null element that have an instance element, use it
const element = (w?.weapon?.element === 0 && w?.element) ? w.element : undefined
return getWeaponImage(w?.weapon?.granblueId, variant, element)
}
</script>
<div class="rep">
<div class="mainhand" class:empty={!mainhand}>
{#if mainhand}<img
alt="Mainhand"
src={weaponImageUrl(mainhand, true)}
loading="lazy"
decoding="async"
/>{/if}
</div>
<div class="weapons">
{#each Array.from( { length: 3 }, (_, rowIndex) => grid.slice(rowIndex * 3, (rowIndex + 1) * 3) ) as row, rowIndex}
<ul class="weapon-row">
{#each row as w, colIndex}
<li class="weapon" class:empty={!w}>
{#if w}<img alt="Weapon" src={weaponImageUrl(w)} loading="lazy" decoding="async" />{/if}
</li>
{/each}
</ul>
{/each}
</div>
</div>
<style lang="scss">
@use '$src/themes/layout' as *;
@use '$src/themes/spacing' as *;
@use '$src/themes/rep' as rep;
.rep {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: 1.11fr #{rep.$weapon-cols-proportion}fr;
gap: $unit-half;
.mainhand {
background: var(--placeholder-bg);
border-radius: $item-corner-small;
@include rep.aspect(rep.$weapon-main-w, rep.$weapon-main-h);
overflow: hidden;
min-height: 115px;
&.empty {
background: var(--placeholder-bg);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
}
img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
}
.weapons {
display: flex;
flex-direction: column;
gap: $unit;
height: 100%;
.weapon-row {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: $unit-half;
margin: 0;
padding: 0;
list-style: none;
flex: 1;
.weapon {
background: var(--unit-bg);
border-radius: $item-corner-small;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
&.empty {
background: var(--placeholder-bg);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
}
img {
border-radius: $item-corner-small;
display: block;
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
}
}
}
}
</style>