201 lines
9.1 KiB
JavaScript
201 lines
9.1 KiB
JavaScript
'use client';
|
|
"use strict";
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "useMenuTrigger_unstable", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return useMenuTrigger_unstable;
|
|
}
|
|
});
|
|
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
|
const _reactaria = require("@fluentui/react-aria");
|
|
const _keyboardkeys = require("@fluentui/keyboard-keys");
|
|
const _reactsharedcontexts = require("@fluentui/react-shared-contexts");
|
|
const _reacttabster = require("@fluentui/react-tabster");
|
|
const _reactutilities = require("@fluentui/react-utilities");
|
|
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
|
const _menuContext = require("../../contexts/menuContext");
|
|
const _menuListContext = require("../../contexts/menuListContext");
|
|
const _utils = require("../../utils");
|
|
function noop() {
|
|
// does nothing
|
|
}
|
|
const useMenuTrigger_unstable = (props)=>{
|
|
const { children, disableButtonEnhancement = false } = props;
|
|
const triggerRef = (0, _menuContext.useMenuContext_unstable)((context)=>context.triggerRef);
|
|
const menuPopoverRef = (0, _menuContext.useMenuContext_unstable)((context)=>context.menuPopoverRef);
|
|
const setOpen = (0, _menuContext.useMenuContext_unstable)((context)=>context.setOpen);
|
|
const open = (0, _menuContext.useMenuContext_unstable)((context)=>context.open);
|
|
const triggerId = (0, _menuContext.useMenuContext_unstable)((context)=>context.triggerId);
|
|
const openOnHover = (0, _menuContext.useMenuContext_unstable)((context)=>context.openOnHover);
|
|
const openOnContext = (0, _menuContext.useMenuContext_unstable)((context)=>context.openOnContext);
|
|
const isSubmenu = (0, _utils.useIsSubmenu)();
|
|
const shouldOpenOnArrowRight = (0, _menuListContext.useMenuListContext_unstable)((ctx)=>{
|
|
var _ctx_shouldOpenOnArrowRight;
|
|
return (_ctx_shouldOpenOnArrowRight = ctx.shouldOpenOnArrowRight) !== null && _ctx_shouldOpenOnArrowRight !== void 0 ? _ctx_shouldOpenOnArrowRight : true;
|
|
});
|
|
const { findFirstFocusable } = (0, _reacttabster.useFocusFinders)();
|
|
const focusFirst = _react.useCallback(()=>{
|
|
const firstFocusable = findFirstFocusable(menuPopoverRef.current);
|
|
firstFocusable === null || firstFocusable === void 0 ? void 0 : firstFocusable.focus();
|
|
}, [
|
|
findFirstFocusable,
|
|
menuPopoverRef
|
|
]);
|
|
const openedWithKeyboardRef = _react.useRef(false);
|
|
const openedViaSafeZoneRef = _react.useRef(false);
|
|
const hasMouseMovedRef = _react.useRef(false);
|
|
const { dir } = (0, _reactsharedcontexts.useFluent_unstable)();
|
|
const OpenArrowKey = dir === 'ltr' ? _keyboardkeys.ArrowRight : _keyboardkeys.ArrowLeft;
|
|
const child = (0, _reactutilities.getTriggerChild)(children);
|
|
// Heads up!
|
|
//
|
|
// Handles an edge case where mouse movement over the menu trigger didn't happen as safe zone blocked pointer events,
|
|
// but the cursor is already over the menu trigger.
|
|
const safeZoneHandlerRef = (0, _utils.useOnMenuSafeZoneTimeout)((0, _reactutilities.useEventCallback)(()=>{
|
|
if (isSubmenu) {
|
|
openedViaSafeZoneRef.current = true;
|
|
}
|
|
}));
|
|
const onContextMenu = (event)=>{
|
|
if (isTargetDisabled(event) || event.isDefaultPrevented()) {
|
|
return;
|
|
}
|
|
if (openOnContext) {
|
|
event.preventDefault();
|
|
setOpen(event, {
|
|
open: true,
|
|
keyboard: false,
|
|
type: 'menuTriggerContextMenu',
|
|
event
|
|
});
|
|
}
|
|
};
|
|
const onClick = (event)=>{
|
|
if (isTargetDisabled(event)) {
|
|
return;
|
|
}
|
|
if (!openOnContext) {
|
|
setOpen(event, {
|
|
open: !open,
|
|
keyboard: openedWithKeyboardRef.current,
|
|
type: 'menuTriggerClick',
|
|
event
|
|
});
|
|
openedWithKeyboardRef.current = false;
|
|
}
|
|
};
|
|
const onKeyDown = (event)=>{
|
|
if (isTargetDisabled(event) || event.isDefaultPrevented()) {
|
|
return;
|
|
}
|
|
const key = event.key;
|
|
if (!openOnContext && (isSubmenu && shouldOpenOnArrowRight && key === OpenArrowKey || !isSubmenu && key === _keyboardkeys.ArrowDown)) {
|
|
setOpen(event, {
|
|
open: true,
|
|
keyboard: true,
|
|
type: 'menuTriggerKeyDown',
|
|
event
|
|
});
|
|
}
|
|
if (key === _keyboardkeys.Escape && !isSubmenu) {
|
|
setOpen(event, {
|
|
open: false,
|
|
keyboard: true,
|
|
type: 'menuTriggerKeyDown',
|
|
event
|
|
});
|
|
}
|
|
// if menu is already open, can't rely on effects to focus
|
|
if (open && key === OpenArrowKey && isSubmenu && shouldOpenOnArrowRight) {
|
|
focusFirst();
|
|
}
|
|
};
|
|
const onMouseOver = (event)=>{
|
|
if (isTargetDisabled(event)) {
|
|
return;
|
|
}
|
|
if (openOnHover) {
|
|
if (hasMouseMovedRef.current) {
|
|
setOpen(event, {
|
|
open: true,
|
|
keyboard: false,
|
|
type: 'menuTriggerMouseEnter',
|
|
event
|
|
});
|
|
} else if (openedViaSafeZoneRef.current) {
|
|
setOpen(event, {
|
|
open: true,
|
|
keyboard: false,
|
|
ignoreHoverDelay: true,
|
|
type: 'menuTriggerMouseEnter',
|
|
event
|
|
});
|
|
openedViaSafeZoneRef.current = false;
|
|
}
|
|
}
|
|
};
|
|
// Opening a menu when a mouse hasn't moved and just entering the trigger is a bad a11y experience
|
|
// First time open the mouse using mousemove and then continue with mouseenter
|
|
// Only use once to determine that the user is using the mouse since it is an expensive event to handle
|
|
const onMouseMove = (event)=>{
|
|
if (isTargetDisabled(event)) {
|
|
return;
|
|
}
|
|
if (openOnHover && !hasMouseMovedRef.current) {
|
|
setOpen(event, {
|
|
open: true,
|
|
keyboard: false,
|
|
type: 'menuTriggerMouseMove',
|
|
event
|
|
});
|
|
hasMouseMovedRef.current = true;
|
|
}
|
|
};
|
|
const onMouseLeave = (event)=>{
|
|
if (isTargetDisabled(event)) {
|
|
return;
|
|
}
|
|
if (openOnHover) {
|
|
setOpen(event, {
|
|
open: false,
|
|
keyboard: false,
|
|
type: 'menuTriggerMouseLeave',
|
|
event
|
|
});
|
|
}
|
|
};
|
|
var _child_props_onMouseEnter;
|
|
const contextMenuProps = {
|
|
id: triggerId,
|
|
...child === null || child === void 0 ? void 0 : child.props,
|
|
ref: (0, _reactutilities.useMergedRefs)(triggerRef, (0, _reactutilities.getReactElementRef)(child), safeZoneHandlerRef),
|
|
onMouseEnter: (0, _reactutilities.useEventCallback)((_child_props_onMouseEnter = child === null || child === void 0 ? void 0 : child.props.onMouseEnter) !== null && _child_props_onMouseEnter !== void 0 ? _child_props_onMouseEnter : noop),
|
|
onMouseLeave: (0, _reactutilities.useEventCallback)((0, _reactutilities.mergeCallbacks)(child === null || child === void 0 ? void 0 : child.props.onMouseLeave, onMouseLeave)),
|
|
onContextMenu: (0, _reactutilities.useEventCallback)((0, _reactutilities.mergeCallbacks)(child === null || child === void 0 ? void 0 : child.props.onContextMenu, onContextMenu)),
|
|
onMouseMove: (0, _reactutilities.useEventCallback)((0, _reactutilities.mergeCallbacks)(child === null || child === void 0 ? void 0 : child.props.onMouseMove, onMouseMove)),
|
|
onMouseOver: (0, _reactutilities.useEventCallback)((0, _reactutilities.mergeCallbacks)(child === null || child === void 0 ? void 0 : child.props.onMouseOver, onMouseOver))
|
|
};
|
|
const triggerChildProps = {
|
|
'aria-haspopup': 'menu',
|
|
'aria-expanded': !open && !isSubmenu ? undefined : open,
|
|
...contextMenuProps,
|
|
onClick: (0, _reactutilities.useEventCallback)((0, _reactutilities.mergeCallbacks)(child === null || child === void 0 ? void 0 : child.props.onClick, onClick)),
|
|
onKeyDown: (0, _reactutilities.useEventCallback)((0, _reactutilities.mergeCallbacks)(child === null || child === void 0 ? void 0 : child.props.onKeyDown, onKeyDown))
|
|
};
|
|
const ariaButtonTriggerChildProps = (0, _reactaria.useARIAButtonProps)((child === null || child === void 0 ? void 0 : child.type) === 'button' || (child === null || child === void 0 ? void 0 : child.type) === 'a' ? child.type : 'div', triggerChildProps);
|
|
return {
|
|
isSubmenu,
|
|
children: (0, _reactutilities.applyTriggerPropsToChildren)(children, openOnContext ? contextMenuProps : disableButtonEnhancement ? triggerChildProps : ariaButtonTriggerChildProps)
|
|
};
|
|
};
|
|
const isTargetDisabled = (event)=>{
|
|
const isDisabled = (el)=>el.hasAttribute('disabled') || el.hasAttribute('aria-disabled') && el.getAttribute('aria-disabled') === 'true';
|
|
if ((0, _reactutilities.isHTMLElement)(event.target) && isDisabled(event.target)) {
|
|
return true;
|
|
}
|
|
return (0, _reactutilities.isHTMLElement)(event.currentTarget) && isDisabled(event.currentTarget);
|
|
};
|