move settings to one icon mobile

This commit is contained in:
Vincent van der Wal
2026-08-01 12:39:19 +02:00
parent 8ad02ea3f9
commit 5cc9a5428c
23 changed files with 1511 additions and 189 deletions
@@ -0,0 +1,123 @@
<script lang="ts">
import { type Theme, storedTheme } from '$lib/stores/settings';
import * as Popover from '$lib/components/ui/popover';
import UnitOptions from '$lib/components/unit-options.svelte';
import UnlockDialog from '$lib/paywall/UnlockDialog.svelte';
import { isSupporter } from '$lib/paywall/supporter';
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: 'System' },
{ value: 'light', label: 'Light' },
{ value: 'dark', label: '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="Settings"
title="Settings"
>
<!-- 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"
>
Theme
</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>
<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;
}}
>
{#if $isSupporter}
<!-- star -->
<svg
class="h-4.5 w-4.5 shrink-0 text-amber-500"
viewBox="0 0 24 24"
fill="currentColor"
aria-hidden="true"
>
<path
d="M12 2.5l2.9 6 6.6.9-4.8 4.6 1.2 6.5L12 17.9 6.1 20.5l1.2-6.5L2.5 9.4l6.6-.9L12 2.5z"
/>
</svg>
{:else}
<!-- padlock -->
<svg
class="h-4.5 w-4.5 shrink-0 text-muted-foreground"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
stroke-width="1.75"
aria-hidden="true"
>
<rect x="4" y="10" width="16" height="11" rx="2" />
<path stroke-linecap="round" d="M8 10V7a4 4 0 0 1 8 0v3" />
</svg>
{/if}
<span class="min-w-0 flex-1">
<span class="block text-[13px] font-semibold">
{$isSupporter ? 'Supporter extras active' : 'Support Drizz.li'}
</span>
<span class="block text-[11px] text-muted-foreground">
{$isSupporter ? 'Manage your access key' : 'Unlock the supporter extras'}
</span>
</span>
</button>
</div>
</div>
</Popover.Content>
</Popover.Root>
<UnlockDialog bind:open={unlockOpen} />