This repository has been archived on 2026-08-10. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
drizzli/src/routes/weather/week/[location]/ModelSelector.svelte
T
2026-08-01 17:58:03 +02:00

113 lines
3.6 KiB
Svelte

<script lang="ts">
import * as Select from '$lib/components/ui/select';
import * as m from '$lib/paraglide/messages';
import { type WeatherModelGroup, modelGroups } from '../../options';
interface Props {
selectedModel: string;
onModelChange: (model: string) => void;
/** Model list to offer; defaults to the deterministic forecast models */
groups?: WeatherModelGroup[];
label?: string;
}
let {
selectedModel,
onModelChange,
groups = modelGroups,
label = m.model_weather()
}: Props = $props();
let model = $derived(
groups.flatMap((group) => group.models).find((mo) => mo.value === selectedModel)
);
let modelLabel = $derived(model?.label ?? selectedModel);
let modelMeta = $derived.by(() => {
if (model?.resolution && model.resolution !== 'varies') {
return model.update ? `${model.resolution} · updated ${model.update}` : model.resolution;
}
return selectedModel === 'best_match'
? 'Automatically picks the best model for this location'
: '';
});
</script>
<Select.Root
name="model_selection"
type="single"
value={selectedModel}
onValueChange={(val) => {
if (val) onModelChange(val);
}}
>
<Select.Trigger
aria-label={m.model_selector_aria({ label })}
class="group h-auto min-h-12 min-w-0 flex-1 cursor-pointer gap-2.5 rounded-xl border-2 border-primary/35 bg-card py-1.5 ps-2.5 shadow-sm transition-colors hover:border-primary/70 hover:shadow-md data-[size=default]:h-auto data-[state=open]:border-primary sm:min-h-14 sm:gap-3 sm:py-2 sm:min-w-72 sm:flex-none"
>
<div
class="flex size-8 shrink-0 items-center justify-center rounded-lg bg-primary/12 text-primary sm:size-9"
>
<!-- layered-globe icon: weather model -->
<svg
class="size-4.5 sm:size-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
stroke-width="1.75"
>
<circle cx="12" cy="12" r="9" />
<path
stroke-linecap="round"
d="M3.6 9h16.8M3.6 15h16.8M12 3a15 15 0 0 1 0 18a15 15 0 0 1 0-18"
/>
</svg>
</div>
<div class="flex min-w-0 flex-1 flex-col items-start gap-0 overflow-hidden text-left">
<span class="text-[11px] font-semibold tracking-wide text-primary uppercase">
{label}
</span>
<span class="max-w-full truncate text-[13px] font-bold text-foreground sm:text-sm"
>{modelLabel}</span
>
{#if modelMeta}
<!-- the meta line is dropped on mobile to keep the trigger compact -->
<span
class="hidden max-w-full truncate text-[11px] leading-tight text-muted-foreground sm:block"
>
{modelMeta}
</span>
{/if}
</div>
</Select.Trigger>
<Select.Content preventScroll={false} class="max-h-[min(480px,60vh)] border-border">
{#each groups as group (group.value)}
<Select.Group>
<Select.GroupHeading
class="text-[10.5px] font-bold tracking-wider text-primary/80 uppercase"
>
{group.label}
</Select.GroupHeading>
{#each group.models as mo (mo.value)}
<Select.Item class="cursor-pointer" value={mo.value} label={mo.label}>
<!-- div, not span: the item base styles force flex row on spans -->
<div class="flex w-full flex-col items-start gap-0 leading-tight">
<span class="font-medium">{mo.label}</span>
{#if mo.resolution && mo.resolution !== 'varies'}
<span class="text-[11px] text-muted-foreground">
{mo.resolution}{mo.update ? ` · updated ${mo.update}` : ''}
</span>
{:else if mo.value === 'best_match'}
<span class="text-[11px] text-muted-foreground"
>{m.model_automatic_selection()}</span
>
{/if}
</div>
</Select.Item>
{/each}
</Select.Group>
{/each}
</Select.Content>
</Select.Root>