Checkpoint for reps
This commit is contained in:
parent
be0a8df439
commit
c8da5c4762
4 changed files with 489 additions and 228 deletions
|
|
@ -1,58 +1,124 @@
|
|||
<script lang="ts">
|
||||
import type { PartyView } from '$lib/api/schemas/party'
|
||||
export let party: PartyView
|
||||
import type { PartyView } from '$lib/api/schemas/party'
|
||||
export let party: PartyView
|
||||
|
||||
const characters = party.characters || []
|
||||
const grid = Array.from({ length: 3 }, (_, i) => characters.find((c: any) => c?.position === i))
|
||||
const characters = party.characters || []
|
||||
const grid = Array.from({ length: 3 }, (_, i) => characters.find((c: any) => c?.position === i))
|
||||
|
||||
function protagonistClass(): string {
|
||||
const main = (party.weapons || []).find((w: any) => w?.mainhand || w?.position === -1)
|
||||
const el = (main as any)?.element || (main as any)?.object?.element
|
||||
switch (el) { case 1: return 'wind'; case 2: return 'fire'; case 3: return 'water'; case 4: return 'earth'; case 5: return 'dark'; case 6: return 'light'; default: return '' }
|
||||
}
|
||||
function protagonistClass(): string {
|
||||
const main = (party.weapons || []).find((w: any) => w?.mainhand || w?.position === -1)
|
||||
const el = (main as any)?.element || (main as any)?.object?.element
|
||||
switch (el) {
|
||||
case 1:
|
||||
return 'wind'
|
||||
case 2:
|
||||
return 'fire'
|
||||
case 3:
|
||||
return 'water'
|
||||
case 4:
|
||||
return 'earth'
|
||||
case 5:
|
||||
return 'dark'
|
||||
case 6:
|
||||
return 'light'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function characterImageUrl(c?: any): string {
|
||||
const id = c?.object?.granblueId
|
||||
if (!id) return ''
|
||||
const uncap = c?.uncapLevel ?? 0
|
||||
const trans = c?.transcendenceStep ?? 0
|
||||
let suffix = '01'
|
||||
if (trans > 0) suffix = '04'
|
||||
else if (uncap >= 5) suffix = '03'
|
||||
else if (uncap > 2) suffix = '02'
|
||||
if (String(id) === '3030182000') {
|
||||
const main = (party.weapons || []).find((w: any) => w?.mainhand || w?.position === -1)
|
||||
const el = (main as any)?.element || (main as any)?.object?.element || 1
|
||||
suffix = `${suffix}_0${el}`
|
||||
}
|
||||
return `/images/character-main/${id}_${suffix}.jpg`
|
||||
}
|
||||
function characterImageUrl(c?: any): string {
|
||||
const id = c?.object?.granblueId
|
||||
if (!id) return ''
|
||||
const uncap = c?.uncapLevel ?? 0
|
||||
const trans = c?.transcendenceStep ?? 0
|
||||
let suffix = '01'
|
||||
if (trans > 0) suffix = '04'
|
||||
else if (uncap >= 5) suffix = '03'
|
||||
else if (uncap > 2) suffix = '02'
|
||||
if (String(id) === '3030182000') {
|
||||
const main = (party.weapons || []).find((w: any) => w?.mainhand || w?.position === -1)
|
||||
const el = (main as any)?.element || (main as any)?.object?.element || 1
|
||||
suffix = `${suffix}_0${el}`
|
||||
}
|
||||
return `/images/character-main/${id}_${suffix}.jpg`
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="rep">
|
||||
<ul class="characters">
|
||||
<li class={`protagonist ${protagonistClass()}`}></li>
|
||||
{#each grid as c, i}
|
||||
<li class="character">{#if c}<img alt="Character" src={characterImageUrl(c)} />{/if}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<ul class="characters">
|
||||
<li class={`protagonist ${protagonistClass()}`}></li>
|
||||
{#each grid as c, i}
|
||||
<li class="character">
|
||||
{#if c}<img alt="Character" src={characterImageUrl(c)} />{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/rep' as rep;
|
||||
|
||||
.rep { aspect-ratio: 2/0.99; border-radius: 10px; grid-gap: $unit-half; height: $rep-height; opacity: .5; }
|
||||
.character, .protagonist { aspect-ratio: 16/33; background: var(--card-bg); border-radius: 4px; box-sizing: border-box; display: grid; overflow: hidden; }
|
||||
.character img { border-radius: 4px; width: 100%; }
|
||||
.characters { display: grid; grid-template-columns: repeat(4, 1fr); gap: $unit-half; }
|
||||
.protagonist { border-color: transparent; border-width: 1px; border-style: solid; aspect-ratio: 32/66; }
|
||||
.protagonist img { position: relative; width: 100%; height: 100%; }
|
||||
.protagonist.wind { background: var(--wind-portrait-bg); border-color: var(--wind-bg); }
|
||||
.protagonist.fire { background: var(--fire-portrait-bg); border-color: var(--fire-bg); }
|
||||
.protagonist.water { background: var(--water-portrait-bg); border-color: var(--water-bg); }
|
||||
.protagonist.earth { background: var(--earth-portrait-bg); border-color: var(--earth-bg); }
|
||||
.protagonist.light { background: var(--light-portrait-bg); border-color: var(--light-bg); }
|
||||
.protagonist.dark { background: var(--dark-portrait-bg); border-color: var(--dark-bg); }
|
||||
.protagonist.empty { background: var(--card-bg); }
|
||||
.rep {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
grid-gap: $unit-half;
|
||||
}
|
||||
.character,
|
||||
.protagonist {
|
||||
aspect-ratio: 16/33;
|
||||
background: var(--card-bg);
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
overflow: hidden;
|
||||
}
|
||||
.character img {
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
.characters {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: $unit-half;
|
||||
}
|
||||
.protagonist {
|
||||
border-color: transparent;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
@include rep.aspect(32, 66);
|
||||
}
|
||||
.protagonist img {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.protagonist.wind {
|
||||
background: var(--wind-portrait-bg);
|
||||
border-color: var(--wind-bg);
|
||||
}
|
||||
.protagonist.fire {
|
||||
background: var(--fire-portrait-bg);
|
||||
border-color: var(--fire-bg);
|
||||
}
|
||||
.protagonist.water {
|
||||
background: var(--water-portrait-bg);
|
||||
border-color: var(--water-bg);
|
||||
}
|
||||
.protagonist.earth {
|
||||
background: var(--earth-portrait-bg);
|
||||
border-color: var(--earth-bg);
|
||||
}
|
||||
.protagonist.light {
|
||||
background: var(--light-portrait-bg);
|
||||
border-color: var(--light-bg);
|
||||
}
|
||||
.protagonist.dark {
|
||||
background: var(--dark-portrait-bg);
|
||||
border-color: var(--dark-bg);
|
||||
}
|
||||
.protagonist.empty {
|
||||
background: var(--card-bg);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,128 +1,259 @@
|
|||
<script lang="ts">
|
||||
import type { PartyView } from '$lib/api/schemas/party'
|
||||
import WeaponRep from '$lib/components/reps/WeaponRep.svelte'
|
||||
import SummonRep from '$lib/components/reps/SummonRep.svelte'
|
||||
import CharacterRep from '$lib/components/reps/CharacterRep.svelte'
|
||||
import type { PartyView } from '$lib/api/schemas/party'
|
||||
import WeaponRep from '$lib/components/reps/WeaponRep.svelte'
|
||||
import SummonRep from '$lib/components/reps/SummonRep.svelte'
|
||||
import CharacterRep from '$lib/components/reps/CharacterRep.svelte'
|
||||
import Icon from '$lib/components/Icon.svelte'
|
||||
|
||||
export let party: PartyView
|
||||
export let href: string = `/teams/${party.shortcode}`
|
||||
export let loading = false
|
||||
export let party: PartyView
|
||||
export let href: string = `/teams/${party.shortcode}`
|
||||
export let loading = false
|
||||
|
||||
let currentView: 'weapons' | 'summons' | 'characters' = 'weapons'
|
||||
let currentView: 'weapons' | 'summons' | 'characters' = 'summons'
|
||||
|
||||
function displayName(input: any): string {
|
||||
if (!input) return '—'
|
||||
const maybe = input.name ?? input
|
||||
if (typeof maybe === 'string') return maybe
|
||||
if (maybe && typeof maybe === 'object') return maybe.en || maybe.ja || '—'
|
||||
return '—'
|
||||
}
|
||||
function displayName(input: any): string {
|
||||
if (!input) return '—'
|
||||
const maybe = input.name ?? input
|
||||
if (typeof maybe === 'string') return maybe
|
||||
if (maybe && typeof maybe === 'object') return maybe.en || maybe.ja || '—'
|
||||
return '—'
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class={`gridRep ${loading ? 'hidden' : 'visible'}`} on:mouseleave={() => currentView='weapons'}>
|
||||
<a href={href} data-sveltekit-preload-data="hover">
|
||||
<div class="details">
|
||||
<div class="top">
|
||||
<div class="info">
|
||||
<h2 class:empty={!party.name}>{party.name || '(untitled)'}</h2>
|
||||
<div class="properties">
|
||||
<span class={`raid ${!party.raid ? 'empty' : ''}`}>{party.raid ? displayName(party.raid) : 'No raid'}</span>
|
||||
{#if party.fullAuto}<span class="fullAuto"> · Full Auto</span>{/if}
|
||||
{#if party.raid?.group?.extra}<span class="extra"> · EX</span>{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gridContainer">
|
||||
{#if currentView==='characters'}
|
||||
<div class="characterGrid"><CharacterRep {party} /></div>
|
||||
{:else if currentView==='summons'}
|
||||
<div class="summonGrid"><SummonRep {party} /></div>
|
||||
{:else}
|
||||
<div class="weaponGrid"><WeaponRep {party} /></div>
|
||||
{/if}
|
||||
</div>
|
||||
<ul class="indicators">
|
||||
<li class:active={currentView==='characters'} on:mouseenter={() => currentView='characters'}>
|
||||
<div class="indicator" />
|
||||
<span class="sr-only">Characters</span>
|
||||
</li>
|
||||
<li class:active={currentView==='weapons'} on:mouseenter={() => currentView='weapons'}>
|
||||
<div class="indicator" />
|
||||
<span class="sr-only">Weapons</span>
|
||||
</li>
|
||||
<li class:active={currentView==='summons'} on:mouseenter={() => currentView='summons'}>
|
||||
<div class="indicator" />
|
||||
<span class="sr-only">Summons</span>
|
||||
</li>
|
||||
</ul>
|
||||
</a>
|
||||
<div
|
||||
class={`gridRep ${loading ? 'hidden' : 'visible'}`}
|
||||
role="link"
|
||||
tabindex="0"
|
||||
on:mouseleave={() => (currentView = 'summons')}
|
||||
>
|
||||
<a {href} data-sveltekit-preload-data="hover">
|
||||
<div class="info">
|
||||
<h2 class:empty={!party.name}>{party.name || '(untitled)'}</h2>
|
||||
<div class="details">
|
||||
<span class={`raid ${!party.raid ? 'empty' : ''}`}
|
||||
>{party.raid ? displayName(party.raid) : 'No raid'}</span
|
||||
>
|
||||
|
||||
<div class="pills">
|
||||
{#if party.chargeAttack}
|
||||
<span class="pill chargeAttack" title="Charge Attack">
|
||||
<Icon name="charge-attack" size={16} />
|
||||
</span>
|
||||
{/if}
|
||||
{#if party.fullAuto}
|
||||
<span class="pill fullAuto" title="Full Auto">
|
||||
<Icon name="full-auto" size={16} />
|
||||
</span>
|
||||
{/if}
|
||||
{#if party.raid?.group?.extra}
|
||||
<span class="pill extra" title="Extra">
|
||||
<Icon name="extra-grid" size={16} />
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gridContainer">
|
||||
{#if currentView === 'characters'}
|
||||
<div class="characterGrid"><CharacterRep {party} /></div>
|
||||
{:else if currentView === 'summons'}
|
||||
<div class="summonGrid"><SummonRep {party} extendedView={true} /></div>
|
||||
{:else}
|
||||
<div class="weaponGrid"><WeaponRep {party} /></div>
|
||||
{/if}
|
||||
</div>
|
||||
<ul class="indicators">
|
||||
<li
|
||||
class:active={currentView === 'characters'}
|
||||
on:mouseenter={() => (currentView = 'characters')}
|
||||
>
|
||||
<div class="indicator"></div>
|
||||
<span class="sr-only">Characters</span>
|
||||
</li>
|
||||
<li class:active={currentView === 'weapons'} on:mouseenter={() => (currentView = 'weapons')}>
|
||||
<div class="indicator"></div>
|
||||
<span class="sr-only">Weapons</span>
|
||||
</li>
|
||||
<li class:active={currentView === 'summons'} on:mouseenter={() => (currentView = 'summons')}>
|
||||
<div class="indicator"></div>
|
||||
<span class="sr-only">Summons</span>
|
||||
</li>
|
||||
</ul>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/layout' as *;
|
||||
@use '$src/themes/rep' as rep;
|
||||
|
||||
.gridRep { aspect-ratio: 3/2; border-radius: 10px; box-sizing: border-box; min-width: 320px; position: relative; width: 100%; opacity: 1; }
|
||||
.gridRep.visible { transition: opacity .3s ease-in-out; opacity: 1; }
|
||||
.gridRep.hidden { opacity: 0; transition: opacity .12s ease-in-out; }
|
||||
.gridRep a {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
gap: 8px;
|
||||
padding: $unit-2x;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 10px;
|
||||
box-sizing: border-box;
|
||||
background: var(--card-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
.gridRep a:hover { background: var(--grid-rep-hover); border-color: rgba(0,0,0,.1); }
|
||||
.gridRep a:hover .indicators { opacity: 1; }
|
||||
.gridContainer { aspect-ratio: 2.1/1; width: 100%; align-self: start; }
|
||||
.gridRep {
|
||||
box-sizing: border-box;
|
||||
min-width: 262px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
opacity: 1;
|
||||
|
||||
.weaponGrid { aspect-ratio: 3.25/1; }
|
||||
.characterGrid { aspect-ratio: 2.1/1; }
|
||||
.summonGrid { aspect-ratio: 2/0.91; }
|
||||
&.visible {
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.details { display: flex; flex-direction: column; gap: $unit; }
|
||||
.details .top { display: flex; flex-direction: row; gap: calc($unit/2); align-items: center; }
|
||||
.details .info { display: flex; flex-direction: column; flex-grow: 1; gap: calc($unit/2); max-width: calc(100% - 44px - $unit); }
|
||||
.details h2 { color: var(--text-primary); font-size: 1.6rem; font-weight: 600; overflow: hidden; padding-bottom: 1px; text-overflow: ellipsis; white-space: nowrap; min-height: 24px; max-width: 258px; }
|
||||
.details h2.empty { color: var(--text-tertiary); }
|
||||
.properties { display: flex; font-size: 1.3rem; gap: $unit-half; }
|
||||
.raid.empty { color: var(--text-tertiary); }
|
||||
.fullAuto { color: var(--full-auto-label-text); white-space: nowrap; }
|
||||
.extra { color: var(--extra-purple-light-text); white-space: nowrap; }
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
}
|
||||
|
||||
.indicators {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: $unit;
|
||||
margin-top: $unit;
|
||||
margin-bottom: $unit;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
.indicators li { flex-grow: 1; padding: $unit 0; position: relative; }
|
||||
.indicator { transition: background-color .12s ease-in-out; height: $unit; border-radius: $unit-half; background-color: var(--button-contained-bg-hover); }
|
||||
.indicators li:hover .indicator, .indicators li.active .indicator { background-color: var(--text-secondary); }
|
||||
a {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
gap: $unit;
|
||||
padding: $unit;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid transparent;
|
||||
border-radius: $card-corner;
|
||||
box-sizing: border-box;
|
||||
background: var(--card-bg);
|
||||
overflow: hidden;
|
||||
|
||||
/* Visually hidden, accessible to screen readers */
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
&:hover {
|
||||
background: var(--grid-rep-hover);
|
||||
border-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:hover .indicators {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gridContainer {
|
||||
/* Reserve a constant visual height for all reps; keeps card height stable */
|
||||
aspect-ratio: calc(#{rep.$rep-body-ratio} / 1);
|
||||
width: 100%;
|
||||
align-self: start;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* inner wrappers simply fill; specific geometry lives inside reps */
|
||||
.weaponGrid,
|
||||
.characterGrid,
|
||||
.summonGrid {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-fourth;
|
||||
padding: $unit-half 0;
|
||||
|
||||
h2 {
|
||||
color: var(--text-primary);
|
||||
font-size: 1.6rem;
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
padding-bottom: 1px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
|
||||
&.empty {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: $unit;
|
||||
justify-content: space-between;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.raid {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
|
||||
&.empty {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
.pills {
|
||||
flex-shrink: 0;
|
||||
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 $unit-half;
|
||||
border-radius: $full-corner;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.chargeAttack {
|
||||
background-color: var(--charge-attack-bg);
|
||||
color: var(--charge-attack-text);
|
||||
}
|
||||
|
||||
&.fullAuto {
|
||||
background-color: var(--full-auto-bg);
|
||||
color: var(--full-auto-text);
|
||||
}
|
||||
|
||||
&.extra {
|
||||
background-color: var(--extra-purple-bg);
|
||||
color: var(--extra-purple-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.indicators {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: $unit;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
|
||||
li {
|
||||
flex-grow: 1;
|
||||
padding: $unit 0;
|
||||
position: relative;
|
||||
|
||||
&:hover .indicator,
|
||||
&.active .indicator {
|
||||
background-color: var(--text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.indicator {
|
||||
transition: background-color 0.12s ease-in-out;
|
||||
height: $unit;
|
||||
border-radius: $unit-half;
|
||||
background-color: var(--button-contained-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
/* Visually hidden, accessible to screen readers */
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,45 +1,110 @@
|
|||
<script lang="ts">
|
||||
import type { PartyView, GridSummonItemView } from '$lib/api/schemas/party'
|
||||
import type { PartyView, GridSummonItemView } from '$lib/api/schemas/party'
|
||||
|
||||
export let party: PartyView
|
||||
export let party: PartyView
|
||||
export let extendedView = false
|
||||
|
||||
const summons = party.summons || []
|
||||
const main = summons.find((s: any) => s?.main || s?.position === -1)
|
||||
const grid = Array.from({ length: 4 }, (_, i) => summons.find((s: any) => s?.position === i))
|
||||
const summons = party.summons || []
|
||||
const main = summons.find((s: any) => s?.main || s?.position === -1)
|
||||
const friend = extendedView
|
||||
? summons.find((s: any) => s?.friend || s?.position === -2)
|
||||
: undefined
|
||||
|
||||
function summonImageUrl(s?: any, isMain = false): string {
|
||||
const id = s?.object?.granblueId
|
||||
if (!id) return ''
|
||||
const folder = isMain ? 'summon-main' : 'summon-grid'
|
||||
return `/images/${folder}/${id}.jpg`
|
||||
}
|
||||
// In standard view: show positions 0-3 (4 summons)
|
||||
// In extended view: show positions 0-5 (6 summons including subauras)
|
||||
const gridLength = extendedView ? 6 : 4
|
||||
const grid = Array.from({ length: gridLength }, (_, i) =>
|
||||
summons.find((s: any) => s?.position === i)
|
||||
)
|
||||
|
||||
function summonImageUrl(s?: any, isMain = false): string {
|
||||
const id = s?.object?.granblueId
|
||||
if (!id) return ''
|
||||
const folder = isMain ? 'summon-main' : 'summon-grid'
|
||||
return `/images/${folder}/${id}.jpg`
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="rep">
|
||||
<div class="mainSummon">{#if main}<img alt="Main Summon" src={summonImageUrl(main, true)} />{/if}</div>
|
||||
<ul class="summons">
|
||||
{#each grid as s, i}
|
||||
<li class="summon">{#if s}<img alt="Summon" src={summonImageUrl(s)} />{/if}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<div class="rep" class:extended={extendedView}>
|
||||
<div class="mainSummon">
|
||||
{#if main}<img alt="Main Summon" src={summonImageUrl(main, true)} />{/if}
|
||||
</div>
|
||||
<ul class="summons">
|
||||
{#each grid as s, i}
|
||||
<li class="summon">
|
||||
{#if s}<img alt="Summon" src={summonImageUrl(s)} />{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{#if extendedView}
|
||||
<div class="friendSummon">
|
||||
{#if friend}<img alt="Friend Summon" src={summonImageUrl(friend, true)} />{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/rep' as rep;
|
||||
|
||||
.rep {
|
||||
aspect-ratio: 2/1.045;
|
||||
border-radius: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2.25fr;
|
||||
grid-gap: $unit-half;
|
||||
height: $rep-height;
|
||||
opacity: .5;
|
||||
}
|
||||
.summon, .mainSummon { background: var(--card-bg); border-radius: 4px; }
|
||||
.mainSummon { aspect-ratio: 56/97; display: grid; }
|
||||
.summons { display: grid; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(2, 1fr); gap: $unit-half; }
|
||||
.summon { aspect-ratio: 184/138; display: grid; }
|
||||
.summon img, .mainSummon img { border-radius: 4px; width: 100%; height: 100%; }
|
||||
.rep {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr #{rep.$summon-cols-proportion}fr;
|
||||
grid-gap: $unit-half;
|
||||
}
|
||||
|
||||
// Extended view layout: main summon | 6 grid summons | friend summon
|
||||
.rep.extended {
|
||||
display: flex;
|
||||
gap: $unit-half;
|
||||
grid-template-columns: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.summon,
|
||||
.mainSummon,
|
||||
.friendSummon {
|
||||
background: var(--card-bg);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mainSummon {
|
||||
@include rep.aspect(56, 97);
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.extended .mainSummon,
|
||||
.extended .friendSummon {
|
||||
@include rep.aspect(56, 97);
|
||||
display: grid;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.summons {
|
||||
@include rep.grid(2, 2, $unit-half);
|
||||
}
|
||||
|
||||
.extended .summons {
|
||||
display: grid;
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: $unit-half;
|
||||
flex: 1 1 0;
|
||||
min-width: 0; /* allow grid to shrink without overflowing */
|
||||
}
|
||||
|
||||
.summon {
|
||||
@include rep.aspect(184, 138);
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.summon img,
|
||||
.mainSummon img,
|
||||
.friendSummon img {
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,49 +1,48 @@
|
|||
<script lang="ts">
|
||||
import type { PartyView, GridWeaponItemView } from '$lib/api/schemas/party'
|
||||
import type { PartyView, GridWeaponItemView } from '$lib/api/schemas/party'
|
||||
|
||||
export let party: PartyView
|
||||
export let party: PartyView
|
||||
|
||||
const weapons = party.weapons || []
|
||||
const mainhand: GridWeaponItemView | undefined = weapons.find((w: any) => w?.mainhand || w?.position === -1)
|
||||
const grid = Array.from({ length: 9 }, (_, i) => weapons.find((w: any) => w?.position === i))
|
||||
const weapons = party.weapons || []
|
||||
const mainhand: GridWeaponItemView | undefined = weapons.find(
|
||||
(w: any) => w?.mainhand || w?.position === -1
|
||||
)
|
||||
const grid = Array.from({ length: 9 }, (_, i) => weapons.find((w: any) => w?.position === i))
|
||||
|
||||
function weaponImageUrl(w?: any, isMain = false): string {
|
||||
const id = w?.object?.granblueId
|
||||
if (!id) return ''
|
||||
const folder = isMain ? 'weapon-main' : 'weapon-grid'
|
||||
const objElement = w?.object?.element
|
||||
const instElement = w?.element
|
||||
if (objElement === 0 && instElement) return `/images/${folder}/${id}_${instElement}.jpg`
|
||||
return `/images/${folder}/${id}.jpg`
|
||||
}
|
||||
function weaponImageUrl(w?: any, isMain = false): string {
|
||||
const id = w?.object?.granblueId
|
||||
if (!id) return ''
|
||||
const folder = isMain ? 'weapon-main' : 'weapon-grid'
|
||||
const objElement = w?.object?.element
|
||||
const instElement = w?.element
|
||||
if (objElement === 0 && instElement) return `/images/${folder}/${id}_${instElement}.jpg`
|
||||
return `/images/${folder}/${id}.jpg`
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="rep">
|
||||
<div class="mainhand">{#if mainhand}<img alt="Mainhand" src={weaponImageUrl(mainhand, true)} />{/if}</div>
|
||||
<ul class="weapons">
|
||||
{#each grid as w, i}
|
||||
<li class="weapon">{#if w}<img alt="Weapon" src={weaponImageUrl(w)} />{/if}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<div class="mainhand">
|
||||
{#if mainhand}<img alt="Mainhand" src={weaponImageUrl(mainhand, true)} />{/if}
|
||||
</div>
|
||||
<ul class="weapons">
|
||||
{#each grid as w, i}
|
||||
<li class="weapon">
|
||||
{#if w}<img alt="Weapon" src={weaponImageUrl(w)} />{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/colors' as *;
|
||||
@use '$src/themes/rep' as rep;
|
||||
|
||||
.rep {
|
||||
height: $rep-height;
|
||||
display: grid;
|
||||
grid-template-columns: calc(#{$rep-height} * (200 / 420)) auto;
|
||||
gap: $unit-half;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.mainhand { background: var(--card-bg); border-radius: 4px; height: 100%; width: 100%; overflow: hidden; }
|
||||
.mainhand img { width: 100%; height: 100%; object-fit: contain; }
|
||||
.rep { width: 100%; height: 100%; display: grid; grid-template-columns: 1fr #{rep.$weapon-cols-proportion}fr; gap: $unit-half; }
|
||||
|
||||
.weapons { margin: 0; padding: 0; list-style: none; height: 100%; display: grid; grid-template-rows: repeat(3, calc((#{$rep-height} - (2 * #{$unit-half})) / 3)); grid-template-columns: repeat(3, calc((#{$rep-height} - (2 * #{$unit-half})) / 3 * (280 / 160))); gap: $unit-half; }
|
||||
.weapon { background: var(--card-bg); border-radius: 4px; overflow: hidden; display: flex; align-items: center; justify-content: center; }
|
||||
.weapon img { width: 100%; height: 100%; }
|
||||
.mainhand { background: var(--unit-bg); border-radius: 4px; @include rep.aspect(rep.$weapon-main-w, rep.$weapon-main-h); overflow: hidden; }
|
||||
.mainhand img { width: 100%; height: 100%; object-fit: cover; border-radius: 4px; }
|
||||
|
||||
.weapons { margin: 0; padding: 0; list-style: none; height: 100%; @include rep.grid(3, 3, $unit-half); }
|
||||
.weapon { background: var(--unit-bg); border-radius: 4px; overflow: hidden; @include rep.aspect(rep.$weapon-cell-w, rep.$weapon-cell-h); }
|
||||
.weapon img { width: 100%; height: 100%; object-fit: cover; border-radius: 4px; }
|
||||
</style>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue