@itwin/itwinui-react
Version:
A react component library for iTwinUI
251 lines (250 loc) • 7.13 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', {
value: true,
});
function _export(target, all) {
for (var name in all)
Object.defineProperty(target, name, {
enumerable: true,
get: all[name],
});
}
_export(exports, {
Menu: function () {
return Menu;
},
MenuContext: function () {
return MenuContext;
},
MenuPortalContext: function () {
return MenuPortalContext;
},
});
const _interop_require_default = require('@swc/helpers/_/_interop_require_default');
const _interop_require_wildcard = require('@swc/helpers/_/_interop_require_wildcard');
const _react = /*#__PURE__*/ _interop_require_wildcard._(require('react'));
const _classnames = /*#__PURE__*/ _interop_require_default._(
require('classnames'),
);
const _index = require('../../utils/index.js');
const _Popover = require('../Popover/Popover.js');
const _react1 = require('@floating-ui/react');
const Menu = _react.forwardRef((props, ref) => {
let {
className,
trigger,
positionReference,
portal: portalProp,
popoverProps: popoverPropsProp,
children,
...rest
} = props;
let menuPortalContext = _react.useContext(MenuPortalContext);
let portal = portalProp ?? menuPortalContext;
let tree = (0, _react1.useFloatingTree)();
let nodeId = (0, _react1.useFloatingNodeId)();
let parentId = (0, _react1.useFloatingParentNodeId)();
let {
interactions: interactionsProp,
visible: visibleProp,
onVisibleChange: onVisibleChangeProp,
...restPopoverProps
} = popoverPropsProp ?? {};
let {
listNavigation: listNavigationPropsProp,
hover: hoverProp,
...restInteractionsProps
} = interactionsProp ?? {};
let [visible, setVisible] = (0, _index.useControlledState)(
false,
visibleProp,
onVisibleChangeProp,
);
let [hasFocusedNodeInSubmenu, setHasFocusedNodeInSubmenu] =
_react.useState(false);
let [menuElement, setMenuElement] = _react.useState(null);
let { focusableElementsRef, focusableElements } = (0,
_index.useFocusableElements)(menuElement, {
filter: (allElements) =>
allElements.filter(
(i) => !allElements?.some((p) => p.contains(i.parentElement)),
),
});
let [activeIndex, setActiveIndex] = _react.useState(null);
let popover = (0, _Popover.usePopover)({
nodeId,
visible,
onVisibleChange: (open) => (open ? setVisible(true) : close()),
interactions: {
hover:
null == tree
? hoverProp
: {
enabled: !!hoverProp && !hasFocusedNodeInSubmenu,
...hoverProp,
},
...restInteractionsProps,
},
...restPopoverProps,
middleware: {
size: {
maxHeight: 'var(--iui-menu-max-height)',
},
...restPopoverProps.middleware,
},
});
let { getReferenceProps, getFloatingProps, getItemProps } = (0,
_react1.useInteractions)([
(0, _react1.useListNavigation)(popover.context, {
activeIndex,
focusItemOnHover: false,
listRef: focusableElementsRef,
onNavigate: setActiveIndex,
...listNavigationPropsProp,
}),
]);
_react.useEffect(() => {
if (void 0 !== positionReference)
popover.refs.setPositionReference(positionReference);
}, [popover.refs, positionReference]);
let refs = (0, _index.useMergedRefs)(
setMenuElement,
ref,
popover.refs.setFloating,
);
let triggerRef = _react.useRef(null);
let close = _react.useCallback(() => {
setVisible(false);
if (null == parentId)
triggerRef.current?.focus({
preventScroll: true,
});
}, [parentId, setVisible]);
(0, _index.useSyncExternalStore)(
_react.useCallback(() => {
let closeUnrelatedMenus = (event) => {
if (
(parentId === event.parentId && nodeId !== event.nodeId) ||
parentId === event.nodeId
) {
setVisible(false);
setHasFocusedNodeInSubmenu(false);
}
};
tree?.events.on('onNodeFocused', closeUnrelatedMenus);
return () => {
tree?.events.off('onNodeFocused', closeUnrelatedMenus);
};
}, [nodeId, parentId, tree?.events, setVisible]),
() => void 0,
() => void 0,
);
let popoverGetItemProps = _react.useCallback(
({ focusableItemIndex, userProps }) =>
getItemProps({
...userProps,
tabIndex:
null != activeIndex &&
activeIndex >= 0 &&
null != focusableItemIndex &&
focusableItemIndex >= 0 &&
activeIndex === focusableItemIndex
? 0
: -1,
onFocus: (0, _index.mergeEventHandlers)(userProps?.onFocus, () => {
queueMicrotask(() => {
setHasFocusedNodeInSubmenu(true);
});
tree?.events.emit('onNodeFocused', {
nodeId: nodeId,
parentId: parentId,
});
}),
onMouseEnter: (0, _index.mergeEventHandlers)(
userProps?.onMouseEnter,
(event) => {
if (null != focusableItemIndex && focusableItemIndex >= 0)
setActiveIndex(focusableItemIndex);
if (event.target === event.currentTarget)
event.currentTarget.focus({
focusVisible: false,
});
},
),
}),
[activeIndex, getItemProps, nodeId, parentId, tree?.events],
);
let reference = (0, _index.cloneElementWithRef)(trigger, (triggerChild) =>
getReferenceProps(
popover.getReferenceProps({
'aria-haspopup': 'menu',
...triggerChild.props,
'aria-expanded': popover.open,
ref: (0, _index.mergeRefs)(triggerRef, popover.refs.setReference),
}),
),
);
let floating =
popover.open &&
_react.createElement(
_index.Portal,
{
portal: portal,
},
_react.createElement(
_index.Box,
{
as: 'div',
className: (0, _classnames.default)('iui-menu', className),
ref: refs,
...getFloatingProps(
popover.getFloatingProps({
role: 'menu',
...rest,
}),
),
},
children,
),
);
return _react.createElement(
_react.Fragment,
null,
_react.createElement(
MenuContext.Provider,
{
value: _react.useMemo(
() => ({
popoverGetItemProps,
focusableElements,
}),
[focusableElements, popoverGetItemProps],
),
},
_react.createElement(
MenuPortalContext.Provider,
{
value: portal,
},
_react.createElement(
_Popover.PopoverOpenContext.Provider,
{
value: popover.open,
},
reference,
),
null != tree
? _react.createElement(
_react1.FloatingNode,
{
id: nodeId,
},
floating,
)
: floating,
),
),
);
});
const MenuContext = _react.createContext(void 0);
const MenuPortalContext = _react.createContext(void 0);