Files
tools/vue/primitives/src/menu/MenuSubTrigger.vue
T

90 lines
2.5 KiB
Vue

<script lang="ts">
import type { MenuItemImplProps } from './MenuItemImpl.vue';
/**
* A menu item that opens its parent MenuSub's submenu. It acts as both the
* positioning anchor and the trigger, opening on hover (with a grace delay) or
* via the directional arrow key and closing on the opposite arrow. Must be used
* inside a MenuSub, alongside a MenuSubContent.
*/
export interface MenuSubTriggerProps extends MenuItemImplProps {}
</script>
<script setup lang="ts">
import { PopperAnchor } from '../popper';
import { useMenuContentContext, useMenuContext, useMenuRootContext, useMenuSubContext } from './context';
import MenuItemImpl from './MenuItemImpl.vue';
import { SUB_CLOSE_KEYS, SUB_OPEN_KEYS, getOpenState } from './utils';
const props = defineProps<MenuSubTriggerProps>();
const menuCtx = useMenuContext();
const subCtx = useMenuSubContext();
const rootCtx = useMenuRootContext();
const contentCtx = useMenuContentContext();
let openTimer: ReturnType<typeof setTimeout> | undefined;
function open() {
clearTimeout(openTimer);
menuCtx.onOpenChange(true);
}
function close() {
clearTimeout(openTimer);
menuCtx.onOpenChange(false);
}
function handlePointerMove(event: PointerEvent) {
if (event.pointerType === 'touch') return;
if (props.disabled) return;
if (contentCtx.onItemEnter(event)) return;
if (!menuCtx.open.value) {
clearTimeout(openTimer);
openTimer = setTimeout(() => menuCtx.onOpenChange(true), 100);
}
}
function handlePointerLeave(event: PointerEvent) {
if (event.pointerType === 'touch') return;
clearTimeout(openTimer);
if (contentCtx.onTriggerLeave(event)) return;
close();
}
function handleKeyDown(event: KeyboardEvent) {
if (props.disabled) return;
const openKeys = SUB_OPEN_KEYS[rootCtx.dir.value]!;
const closeKeys = SUB_CLOSE_KEYS[rootCtx.dir.value]!;
if (openKeys.includes(event.key)) {
event.preventDefault();
open();
}
if (closeKeys.includes(event.key)) {
event.preventDefault();
close();
}
}
</script>
<template>
<PopperAnchor>
<MenuItemImpl
v-bind="props"
:id="subCtx.triggerId.value"
:ref="(el: unknown) => subCtx.onTriggerChange((el as any)?.$el ?? null)"
aria-haspopup="menu"
:aria-expanded="menuCtx.open.value"
:aria-controls="subCtx.contentId.value"
:data-state="getOpenState(menuCtx.open.value)"
role="menuitem"
@pointermove="handlePointerMove"
@pointerleave="handlePointerLeave"
@keydown="handleKeyDown"
@select.prevent
>
<slot />
</MenuItemImpl>
</PopperAnchor>
</template>