57 lines
1.5 KiB
Svelte
57 lines
1.5 KiB
Svelte
<script lang="ts">
|
|
import ItemIcon from './ItemIcon.svelte';
|
|
|
|
let {
|
|
label,
|
|
name,
|
|
items,
|
|
value = $bindable(''),
|
|
columns = Math.min(items.length, 4),
|
|
searchable = false,
|
|
iconOnly = false,
|
|
required = false,
|
|
onchange
|
|
}: {
|
|
label: string;
|
|
name: string;
|
|
items: Array<{ id: string; name: string; iconUrl?: string; category?: string }>;
|
|
value?: string;
|
|
columns?: number;
|
|
searchable?: boolean;
|
|
iconOnly?: 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" class:icon-only={iconOnly} 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} aria-label={item.name} onchange={() => choose(item.id)}/>
|
|
<span>
|
|
<ItemIcon src={item.iconUrl} alt=""/>
|
|
<b>{item.name}</b>
|
|
{#if item.category}<small>{item.category}</small>{/if}
|
|
</span>
|
|
</label>
|
|
{:else}
|
|
<p>NO MATCHING OPTIONS</p>
|
|
{/each}
|
|
</div>
|
|
</fieldset>
|