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/lib/components/navigation/settings-menu.svelte
T
2026-08-07 10:58:30 +02:00

113 lines
4.1 KiB
Svelte

<script lang="ts">
import { type Theme, storedTheme } from '$lib/stores/settings';
import LanguageOptions from '$lib/components/language-options.svelte';
import * as Popover from '$lib/components/ui/popover';
import UnitOptions from '$lib/components/unit-options.svelte';
import * as m from '$lib/paraglide/messages';
import SupporterIcon from '$lib/supporter/SupporterIcon.svelte';
import UnlockDialog from '$lib/supporter/UnlockDialog.svelte';
import { SUPPORTER_ENABLED } from '$lib/supporter/config';
import { isSupporter } from '$lib/supporter/store';
import ThemeIcon from './theme-icon.svelte';
// The topbar has room for one control on a phone, so units, theme and the
// supporter status share this menu instead of each carrying its own trigger.
const THEMES: { value: Theme; label: () => string }[] = [
{ value: 'system', label: m.theme_system },
{ value: 'light', label: m.theme_light },
{ value: 'dark', label: m.theme_dark }
];
let open = $state(false);
let unlockOpen = $state(false);
</script>
<Popover.Root bind:open>
<Popover.Trigger
class="flex h-9 w-9 shrink-0 cursor-pointer items-center justify-center rounded-full border border-border/70 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground data-[state=open]:bg-muted data-[state=open]:text-foreground"
aria-label={m.settings_title()}
title={m.settings_title()}
>
<!-- gear -->
<svg
class="h-4.5 w-4.5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
stroke-width="1.75"
>
<circle cx="12" cy="12" r="3" />
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-2.9 1.2v.2a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.9.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0-1.2-2.9H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.9l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.9.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 2.9 1.2l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.9V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z"
/>
</svg>
</Popover.Trigger>
<Popover.Content align="end" class="w-72 border-border">
<div class="flex flex-col gap-4">
<UnitOptions />
<div>
<span
class="mb-1.5 block text-[11px] font-semibold tracking-wide text-muted-foreground uppercase"
>
{m.theme_label()}
</span>
<div class="flex gap-1 rounded-lg bg-muted p-0.5">
{#each THEMES as option (option.value)}
{@const active = $storedTheme === option.value}
<button
class="flex flex-1 cursor-pointer items-center justify-center gap-1.5 rounded-md px-2 py-1.5 text-[13px] font-semibold transition-colors {active
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'}"
aria-pressed={active}
onclick={() => storedTheme.set(option.value)}
>
<ThemeIcon theme={option.value} class="h-4 w-4" />
{option.label()}
</button>
{/each}
</div>
</div>
<LanguageOptions onSelect={() => (open = false)} />
<!-- Supporter status / unlock (hidden while supporter features are parked) -->
{#if SUPPORTER_ENABLED}
<div class="border-t border-border/70 pt-3">
<button
type="button"
class="flex w-full cursor-pointer items-center gap-2.5 rounded-lg px-2 py-2 text-left transition-colors hover:bg-muted"
onclick={() => {
open = false;
unlockOpen = true;
}}
>
<SupporterIcon
filled={$isSupporter}
class="h-4.5 w-4.5 shrink-0 {$isSupporter
? 'text-amber-500'
: 'text-muted-foreground'}"
/>
<span class="min-w-0 flex-1">
<span class="block text-[13px] font-semibold">
{$isSupporter ? m.supporter_active() : m.supporter_support()}
</span>
<span class="block text-[11px] text-muted-foreground">
{$isSupporter ? m.supporter_manage_key() : m.supporter_unlock()}
</span>
</span>
</button>
</div>
{/if}
</div>
</Popover.Content>
</Popover.Root>
<UnlockDialog bind:open={unlockOpen} />