<script module lang="ts">
	import { SvelteMap, SvelteSet } from 'svelte/reactivity';

	// opening one dropdown in a group closes its siblings.
	const groups = new SvelteMap<string, SvelteSet<() => void>>();

	function registerDropdown(group: string | undefined, close: () => void) {
		if (!group) return () => {};
		let set = groups.get(group);
		if (!set) {
			set = new SvelteSet();
			groups.set(group, set);
		}
		set.add(close);
		return () => {
			set.delete(close);
		};
	}

	function closeGroupExcept(group: string | undefined, keep: () => void) {
		if (!group) return;
		groups.get(group)?.forEach((close) => {
			if (close !== keep) close();
		});
	}
</script>

<script lang="ts">
	import type { Snippet } from 'svelte';
	import { onMount, setContext } from 'svelte';
	import Button from './Button.svelte';

	interface Props {
		id?: string;
		group?: string;
		variant?: 'plain' | 'button';
		label?: string;
		trigger: Snippet;
		children: Snippet;
	}

	let { id, group, variant = 'plain', label, trigger, children }: Props = $props();

	let open = $state(false);
	let root = $state<HTMLElement>();

	const close = () => {
		open = false;
	};

	const toggle = () => {
		open = !open;
		if (open) closeGroupExcept(group, close);
	};

	setContext('dropdown-close', close);

	onMount(() => {
		const unregister = registerDropdown(group, close);

		const onDocumentClick = (event: MouseEvent) => {
			if (open && event.target instanceof Node && root && !root.contains(event.target)) close();
		};
		const onKeydown = (event: KeyboardEvent) => {
			if (event.key === 'Escape') close();
		};

		document.addEventListener('click', onDocumentClick);
		document.addEventListener('keydown', onKeydown);

		return () => {
			unregister();
			document.removeEventListener('click', onDocumentClick);
			document.removeEventListener('keydown', onKeydown);
		};
	});
</script>

<div bind:this={root} class="dropdown">
	{#if variant === 'button'}
		<Button
			size="sm"
			class="gap-2 !py-0 text-sm"
			onclick={toggle}
			aria-haspopup="menu"
			aria-expanded={open}
			aria-label={label}
		>
			{@render trigger()}
		</Button>
	{:else}
		<button
			type="button"
			class="plain"
			onclick={toggle}
			aria-haspopup="menu"
			aria-expanded={open}
			aria-label={label}
		>
			{@render trigger()}
		</button>
	{/if}

	<div {id} class="menu" class:open role="menu">
		{@render children()}
	</div>
</div>

<style>
	.dropdown {
		position: relative;
		display: inline-block;
	}

	.plain {
		display: flex;
		align-items: center;
		gap: 0.25rem;
		border: 0;
		background: transparent;
		padding: 0;
		color: inherit;
		cursor: pointer;
	}

	.menu {
		position: absolute;
		right: 0;
		z-index: 50;
		margin-top: 0.5rem;
		width: 14rem;
		overflow: hidden;
		border: 1px solid var(--color-border);
		border-radius: 0.25rem;
		background-color: var(--color-surface);
		box-shadow: var(--shadow-menu);
		font-size: 0.875rem;
	}

	.menu:not(.open) {
		display: none;
	}
</style>
