<script lang="ts">
import type { Snippet } from "svelte";
import { placeBeside } from "../../utils/menu.js";
interface Props {
label: string;
detail?: string;
disabled?: boolean;
children: Snippet;
}
let { label, detail = "", disabled = false, children }: Props = $props();
let open = $state(false);
let anchor = $state<HTMLElement>();
let panel = $state<HTMLElement>();
let hoverTimer: ReturnType<typeof setTimeout> | undefined;
$effect(() => {
if (open && panel && anchor) placeBeside(panel, anchor);
});
function openLater() {
if (disabled) return;
clearTimeout(hoverTimer);
hoverTimer = setTimeout(() => (open = true), 120);
}
function closeLater() {
clearTimeout(hoverTimer);
hoverTimer = setTimeout(() => (open = false), 180);
}
function keep() {
clearTimeout(hoverTimer);
}
</script>
<div class="submenu-holder" onmouseenter={openLater} onmouseleave={closeLater}>
<button
class="context-menu-item submenu-item"
bind:this={anchor}
{disabled}
aria-haspopup="menu"
aria-expanded={open}
onclick={() => (open = !open)}
>
<span class="submenu-label">{label}</span>
{#if detail}<span class="submenu-detail">{detail}</span>{/if}
<span class="submenu-caret">›</span>
</button>
{#if open}
<div
class="context-menu submenu-panel"
bind:this={panel}
role="menu"
tabindex="-1"
onmouseenter={keep}
onmouseleave={closeLater}
onclick={() => (open = false)}
>
{@render children()}
</div>
{/if}
</div>
<style>
.submenu-holder {
position: relative;
}
.submenu-item {
display: flex;
align-items: center;
gap: var(--wb-spacing-8);
}
.submenu-label {
flex: 1;
}
.submenu-detail,
.submenu-caret {
opacity: 0.6;
font-size: var(--wb-font-size-11);
}
.submenu-panel {
position: fixed;
z-index: 10000;
}
</style>