Add reusable segmented inbox filters

This commit is contained in:
JB
2026-07-25 15:42:34 -04:00
parent 29f4de807a
commit 8b7b747866
7 changed files with 134 additions and 8 deletions
+65
View File
@@ -0,0 +1,65 @@
<script lang="ts">
import GameIcon from './GameIcon.svelte';
import type { GameIconName } from '$lib/game-icons';
export type SegmentedChoiceOption = {
value: string;
label: string;
className?: string;
icon?: GameIconName;
};
let {
legend,
name,
options,
mode = 'radio',
value = $bindable(''),
selected = $bindable([]),
columns = options.length,
required = false,
onchange
}: {
legend: string;
name: string;
options: SegmentedChoiceOption[];
mode?: 'radio' | 'checkbox';
value?: string;
selected?: string[];
columns?: number;
required?: boolean;
onchange?: (values: string[]) => void;
} = $props();
function change(optionValue: string, checked: boolean) {
if (mode === 'radio') {
value = optionValue;
onchange?.([optionValue]);
return;
}
selected = checked ? [...selected, optionValue] : selected.filter((current) => current !== optionValue);
onchange?.(selected);
}
</script>
<fieldset class:multi={mode === 'checkbox'} class="segmented-choice">
<legend>{legend}</legend>
<div style={`--segmented-columns:${columns}`}>
{#each options as option}
<label>
<input
type={mode}
{name}
value={option.value}
checked={mode === 'radio' ? value === option.value : selected.includes(option.value)}
{required}
onchange={(event) => change(option.value, event.currentTarget.checked)}
/>
<span class={option.className ?? ''}>
{#if option.icon}<GameIcon name={option.icon} decorative/>{:else}<i aria-hidden="true"></i>{/if}
<b>{option.label}</b>
</span>
</label>
{/each}
</div>
</fieldset>