feat: refine public gear cards
Validate and deploy staging / validate (push) Successful in 21s
Validate and deploy production / validate (push) Successful in 25s
Validate and deploy staging / deploy (push) Successful in 42s
Validate and deploy production / deploy (push) Successful in 30s

This commit is contained in:
jb
2026-08-01 02:01:10 -04:00
parent 1998e87309
commit b47e2588e9
5 changed files with 19 additions and 9 deletions
+4
View File
@@ -242,6 +242,10 @@
.database-item-card.talent-card>header{min-height:86px}
@media(max-width:700px){.database-item-card>header:has(.database-item-render){grid-template-columns:52px 42px minmax(0,1fr);gap:8px}.database-item-card>header:has(.database-item-render) .database-item-render{width:52px;height:52px}.database-item-card>header:has(.database-item-render) .database-header-status{grid-column:3;position:static;justify-self:end}}
.database-item-card>header:has(.database-item-render){position:relative;isolation:isolate;grid-template-columns:44px minmax(0,1fr);min-height:104px;overflow:hidden;background:#101415}.database-item-card>header:has(.database-item-render)::after{content:"";position:absolute;z-index:1;inset:0;background:linear-gradient(90deg,rgba(8,11,12,.92) 0%,rgba(8,11,12,.76) 48%,rgba(8,11,12,.48) 100%);pointer-events:none}.database-item-card>header:has(.database-item-render) .database-item-render{position:absolute;z-index:0;inset:0;width:100%;height:100%;border:0;object-fit:cover;opacity:.82}.database-item-card>header:has(.database-item-render) .database-item-icon,.database-item-card>header:has(.database-item-render) .database-item-identity{position:relative;z-index:2}.database-item-card>header:has(.database-item-render) .database-header-status{position:absolute;z-index:2;right:12px}.database-item-card>header:has(.database-item-render) .database-item-icon{width:44px;height:44px}.database-item-card>header:has(.database-item-render) .database-item-icon :is(img,.game-icon){width:38px;height:38px}
.database-item-card:not(:has(.database-item-render)) .database-item-icon{width:48px;height:48px}.database-item-card:not(:has(.database-item-render)) .database-item-icon :is(img,.game-icon){width:42px;height:42px}
.database-item-facts.stacked>div.primary.armor-fact{border-left-color:#71aaa5;background:linear-gradient(90deg,#13211f,#101516)}.database-item-facts.stacked>div.primary.armor-fact>span{color:#8eb9b4}.database-item-facts.stacked>div.primary.armor-fact b{color:#a5d1ca}
@media(max-width:700px){.database-item-card>header:has(.database-item-render){grid-template-columns:42px minmax(0,1fr);min-height:92px}.database-item-card>header:has(.database-item-render) .database-item-icon{width:42px;height:42px}.database-item-card>header:has(.database-item-render) .database-item-icon :is(img,.game-icon){width:36px;height:36px}.database-item-card:not(:has(.database-item-render)) .database-item-icon{width:44px;height:44px}.database-item-card:not(:has(.database-item-render)) .database-item-icon :is(img,.game-icon){width:38px;height:38px}}
.database-item-card.talent-card h2{font-size:18px;line-height:1.1}
.database-item-card.talent-card>header small,.database-item-card.talent-card .database-item-lines span{color:var(--shd-text-muted)}
.database-talent-tiers{display:grid;gap:var(--shd-gap-sm);margin:10px 12px 0}
+1 -1
View File
@@ -15,7 +15,7 @@
<div class:stacked={layout === 'stack'} class="database-item-facts">
{#each facts as fact}
<div class:primary={fact.emphasis === 'primary'}>
<div class:primary={fact.emphasis === 'primary'} class:armor-fact={fact.label === 'BASE ARMOR'}>
<span>{fact.label}</span>
<div class="database-fact-value">
{#if fact.gameIcon}<GameIcon name={fact.gameIcon}/>{/if}
+2 -1
View File
@@ -23,9 +23,10 @@
<small>{item.kicker}</small>
<h2>{item.name}</h2>
</div>
{#if item.headerBadge || item.headerMetaIcon}
{#if item.headerBadges?.length || item.headerBadge || item.headerMetaIcon}
<div class="database-header-status">
{#if item.headerMetaIcon}<GameIcon name={item.headerMetaIcon}/>{/if}
{#each item.headerBadges ?? [] as badge}<span class="database-header-badge">{badge}</span>{/each}
{#if item.headerBadge}<span class="database-header-badge">{item.headerBadge}</span>{/if}
</div>
{/if}
+1
View File
@@ -20,6 +20,7 @@ export type DatabaseItem = {
renderUrl?: string;
gameIcon?: GameIconName;
headerBadge?: string;
headerBadges?: string[];
headerMetaIcon?: GameIconName;
badges?: DatabaseBadge[];
facts: DatabaseFact[];
+11 -7
View File
@@ -1,7 +1,7 @@
import { databaseCategory } from '$lib/database/categories';
import type { DatabaseFact, DatabaseItem } from '$lib/database/items';
import type { GameIconName } from '$lib/game-icons';
import { weaponBaseRange } from '$lib/builder/loadout';
import { gearBaseStatRange, weaponBaseRange } from '$lib/builder/loadout';
import { gameDataIconUrl, type GameDataIconType } from '$lib/server/game-data-icons';
import { managedImageUrl } from '$lib/server/managed-images';
import { getDatabase } from '$lib/server/database';
@@ -454,6 +454,8 @@ export const load: PageServerLoad = async ({ params, url }) => {
case 'gear':
{
const relations = gearRelations.filter((relation) => relation.gear_id === row.id);
const level50Health = gearBaseStatRange(row.base_health_min, row.base_health_max, 50);
const level50Armor = row.base_armor_min === null ? null : gearBaseStatRange(row.base_armor_min, row.base_armor_max, 50);
const relatedItems = (kind: 'brand' | 'talent', type: GameDataIconType) => relations
.filter((relation) => relation.kind === kind)
.map((relation) => {
@@ -484,14 +486,16 @@ export const load: PageServerLoad = async ({ params, url }) => {
? [{ label: 'AVAILABLE TALENTS', items: relatedItems('talent', 'talent'), emptyLabel: 'NO TALENTS ASSIGNED' }]
: [])
];
return {
id: String(row.id), name: String(row.name), kicker: `${row.slot} // T${row.tier}`,
renderUrl: row.render_id ? managedImageUrl(String(row.render_id), row.render_updated_at) : '',
return {
id: String(row.id), name: String(row.name), kicker: `${row.slot} // T${row.tier}`,
renderUrl: row.render_id ? managedImageUrl(String(row.render_id), row.render_updated_at) : '',
gameIcon: gearIcon(row.slot),
badges: [{ label: `T${row.tier}` }, { label: `${Number(row.tier) === 0 ? 1 : 2} MOD SLOT${Number(row.tier) === 0 ? '' : 'S'}` }],
headerBadges: [`${Number(row.tier) === 0 ? 1 : 2} MOD SLOT${Number(row.tier) === 0 ? '' : 'S'}`],
headerBadge: `T${row.tier}`,
factLayout: 'stack' as const,
facts: [
{ label: 'BASE HEALTH', value: range(row.base_health_min,row.base_health_max) },
...(row.base_armor_min === null ? [] : [{ label: 'BASE ARMOR', value: range(row.base_armor_min,row.base_armor_max) }])
{ label: 'BASE HEALTH', value: `${level50Health.min.toLocaleString('en-US')}–${level50Health.max.toLocaleString('en-US')}`, emphasis: 'primary' as const, note: 'AT LEVEL 50' },
...(level50Armor === null ? [] : [{ label: 'BASE ARMOR', value: `${level50Armor.min.toLocaleString('en-US')}–${level50Armor.max.toLocaleString('en-US')}`, emphasis: 'primary' as const, note: 'AT LEVEL 50' }])
],
iconGroups
};