60 lines
2.4 KiB
JavaScript
60 lines
2.4 KiB
JavaScript
import * as React from 'react';
|
|
import { useEventCallback, elementContains } from '@fluentui/react-utilities';
|
|
/**
|
|
* Name of the custom event
|
|
*/ export const MENU_ENTER_EVENT = 'fuimenuenter';
|
|
/**
|
|
* This hook works similarly to @see useOnClickOutside
|
|
*
|
|
* Problem: Trying to behave the same as system menus:
|
|
* When the mouse leaves a stack of nested menus the stack should not dismiss.
|
|
* However if the mouse leaves a stack of menus and enters a parent menu all its children menu should dismiss.
|
|
*
|
|
* We don't use the native mouseenter event because it would trigger too many times in the document
|
|
* Instead, dispatch custom DOM event from the menu so that it can bubble
|
|
* Each nested menu can use the listener to check if the event is from a child or parent menu
|
|
*/ export const useOnMenuMouseEnter = (options)=>{
|
|
const { refs, callback, element, disabled } = options;
|
|
// Keep mouse event here because this is essentially a custom 'mouseenter' event
|
|
const listener = useEventCallback((ev)=>{
|
|
const popoverRef = refs[0];
|
|
const someMenuPopover = ev.target;
|
|
var _popoverRef_current;
|
|
// someMenu is a child -> will always be contained because of vParents
|
|
// someMenu is a parent -> will always not be contained because no vParent
|
|
// someMenu is the current popover -> it will contain itself
|
|
const isOutsidePopover = !elementContains((_popoverRef_current = popoverRef.current) !== null && _popoverRef_current !== void 0 ? _popoverRef_current : null, someMenuPopover);
|
|
if (isOutsidePopover && !disabled) {
|
|
callback(ev);
|
|
}
|
|
});
|
|
React.useEffect(()=>{
|
|
// eslint-disable-next-line eqeqeq
|
|
if (element == null) {
|
|
return;
|
|
}
|
|
if (!disabled) {
|
|
element.addEventListener(MENU_ENTER_EVENT, listener);
|
|
}
|
|
return ()=>{
|
|
element.removeEventListener(MENU_ENTER_EVENT, listener);
|
|
};
|
|
}, [
|
|
listener,
|
|
element,
|
|
disabled
|
|
]);
|
|
};
|
|
/**
|
|
* Dispatches the custom MouseEvent enter event. Similar to calling `el.click()`
|
|
* @param el - element for the event target
|
|
* @param nativeEvent - the native mouse event this is mapped to
|
|
*/ export const dispatchMenuEnterEvent = (el, nativeEvent)=>{
|
|
el.dispatchEvent(new CustomEvent(MENU_ENTER_EVENT, {
|
|
bubbles: true,
|
|
detail: {
|
|
nativeEvent
|
|
}
|
|
}));
|
|
};
|