|
|
@@ -0,0 +1,77 @@
|
|
|
+<script lang="ts">
|
|
|
+ import { sineInOut } from 'svelte/easing';
|
|
|
+ import type { ClassValue } from 'svelte/elements';
|
|
|
+ import { slide } from 'svelte/transition';
|
|
|
+
|
|
|
+ import type { FilterRow } from './filters.types';
|
|
|
+
|
|
|
+ let {
|
|
|
+ class: className = '',
|
|
|
+ shown,
|
|
|
+ rows = $bindable(),
|
|
|
+ }: { class?: ClassValue; shown: boolean; rows: FilterRow[] } = $props();
|
|
|
+
|
|
|
+ const onSelect = (idx: number, value: string) => {
|
|
|
+ rows = rows.map((row, i) => {
|
|
|
+ if (i === idx) {
|
|
|
+ if (row.type === 'single') {
|
|
|
+ return {
|
|
|
+ ...row,
|
|
|
+ selected: value,
|
|
|
+ };
|
|
|
+ } else if (row.type === 'multi') {
|
|
|
+ if (row.selected.has(value)) row.selected.delete(value);
|
|
|
+ else row.selected.add(value);
|
|
|
+ }
|
|
|
+ if (row.onchange) row.onchange(value);
|
|
|
+ }
|
|
|
+ return row;
|
|
|
+ });
|
|
|
+ };
|
|
|
+</script>
|
|
|
+
|
|
|
+{#if shown}
|
|
|
+ <div
|
|
|
+ class={['flex w-full flex-col gap-1 overflow-hidden', className]}
|
|
|
+ transition:slide={{ easing: sineInOut, duration: 200 }}
|
|
|
+ >
|
|
|
+ {#each rows as row, idx (row.label)}
|
|
|
+ <div class="flex w-full items-center gap-1">
|
|
|
+ <span class="w-20 shrink-0 text-xs text-white/25">{row.label}</span>
|
|
|
+ <div class="flex flex-wrap items-center gap-1">
|
|
|
+ {#if row.type === 'single'}
|
|
|
+ {#each row.options as opt (opt.value)}
|
|
|
+ {@const active = row.selected === opt.value}
|
|
|
+ <button
|
|
|
+ class={[
|
|
|
+ 'inline-flex h-7 items-center gap-1 rounded-md px-2 text-xs font-medium transition-colors',
|
|
|
+ active
|
|
|
+ ? 'bg-primary/50 text-white/75'
|
|
|
+ : 'text-white/40 hover:bg-white/6 hover:text-white/70',
|
|
|
+ ]}
|
|
|
+ onclick={() => onSelect(idx, opt.value)}
|
|
|
+ >
|
|
|
+ {opt.label}
|
|
|
+ </button>
|
|
|
+ {/each}
|
|
|
+ {:else}
|
|
|
+ {#each row.options as opt (opt.value)}
|
|
|
+ {@const active = row.selected.has(opt.value)}
|
|
|
+ <button
|
|
|
+ class={[
|
|
|
+ 'inline-flex h-7 items-center rounded-md px-2 text-xs font-medium transition-colors',
|
|
|
+ active
|
|
|
+ ? 'bg-primary/50 text-white/75'
|
|
|
+ : 'text-white/40 hover:bg-white/6 hover:text-white/70',
|
|
|
+ ]}
|
|
|
+ onclick={() => onSelect(idx, opt.value)}
|
|
|
+ >
|
|
|
+ {opt.label}
|
|
|
+ </button>
|
|
|
+ {/each}
|
|
|
+ {/if}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ {/each}
|
|
|
+ </div>
|
|
|
+{/if}
|