feat: consume selected mechanic revisions
This commit is contained in:
@@ -7,15 +7,20 @@
|
||||
import type { DatabaseItem } from '$lib/database/items';
|
||||
import MarkdownContent from './MarkdownContent.svelte';
|
||||
import WeaponModSlotIcon from './WeaponModSlotIcon.svelte';
|
||||
import DatabaseMechanicGroupCard from './database/DatabaseMechanicGroup.svelte';
|
||||
import DatabaseMechanicStatusBadge from './database/DatabaseMechanicStatusBadge.svelte';
|
||||
import { databaseMechanicGroupStatus, type DatabaseMechanicGroup } from '$lib/database/items';
|
||||
|
||||
let {
|
||||
item,
|
||||
hideDiscussionUtility = false,
|
||||
anchor = true
|
||||
anchor = true,
|
||||
detail = false
|
||||
}: {
|
||||
item: DatabaseItem & { gameIcon?: GameIconName };
|
||||
hideDiscussionUtility?: boolean;
|
||||
anchor?: boolean;
|
||||
detail?: boolean;
|
||||
} = $props();
|
||||
let protocolLevel = $state(1);
|
||||
let talentUpgrades = $state(0);
|
||||
@@ -29,6 +34,14 @@
|
||||
const effect = item.protocolPreview?.talentEffects[Number(raw) - 1];
|
||||
return effect ? valueWithUnit(effect.baseValue * (1 + talentUpgrades / 5), effect.unit) : placeholder;
|
||||
});
|
||||
const mechanicGroups = () => item.mechanicGroups ?? [];
|
||||
const hasMechanicGroups = () => mechanicGroups().length > 0;
|
||||
const isSpecializationMechanic = (group: DatabaseMechanicGroup) => group.ownerType === 'specialization_tree_rank';
|
||||
const hasSpecializationMechanics = () => mechanicGroups().some(isSpecializationMechanic);
|
||||
const primaryMechanicGroup = () => {
|
||||
const groups = mechanicGroups();
|
||||
return groups.length === 1 && !isSpecializationMechanic(groups[0]) ? groups[0] : null;
|
||||
};
|
||||
</script>
|
||||
|
||||
<article id={anchor ? `database-item-${item.id}` : undefined} class="database-item-card" class:talent-card={item.variant === 'talent'} class:protocol-quality-standard={item.topBorderTone === 'standard'} class:protocol-quality-specialized={item.topBorderTone === 'specialized'} class:protocol-quality-superior={item.topBorderTone === 'superior'} class:protocol-quality-highend={item.topBorderTone === 'highend'}>
|
||||
@@ -39,7 +52,12 @@
|
||||
</div>
|
||||
<div class="database-item-identity">
|
||||
<small>{item.kicker}</small>
|
||||
<h2>{item.name}</h2>
|
||||
<div class="database-item-name-line">
|
||||
<h2>{item.name}</h2>
|
||||
{#if primaryMechanicGroup()}
|
||||
<DatabaseMechanicStatusBadge status={databaseMechanicGroupStatus(primaryMechanicGroup()!)} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if item.headerBadges?.length || item.headerBadge || item.headerMetaIcon}
|
||||
<div class="database-header-status">
|
||||
@@ -53,7 +71,7 @@
|
||||
{#if item.protocolPreview}
|
||||
<section class="database-protocol-preview">
|
||||
<div class="database-protocol-primary"><span>{item.protocolPreview.primaryStat}</span><b>{Number.isFinite(protocolPrimaryValue()) ? valueWithUnit(protocolPrimaryValue(), item.protocolPreview.primaryUnit) : 'NOT CONFIGURED'}</b></div>
|
||||
{#if item.protocolPreview.talentDescription}
|
||||
{#if item.protocolPreview.talentDescription && !hasMechanicGroups()}
|
||||
<div class="database-protocol-talent"><p>{talentDescription()}</p></div>
|
||||
{/if}
|
||||
<div class="database-protocol-controls"><label class="database-protocol-level"><span>LVL {protocolLevel}</span><input type="range" min="1" max="50" step="1" bind:value={protocolLevel} aria-label={`${item.name} level`}/></label>{#if item.protocolPreview.talentDescription}<div class="database-protocol-diamonds" role="group" aria-label={`${item.name} talent upgrades`}>{#each [1, 2, 3, 4, 5] as upgrade}<button type="button" class:active={talentUpgrades >= upgrade} aria-label={`Set talent upgrades to ${upgrade}`} aria-pressed={talentUpgrades === upgrade} onclick={() => talentUpgrades = talentUpgrades === upgrade ? 0 : upgrade}>◆</button>{/each}</div>{/if}</div>
|
||||
@@ -86,15 +104,29 @@
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if item.variant === 'talent'}
|
||||
{#if hasMechanicGroups() && !hasSpecializationMechanics()}
|
||||
<div class="database-mechanic-groups">
|
||||
{#each mechanicGroups() as group (group.id)}
|
||||
<DatabaseMechanicGroupCard {group} {detail}/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if item.variant === 'talent' && !hasMechanicGroups()}
|
||||
<div class="database-talent-tiers">
|
||||
<section><span>TIER I</span><p>{item.description ?? 'Description is not available.'}</p></section>
|
||||
<section><span>TIER II</span><p>{item.secondaryDescription ?? 'Description is not available.'}</p></section>
|
||||
{#if item.secondaryDescription}<section><span>TIER II</span><p>{item.secondaryDescription}</p></section>{/if}
|
||||
</div>
|
||||
{:else if !item.protocolPreview}
|
||||
{:else if !item.protocolPreview && (!hasMechanicGroups() || hasSpecializationMechanics())}
|
||||
{#if item.description}<p class:data-description={item.descriptionStyle === 'data'}>{item.description}</p>{/if}
|
||||
{#if item.secondaryDescription}<p class="secondary-description" class:data-description={item.descriptionStyle === 'data'}>{item.secondaryDescription}</p>{/if}
|
||||
{/if}
|
||||
{#if hasSpecializationMechanics()}
|
||||
<div class="database-mechanic-groups database-specialization-effect-groups">
|
||||
{#each mechanicGroups().filter(isSpecializationMechanic) as group (group.id)}
|
||||
<DatabaseMechanicGroupCard {group} {detail} showIdentity/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if item.afterLines?.length}
|
||||
<div class="database-item-lines after-description">{#each item.afterLines as line}<div><span>{line.label}</span><b>{line.value}</b></div>{/each}</div>
|
||||
{/if}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
let closeButton = $state<HTMLButtonElement | null>(null);
|
||||
let dialog = $state<HTMLDivElement | null>(null);
|
||||
const openPreview = async (item: DatabaseRelatedItem, button: HTMLButtonElement) => {
|
||||
if (!item.preview) return;
|
||||
selected = item;
|
||||
trigger = button;
|
||||
await tick();
|
||||
@@ -32,8 +33,8 @@
|
||||
};
|
||||
const trapDialogFocus = (event: KeyboardEvent) => {
|
||||
if (event.key !== 'Tab' || !dialog) return;
|
||||
const focusable = [...dialog.querySelectorAll<HTMLElement>('a[href],button:not(:disabled),[tabindex]:not([tabindex="-1"])')]
|
||||
.filter((element) => !element.hasAttribute('hidden'));
|
||||
const focusable = [...dialog.querySelectorAll<HTMLElement>('a[href],button:not(:disabled),input:not([type="hidden"]):not(:disabled),select:not(:disabled),textarea:not(:disabled),[tabindex]:not([tabindex="-1"])')]
|
||||
.filter((element) => !element.hasAttribute('hidden') && element.getClientRects().length > 0);
|
||||
const first = focusable[0];
|
||||
const last = focusable.at(-1);
|
||||
if (!first || !last) return;
|
||||
@@ -65,7 +66,7 @@
|
||||
<div class="database-preview-backdrop" role="presentation" onclick={(event) => { if (event.target === event.currentTarget) void closePreview(); }}>
|
||||
<div class="database-preview-dialog" bind:this={dialog} role="dialog" aria-modal="true" aria-labelledby={`database-preview-${selected.id}`} tabindex="-1" onkeydown={trapDialogFocus}>
|
||||
<header><div><small>DATABASE QUICK VIEW</small><strong id={`database-preview-${selected.id}`}>{selected.preview.name}</strong></div><button type="button" bind:this={closeButton} aria-label="Close details" onclick={() => void closePreview()}>×</button></header>
|
||||
<DatabaseItemCard item={selected.preview}/>
|
||||
<DatabaseItemCard item={selected.preview} anchor={false} detail/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
databaseMechanicGroupDisplaySourceLabel,
|
||||
databaseMechanicGroupStatus,
|
||||
databaseMechanicStatusLabel,
|
||||
type DatabaseMechanicGroup
|
||||
} from '$lib/database/items';
|
||||
import DatabaseMechanicStatusBadge from './DatabaseMechanicStatusBadge.svelte';
|
||||
|
||||
let {
|
||||
group,
|
||||
detail = false,
|
||||
showIdentity = false
|
||||
}: {
|
||||
group: DatabaseMechanicGroup;
|
||||
detail?: boolean;
|
||||
showIdentity?: boolean;
|
||||
} = $props();
|
||||
|
||||
const status = $derived(databaseMechanicGroupStatus(group));
|
||||
const displaySource = $derived(databaseMechanicGroupDisplaySourceLabel(group));
|
||||
</script>
|
||||
|
||||
<section
|
||||
class="database-mechanic-group"
|
||||
class:detail
|
||||
class:specialization-effect={showIdentity}
|
||||
data-status={status}
|
||||
aria-label={`${group.label}: ${databaseMechanicStatusLabel(status)}`}
|
||||
>
|
||||
{#if showIdentity}
|
||||
<header class="database-mechanic-group-identity">
|
||||
<div>
|
||||
<small>{group.label}</small>
|
||||
{#if group.context}<strong>{group.context}</strong>{/if}
|
||||
</div>
|
||||
<DatabaseMechanicStatusBadge {status}/>
|
||||
</header>
|
||||
{:else if group.context}
|
||||
<p class="database-mechanic-group-context">{group.context}</p>
|
||||
{/if}
|
||||
|
||||
<div class="database-mechanic-variants">
|
||||
{#each group.variants as variant (variant.label)}
|
||||
<section class="database-mechanic-variant" data-status={variant.status}>
|
||||
<div class="database-mechanic-variant-label"><span>{variant.label}</span></div>
|
||||
<div class="database-mechanic-behavior">
|
||||
<small>{variant.status === 'modeled' ? 'MODELED BEHAVIOR' : 'AUTHORED DESCRIPTION'}</small>
|
||||
<p aria-label={variant.behaviorText} title={detail ? undefined : variant.behaviorText}>{variant.behaviorText}</p>
|
||||
</div>
|
||||
</section>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if detail}
|
||||
<dl class="database-mechanic-display-source">
|
||||
<div><dt>DISPLAY SOURCE</dt><dd>{displaySource}</dd></div>
|
||||
</dl>
|
||||
{/if}
|
||||
</section>
|
||||
@@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
databaseMechanicStatusLabel,
|
||||
type DatabaseMechanicStatus
|
||||
} from '$lib/database/items';
|
||||
|
||||
let { status }: { status: DatabaseMechanicStatus } = $props();
|
||||
</script>
|
||||
|
||||
<strong class="database-mechanic-status" class:not-modeled={status === 'not_modeled'}>
|
||||
{databaseMechanicStatusLabel(status)}
|
||||
</strong>
|
||||
Reference in New Issue
Block a user