113 lines
4.1 KiB
Svelte
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} />
|