UNPKG

@itwin/itwinui-react

Version:

A react component library for iTwinUI

251 lines (250 loc) 7.13 kB
'use strict'; 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);