add context menu to job components
This commit is contained in:
parent
0ab2782697
commit
a88411eb46
3 changed files with 123 additions and 70 deletions
|
|
@ -1,6 +1,11 @@
|
|||
<script lang="ts">
|
||||
import type { Job } from '$lib/types/api/entities'
|
||||
import { getJobIconUrl, formatJobProficiency } from '$lib/utils/jobUtils'
|
||||
import {
|
||||
getJobIconUrl,
|
||||
getJobWideImageUrl,
|
||||
formatJobProficiency,
|
||||
Gender
|
||||
} from '$lib/utils/jobUtils'
|
||||
import ProficiencyLabel from '../labels/ProficiencyLabel.svelte'
|
||||
|
||||
interface Props {
|
||||
|
|
@ -21,26 +26,34 @@
|
|||
aria-pressed={selected}
|
||||
aria-label="{job.name.en} - {selected ? 'Currently selected' : 'Click to select'}"
|
||||
>
|
||||
<img src={getJobIconUrl(job.granblueId)} alt={job.name.en} class="job-icon" loading="lazy" />
|
||||
<div class="job-image-container">
|
||||
<img
|
||||
src={getJobWideImageUrl(job, Gender.Gran)}
|
||||
alt={job.name.en}
|
||||
class="job-wide"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="job-info">
|
||||
<span class="job-name">{job.name.en}</span>
|
||||
|
||||
<div class="job-details">
|
||||
{#if job.ultimateMastery}
|
||||
<span class="badge ultimate">UM</span>
|
||||
{/if}
|
||||
{#if proficiencies.length > 0}
|
||||
<div class="proficiencies">
|
||||
{#each job.proficiency as prof}
|
||||
{#if prof > 0}
|
||||
<ProficiencyLabel proficiency={prof} size="small" />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if proficiencies.length > 0}
|
||||
<div class="proficiencies">
|
||||
{#each job.proficiency as prof}
|
||||
{#if prof > 0}
|
||||
<ProficiencyLabel proficiency={prof} size="small" />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="job-right">
|
||||
{#if job.ultimateMastery}
|
||||
<span class="badge ultimate">UM</span>
|
||||
{/if}
|
||||
<img src={getJobIconUrl(job.granblueId)} alt="" class="job-icon" loading="lazy" />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
|
|
@ -53,7 +66,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: spacing.$unit;
|
||||
padding: spacing.$unit-2x spacing.$unit;
|
||||
padding: spacing.$unit;
|
||||
background: var(--card-bg);
|
||||
border-radius: layout.$card-corner;
|
||||
border: none;
|
||||
|
|
@ -83,24 +96,26 @@
|
|||
|
||||
position: relative;
|
||||
|
||||
.job-icon {
|
||||
// Display at native size (job icons are typically 48x48px)
|
||||
width: auto;
|
||||
height: 24px;
|
||||
max-width: 48px;
|
||||
max-height: 48px;
|
||||
border-radius: 4px;
|
||||
.job-image-container {
|
||||
position: relative;
|
||||
width: 120px;
|
||||
border-radius: layout.$item-corner;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
object-fit: contain;
|
||||
|
||||
.job-wide {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.job-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
flex-direction: column;
|
||||
gap: spacing.$unit-half;
|
||||
min-width: 0;
|
||||
|
||||
.job-name {
|
||||
|
|
@ -112,39 +127,46 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.job-details {
|
||||
.proficiencies {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: spacing.$unit-half;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
font-size: typography.$font-small;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
flex-shrink: 0;
|
||||
.job-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: spacing.$unit;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.master {
|
||||
background: var(--badge-master-bg, #ffd700);
|
||||
color: var(--badge-master-text, #000);
|
||||
}
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
font-size: typography.$font-small;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
&.ultimate {
|
||||
background: var(--badge-ultimate-bg, #9b59b6);
|
||||
color: var(--badge-ultimate-text, #fff);
|
||||
}
|
||||
&.master {
|
||||
background: var(--badge-master-bg, #ffd700);
|
||||
color: var(--badge-master-text, #000);
|
||||
}
|
||||
|
||||
.proficiencies {
|
||||
display: flex;
|
||||
gap: spacing.$unit-half;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
&.ultimate {
|
||||
background: var(--badge-ultimate-bg, #9b59b6);
|
||||
color: var(--badge-ultimate-text, #fff);
|
||||
}
|
||||
}
|
||||
|
||||
.job-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: layout.$item-corner;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -86,23 +86,36 @@
|
|||
<div class="job-header">
|
||||
{#if canEdit}
|
||||
<button class="job-name clickable" on:click={onSelectJob}>
|
||||
<img src={jobIconUrl} alt="{job.name.en} icon" class="job-icon" />
|
||||
<h3>{job.name.en}</h3>
|
||||
<div class="job-name-row">
|
||||
<img src={jobIconUrl} alt="{job.name.en} icon" class="job-icon" />
|
||||
<h3>{job.name.en}</h3>
|
||||
</div>
|
||||
{#if job.masterLevel || job.ultimateMastery}
|
||||
<div class="job-badges">
|
||||
{#if job.masterLevel}
|
||||
<span class="badge master">ML{job.masterLevel}</span>
|
||||
{/if}
|
||||
{#if job.ultimateMastery}
|
||||
<span class="badge ultimate">UM</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
{:else}
|
||||
<div class="job-name">
|
||||
<img src={jobIconUrl} alt="{job.name.en} icon" class="job-icon" />
|
||||
<h3>{job.name.en}</h3>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if job.masterLevel || job.ultimateMastery}
|
||||
<div class="job-badges">
|
||||
{#if job.masterLevel}
|
||||
<span class="badge master">Master Lv.{job.masterLevel}</span>
|
||||
{/if}
|
||||
{#if job.ultimateMastery}
|
||||
<span class="badge ultimate">Ultimate</span>
|
||||
<div class="job-name-row">
|
||||
<img src={jobIconUrl} alt="{job.name.en} icon" class="job-icon" />
|
||||
<h3>{job.name.en}</h3>
|
||||
</div>
|
||||
{#if job.masterLevel || job.ultimateMastery}
|
||||
<div class="job-badges">
|
||||
{#if job.masterLevel}
|
||||
<span class="badge master">ML{job.masterLevel}</span>
|
||||
{/if}
|
||||
{#if job.ultimateMastery}
|
||||
<span class="badge ultimate">UM</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -297,6 +310,7 @@
|
|||
.job-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: spacing.$unit;
|
||||
padding: spacing.$unit;
|
||||
border-radius: layout.$card-corner;
|
||||
|
|
@ -316,6 +330,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.job-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: spacing.$unit-half;
|
||||
}
|
||||
|
||||
.job-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
|
|
|||
|
|
@ -41,11 +41,9 @@ export function getJobFullImageUrl(job: Job | undefined, gender: Gender = Gender
|
|||
return '/images/placeholders/placeholder-weapon-grid.png'
|
||||
}
|
||||
|
||||
// Convert job name to slug format (lowercase, spaces to hyphens)
|
||||
const slug = job.name.en.toLowerCase().replace(/\s+/g, '-')
|
||||
const genderSuffix = gender === Gender.Djeeta ? 'b' : 'a'
|
||||
|
||||
return `/images/jobs/${slug}_${genderSuffix}.png`
|
||||
return `/images/job-zoom/${job.granblueId}_${genderSuffix}.png`
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -61,6 +59,19 @@ export function getJobIconUrl(granblueId: string | undefined): string {
|
|||
return `/images/job-icons/${granblueId}.png`
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate job wide banner image URL for JobItem component
|
||||
* These are wider banner-style images stored in /static/images/job-wide/
|
||||
*/
|
||||
export function getJobWideImageUrl(job: Job | undefined, gender: Gender = Gender.Gran): string {
|
||||
if (!job) {
|
||||
return '/images/placeholders/placeholder-weapon-grid.png'
|
||||
}
|
||||
|
||||
const genderSuffix = gender === Gender.Djeeta ? 'b' : 'a'
|
||||
return `/images/job-wide/${job.granblueId}_${genderSuffix}.jpg`
|
||||
}
|
||||
|
||||
/**
|
||||
* Get job tier display name
|
||||
* Converts internal row codes to user-friendly names
|
||||
|
|
|
|||
Loading…
Reference in a new issue