~james/core

core/web/src/lib/components/ui/DropdownItem.svelte -rw-r--r-- 2.1 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 16 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
<script module lang="ts">
	import { tv, type VariantProps } from "tailwind-variants";

	export const dropdownItem = tv({
		base: "w-full justify-start rounded-[calc(var(--radius-md)-var(--spacing))] px-xs text-left focus-visible:-outline-offset-2",
		variants: {
			danger: {
				false: "",
				true: "text-foreground-danger hover:bg-background-danger-subtle"
			}
		},
		defaultVariants: {
			danger: false
		}
	});

	export type DropdownItemVariants = VariantProps<typeof dropdownItem>;
</script>

<script lang="ts">
	import type { ResolvedPathname } from "$app/types";
	import type { Component, Snippet } from "svelte";
	import type { SvelteHTMLElements } from "svelte/elements";
	import { getContext } from "svelte";
	import Button, { type ButtonVariants } from "#lib/components/ui/Button.svelte";

	interface Props {
		href?: ResolvedPathname;
		target?: string;
		rel?: string;
		download?: string;
		title?: string;
		icon?: Component<SvelteHTMLElements["svg"]>;
		danger?: DropdownItemVariants["danger"];
		disabled?: boolean;
		onclick?: (event: MouseEvent) => void;
		/** Button hugs its children, so a two-column row needs `[&>span]:w-full` here. */
		class?: string;
		children: Snippet;
		size?: ButtonVariants["size"];
		"aria-label"?: string;
		[attr: `data-${string}`]: unknown;
	}

	let {
		href,
		icon,
		danger = false,
		disabled = false,
		onclick,
		class: className,
		children,
		size,
		...rest
	}: Props = $props();
	const classes = $derived(dropdownItem({ danger, class: className }));

	const closeDropdown = getContext<(() => void) | undefined>("dropdown-close");
	const registerItem = getContext<((el: HTMLElement) => () => void) | undefined>(
		"dropdown-register"
	);

	let el = $state<HTMLAnchorElement | HTMLButtonElement>();

	$effect(() => {
		if (!el || !registerItem) return;
		return registerItem(el);
	});

	const handleClick = (event: MouseEvent) => {
		closeDropdown?.();
		onclick?.(event);
	};
</script>

<Button
	{...rest}
	bind:ref={el}
	variant="ghost"
	{href}
	class={classes}
	{icon}
	role={registerItem ? "menuitem" : undefined}
	{disabled}
	tabindex={registerItem ? -1 : undefined}
	onclick={handleClick}
	{size}
>
	{@render children()}
</Button>