hensei-web/src/lib/components/grids/WeaponGrid.svelte

174 lines
3.9 KiB
Svelte

<svelte:options runes={true} />
<script lang="ts">
import type { GridWeapon } from '$lib/types/api/party'
import { getContext } from 'svelte'
import type { PartyContext } from '$lib/types/party-context'
import type { DragDropContext } from '$lib/composables/drag-drop.svelte'
import DraggableItem from '$lib/components/dnd/DraggableItem.svelte'
import DropZone from '$lib/components/dnd/DropZone.svelte'
interface Props {
weapons?: GridWeapon[]
raidExtra?: boolean
showGuidebooks?: boolean
guidebooks?: Record<string, any>
}
let {
weapons = [],
raidExtra = undefined,
showGuidebooks = undefined,
guidebooks = undefined
}: Props = $props()
import WeaponUnit from '$lib/components/units/WeaponUnit.svelte'
import ExtraContainer from '$lib/components/extra/ExtraContainer.svelte'
import ExtraWeapons from '$lib/components/extra/ExtraWeaponsGrid.svelte'
import Guidebooks from '$lib/components/extra/GuidebooksGrid.svelte'
const ctx = getContext<PartyContext>('party')
const dragContext = getContext<DragDropContext | undefined>('drag-drop')
let mainhand = $derived(weapons.find((w) => (w as any).mainhand || w.position === -1))
// Create array for sub-weapons (positions 0-8)
let subWeaponSlots = $derived.by(() => {
const slots: (GridWeapon | undefined)[] = Array(9).fill(undefined)
weapons.forEach(weapon => {
if (weapon.position >= 0 && weapon.position < 9) {
slots[weapon.position] = weapon
}
})
return slots
})
</script>
<div class="wrapper">
<div class="grid">
<div aria-label="Mainhand Weapon">
<WeaponUnit item={mainhand} position={-1} />
</div>
<ul class="weapons" aria-label="Weapon Grid">
{#each subWeaponSlots as weapon, i}
<li
aria-label={weapon ? `Weapon ${i}` : `Empty slot ${i}`}
data-index={i}
class={weapon ? '' : 'Empty'}
>
{#if dragContext}
<DropZone
container="main-weapons"
position={i}
type="weapon"
item={weapon}
canDrop={ctx?.canEdit() ?? false}
>
<DraggableItem
item={weapon}
container="main-weapons"
position={i}
type="weapon"
canDrag={!!weapon && (ctx?.canEdit() ?? false)}
>
<WeaponUnit item={weapon} position={i} />
</DraggableItem>
</DropZone>
{:else}
<WeaponUnit item={weapon} position={i} />
{/if}
</li>
{/each}
</ul>
</div>
{#if raidExtra || showGuidebooks}
<ExtraContainer>
{#if raidExtra}
<ExtraWeapons {weapons} offset={9} />
{/if}
{#if showGuidebooks}
<Guidebooks {guidebooks} />
{/if}
</ExtraContainer>
{/if}
</div>
<style lang="scss">
@use '$src/themes/colors' as *;
@use '$src/themes/typography' as *;
@use '$src/themes/spacing' as *;
@use '$src/themes/mixins' as *;
.wrapper {
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
@include breakpoint(phone) {
margin: 0 2px;
}
.grid {
display: grid;
gap: $unit-3x;
grid-template-columns: 1.278fr 3fr;
justify-items: center;
grid-template-areas: 'mainhand grid';
max-width: $grid-width;
@include breakpoint(tablet) {
gap: $unit-2x;
}
@include breakpoint(phone) {
gap: $unit;
}
.weapons {
display: grid; /* make the right-images container a grid */
grid-template-columns: repeat(
3,
minmax(0, 1fr)
); /* create 3 columns, each taking up 1 fraction */
grid-template-rows: repeat(3, 1fr); /* create 3 rows, each taking up 1 fraction */
gap: $unit-3x;
@include breakpoint(tablet) {
gap: $unit-2x;
}
@include breakpoint(phone) {
gap: $unit;
}
& > li {
list-style: none;
}
}
}
}
.unit {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: $unit;
}
.image {
width: 100%;
height: auto;
border: 1px solid $grey-75;
border-radius: 8px;
display: block;
}
.name {
font-size: $font-small;
text-align: center;
color: var(--text-secondary);
}
</style>