Add level-aware builder controls

This commit is contained in:
JB
2026-07-25 20:52:08 -04:00
parent 7f9a75021d
commit 0425fede8e
13 changed files with 654 additions and 26 deletions
@@ -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>