Files
beavergram/frontend/src/lib/components/ui/ContextMenu.svelte
T

61 lines
1.5 KiB
Svelte

<script lang="ts" module>
type PointerHandler = (event: PointerEvent) => void;
const claimed = new WeakSet<Event>();
</script>
<script lang="ts">
import { ContextMenu } from "bits-ui";
import type { Snippet } from "svelte";
interface Props {
children: Snippet<[{ props: Record<string, unknown> }]>;
menu: Snippet;
}
const { children, menu }: Props = $props();
let open = $state(false);
let suppressClick = false;
function triggerProps(props: Record<string, unknown>) {
const onpointerdown = props.onpointerdown as PointerHandler;
const onpointerup = props.onpointerup as PointerHandler;
return {
...props,
onclickcapture(event: MouseEvent) {
if (suppressClick) {
suppressClick = false;
event.preventDefault();
event.stopPropagation();
}
},
onpointerdown(event: PointerEvent) {
suppressClick = false;
if (claimed.has(event)) {
return;
}
claimed.add(event);
onpointerdown(event);
},
onpointerup(event: PointerEvent) {
suppressClick = open && event.pointerType !== "mouse";
onpointerup(event);
},
};
}
</script>
<ContextMenu.Root bind:open>
<ContextMenu.Trigger>
{#snippet child({ props })}
{@render children({ props: triggerProps(props) })}
{/snippet}
</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content class="bg-menu-content">
{@render menu()}
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>