@base-ui-components/react
Version:
Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.
131 lines (130 loc) • 4.59 kB
JavaScript
'use client';
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Menubar = void 0;
var React = _interopRequireWildcard(require("react"));
var _useAnimationFrame = require("@base-ui-components/utils/useAnimationFrame");
var _floatingUiReact = require("../floating-ui-react");
var _MenubarContext = require("./MenubarContext");
var _useScrollLock = require("../utils/useScrollLock");
var _useOpenInteractionType = require("../utils/useOpenInteractionType");
var _CompositeRoot = require("../composite/root/CompositeRoot");
var _useBaseUiId = require("../utils/useBaseUiId");
var _jsxRuntime = require("react/jsx-runtime");
/**
* The container for menus.
*
* Documentation: [Base UI Menubar](https://base-ui.com/react/components/menubar)
*/
const Menubar = exports.Menubar = /*#__PURE__*/React.forwardRef(function Menubar(props, forwardedRef) {
const {
orientation = 'horizontal',
loop = true,
render,
className,
modal = true,
disabled = false,
id: idProp,
...elementProps
} = props;
const [contentElement, setContentElement] = React.useState(null);
const [hasSubmenuOpen, setHasSubmenuOpen] = React.useState(false);
const {
openMethod,
triggerProps: interactionTypeProps,
reset: resetOpenInteractionType
} = (0, _useOpenInteractionType.useOpenInteractionType)(hasSubmenuOpen);
React.useEffect(() => {
if (!hasSubmenuOpen) {
resetOpenInteractionType();
}
}, [hasSubmenuOpen, resetOpenInteractionType]);
(0, _useScrollLock.useScrollLock)({
enabled: modal && hasSubmenuOpen && openMethod !== 'touch',
open: hasSubmenuOpen,
mounted: hasSubmenuOpen,
referenceElement: contentElement
});
const id = (0, _useBaseUiId.useBaseUiId)(idProp);
const state = React.useMemo(() => ({
orientation,
modal
}), [orientation, modal]);
const contentRef = React.useRef(null);
const allowMouseUpTriggerRef = React.useRef(false);
const context = React.useMemo(() => ({
contentElement,
setContentElement,
setHasSubmenuOpen,
hasSubmenuOpen,
modal,
disabled,
orientation,
allowMouseUpTriggerRef,
rootId: id
}), [contentElement, hasSubmenuOpen, modal, disabled, orientation, id]);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_MenubarContext.MenubarContext.Provider, {
value: context,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingUiReact.FloatingTree, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(MenubarContent, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_CompositeRoot.CompositeRoot, {
render: render,
className: className,
state: state,
refs: [forwardedRef, setContentElement, contentRef],
props: [{
role: 'menubar',
id
}, interactionTypeProps, elementProps],
orientation: orientation,
loop: loop,
highlightItemOnHover: hasSubmenuOpen
})
})
})
});
});
if (process.env.NODE_ENV !== "production") Menubar.displayName = "Menubar";
function MenubarContent(props) {
const nodeId = (0, _floatingUiReact.useFloatingNodeId)();
const {
events: menuEvents
} = (0, _floatingUiReact.useFloatingTree)();
const openSubmenusRef = React.useRef(new Set());
const rootContext = (0, _MenubarContext.useMenubarContext)();
React.useEffect(() => {
function onSubmenuOpenChange(details) {
if (!details.nodeId || details.parentNodeId !== nodeId) {
return;
}
if (details.open) {
openSubmenusRef.current.add(details.nodeId);
} else {
openSubmenusRef.current.delete(details.nodeId);
}
const isAnyOpen = openSubmenusRef.current.size > 0;
if (isAnyOpen) {
rootContext.setHasSubmenuOpen(true);
} else if (rootContext.hasSubmenuOpen) {
// wait for the next frame to set the state to make sure another menu doesn't open
// immediately after the previous one is closed
_useAnimationFrame.AnimationFrame.request(() => {
if (openSubmenusRef.current.size === 0) {
rootContext.setHasSubmenuOpen(false);
}
});
}
}
menuEvents.on('menuopenchange', onSubmenuOpenChange);
return () => {
menuEvents.off('menuopenchange', onSubmenuOpenChange);
};
}, [menuEvents, nodeId, rootContext]);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingUiReact.FloatingNode, {
id: nodeId,
children: props.children
});
}
;