<script module lang="ts">
	import type { Component } from "svelte";
	import type { SvelteHTMLElements } from "svelte/elements";

	export interface SettingsNavItem {
		id: string;
		label: string;
		href: string;
		icon?: Component<SvelteHTMLElements["svg"]>;
	}
</script>

<script lang="ts">
	import { goto } from "$app/navigation";
	import { resolve } from "$app/paths";
	import ChevronDown from "$icon/chevron-down";

	interface Props {
		items: SettingsNavItem[];
		active: string;
		label?: string;
	}

	let { items, active, label = "Settings" }: Props = $props();

	const ActiveGlyph = $derived(items.find((i) => i.id === active)?.icon);

	const jump = (event: Event) => {
		const item = items.find((i) => i.id === (event.currentTarget as HTMLSelectElement).value);
		if (item) void goto(resolve(item.href as "/"));
	};

	// Figma DS "Navigation / Button" (1009:2662) has four states across Active ×
	// Hovered. Inactive/resting is the only one with no fill and no border, so the
	// hover borders have to be added rather than recoloured — otherwise the item
	// grows by 1px on hover.
	const itemClass = (isActive: boolean) =>
		[
			"flex min-h-8 w-full items-center gap-1.5 overflow-hidden rounded-sm border px-3 py-1.5",
			"typography-paragraph-regular text-foreground-default no-underline hover:no-underline",
			"transition-colors duration-150 ease-in-out",
			"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus",
			isActive
				? "border-border-navigation-item-active bg-background-navigation-item hover:border-border-strong hover:bg-background-muted"
				: "border-transparent hover:border-border-strong hover:bg-background-inset"
		].join(" ");
</script>

<!-- Below md the panel is only as wide as the phone, so the sidebar collapses into
     a native picker rather than seven rows that push the page content off-screen.
     The hairline under it stands in for the sidebar's edge. -->
<div class="flex w-full flex-col gap-4 md:hidden">
	<div class="relative w-full">
		{#if ActiveGlyph}
			<ActiveGlyph
				class="pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 text-foreground-default"
				aria-hidden="true"
			/>
		{/if}
		<select
			aria-label={label}
			value={active}
			onchange={jump}
			class="h-8 w-full appearance-none rounded-sm border border-border-default bg-background-default pr-8 typography-paragraph-regular text-foreground-default outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus {ActiveGlyph
				? 'pl-7'
				: 'pl-2'}"
		>
			{#each items as item (item.id)}
				<option value={item.id}>{item.label}</option>
			{/each}
		</select>
		<ChevronDown
			class="pointer-events-none absolute top-1/2 right-2 size-3.5 -translate-y-1/2 text-foreground-default"
			aria-hidden="true"
		/>
	</div>
	<hr class="w-full border-0 border-t border-border-default" />
</div>

<nav
	class="hidden w-full shrink-0 flex-col gap-1 bg-background-navigation-frame p-2 md:flex md:w-44 md:self-stretch md:border-r md:border-border-default"
	aria-label={label}
>
	{#each items as item (item.id)}
		{@const isActive = active === item.id}
		{@const Glyph = item.icon}
		<a
			href={resolve(item.href as "/")}
			aria-current={isActive ? "page" : undefined}
			class={itemClass(isActive)}
		>
			{#if Glyph}
				<Glyph class="size-3.5 shrink-0" aria-hidden="true" />
			{/if}
			<!-- Figma clips the item rather than letting a long label wrap it taller; an
			     ellipsis keeps that height without hiding the label outright -->
			<span class="min-w-0 truncate">{item.label}</span>
		</a>
	{/each}
</nav>
