Add level-aware builder controls
This commit is contained in:
@@ -132,7 +132,13 @@
|
||||
{@render children()}
|
||||
|
||||
<footer class="site-footer">
|
||||
<div><a class="brand footer-brand" href="/"><span class="brand-mark"><i></i><b>SHD</b></span><span>SHD<span class="accent">.build</span></span></a><p>Independent loadout intelligence for the agent community.</p></div>
|
||||
<div>
|
||||
<a class="brand footer-brand" href="/" aria-label="SHD.build home">
|
||||
<span class="brand-mark"><i></i><b>SHD</b></span>
|
||||
<span class="brand-name">SHD<span>.build</span><small>LOADOUT NETWORK</small></span>
|
||||
</a>
|
||||
<p>Independent loadout intelligence for the agent community.</p>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
<a class="footer-discord" href="https://discord.gg/dhpd33VwQK" target="_blank" rel="noreferrer">
|
||||
<img src="/discord-symbol.svg" alt="" aria-hidden="true" />
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<script lang="ts">
|
||||
let {
|
||||
label,
|
||||
name,
|
||||
items,
|
||||
value = $bindable(''),
|
||||
columns = Math.min(items.length, 4),
|
||||
searchable = false,
|
||||
required = false,
|
||||
onchange
|
||||
}: {
|
||||
label: string;
|
||||
name: string;
|
||||
items: Array<{ id: string; name: string; iconUrl?: string; category?: string }>;
|
||||
value?: string;
|
||||
columns?: number;
|
||||
searchable?: boolean;
|
||||
required?: boolean;
|
||||
onchange?: (id: string) => void;
|
||||
} = $props();
|
||||
|
||||
let query = $state('');
|
||||
const visible = $derived(items.filter((item) =>
|
||||
!query.trim() || `${item.name} ${item.category ?? ''}`.toLocaleLowerCase().includes(query.trim().toLocaleLowerCase())
|
||||
));
|
||||
|
||||
function choose(id: string) {
|
||||
value = id;
|
||||
onchange?.(id);
|
||||
}
|
||||
</script>
|
||||
|
||||
<fieldset class="builder-radio-grid" style={`--builder-radio-columns:${Math.max(1, columns)}`}>
|
||||
<legend>{label}</legend>
|
||||
{#if searchable}
|
||||
<input class="builder-radio-search" type="search" bind:value={query} placeholder="SEARCH AVAILABLE OPTIONS" aria-label={`Search ${label.toLocaleLowerCase()}`}/>
|
||||
{/if}
|
||||
<div>
|
||||
{#each visible as item}
|
||||
<label title={item.name}>
|
||||
<input type="radio" {name} value={item.id} checked={value === item.id} {required} onchange={() => choose(item.id)}/>
|
||||
<span>
|
||||
{#if item.iconUrl}<img src={item.iconUrl} alt=""/>{:else}<i>{item.name.slice(0,2).toUpperCase()}</i>{/if}
|
||||
<b>{item.name}</b>
|
||||
{#if item.category}<small>{item.category}</small>{/if}
|
||||
</span>
|
||||
</label>
|
||||
{:else}
|
||||
<p>NO MATCHING OPTIONS</p>
|
||||
{/each}
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import { clamp } from '$lib/builder/loadout';
|
||||
|
||||
let {
|
||||
label,
|
||||
min,
|
||||
max,
|
||||
value = $bindable(),
|
||||
provisional = false,
|
||||
step = 1,
|
||||
onchange
|
||||
}: {
|
||||
label: string;
|
||||
min: number;
|
||||
max: number;
|
||||
value: number;
|
||||
provisional?: boolean;
|
||||
step?: number;
|
||||
onchange?: (value: number) => void;
|
||||
} = $props();
|
||||
|
||||
function update(raw: unknown) {
|
||||
const next = Number(clamp(raw, min, max));
|
||||
if (onchange) onchange(next);
|
||||
else value = next;
|
||||
}
|
||||
</script>
|
||||
|
||||
<label class="range-value-control">
|
||||
<span class="range-value-head">
|
||||
<b>{label}</b>
|
||||
<small>{min}–{max}{provisional ? ' · ESTIMATED' : ''}</small>
|
||||
</span>
|
||||
<span class="range-value-inputs">
|
||||
<input
|
||||
class="range-value-slider"
|
||||
type="range"
|
||||
{min}
|
||||
{max}
|
||||
{step}
|
||||
value={value}
|
||||
aria-label={`${label} slider`}
|
||||
oninput={(event) => update(event.currentTarget.value)}
|
||||
/>
|
||||
<input
|
||||
class="range-value-number"
|
||||
type="number"
|
||||
{min}
|
||||
{max}
|
||||
{step}
|
||||
value={value}
|
||||
aria-label={`${label} value`}
|
||||
onchange={(event) => update(event.currentTarget.value)}
|
||||
onblur={(event) => update(event.currentTarget.value)}
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
Reference in New Issue
Block a user