61 lines
1.5 KiB
Svelte
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>
|