1<script lang="ts">
2 import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
3 import * as Tooltip from '$lib/components/ui/tooltip';
4 import { KeyboardShortcutInfo } from '$lib/components/app';
5 import type { Component } from 'svelte';
6
7 interface ActionItem {
8 icon: Component;
9 label: string;
10 onclick: (event: Event) => void;
11 variant?: 'default' | 'destructive';
12 disabled?: boolean;
13 shortcut?: string[];
14 separator?: boolean;
15 }
16
17 interface Props {
18 triggerIcon: Component;
19 triggerTooltip?: string;
20 triggerClass?: string;
21 actions: ActionItem[];
22 align?: 'start' | 'center' | 'end';
23 open?: boolean;
24 }
25
26 let {
27 triggerIcon,
28 triggerTooltip,
29 triggerClass = '',
30 actions,
31 align = 'end',
32 open = $bindable(false)
33 }: Props = $props();
34</script>
35
36<DropdownMenu.Root bind:open>
37 <DropdownMenu.Trigger
38 class="flex h-6 w-6 cursor-pointer items-center justify-center rounded-md p-0 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground {triggerClass}"
39 onclick={(e) => e.stopPropagation()}
40 >
41 {#if triggerTooltip}
42 <Tooltip.Root>
43 <Tooltip.Trigger>
44 {@render iconComponent(triggerIcon, 'h-3 w-3')}
45 <span class="sr-only">{triggerTooltip}</span>
46 </Tooltip.Trigger>
47 <Tooltip.Content>
48 <p>{triggerTooltip}</p>
49 </Tooltip.Content>
50 </Tooltip.Root>
51 {:else}
52 {@render iconComponent(triggerIcon, 'h-3 w-3')}
53 {/if}
54 </DropdownMenu.Trigger>
55
56 <DropdownMenu.Content {align} class="z-[999999] w-48">
57 {#each actions as action, index (action.label)}
58 {#if action.separator && index > 0}
59 <DropdownMenu.Separator />
60 {/if}
61
62 <DropdownMenu.Item
63 onclick={action.onclick}
64 variant={action.variant}
65 disabled={action.disabled}
66 class="flex items-center justify-between hover:[&>kbd]:opacity-100"
67 >
68 <div class="flex items-center gap-2">
69 {@render iconComponent(
70 action.icon,
71 `h-4 w-4 ${action.variant === 'destructive' ? 'text-destructive' : ''}`
72 )}
73 {action.label}
74 </div>
75
76 {#if action.shortcut}
77 <KeyboardShortcutInfo keys={action.shortcut} variant={action.variant} />
78 {/if}
79 </DropdownMenu.Item>
80 {/each}
81 </DropdownMenu.Content>
82</DropdownMenu.Root>
83
84{#snippet iconComponent(IconComponent: Component, className: string)}
85 <IconComponent class={className} />
86{/snippet}