@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
102 lines (99 loc) • 4.27 kB
JavaScript
'use client';
import { useNativeButton } from "../hooks/useNativeButton.mjs";
import { placementMap } from "../utils/placement.mjs";
import { CLASSNAMES } from "../styles/classNames.mjs";
import { styles } from "../Menu/sharedStyle.mjs";
import { usePortalContainer } from "../hooks/usePortalContainer.mjs";
import { renderDropdownMenuItems } from "./renderItems.mjs";
import { cloneElement, isValidElement, memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
import { cx } from "antd-style";
import { mergeProps } from "@base-ui/react/merge-props";
import { Menu } from "@base-ui/react/menu";
import clsx from "clsx";
//#region src/DropdownMenu/DropdownMenu.tsx
const DROPDOWN_MENU_CONTAINER_ATTR = "data-lobe-ui-dropdown-menu-container";
const DropdownMenu = memo(({ children, defaultOpen, items, nativeButton, onOpenChange, onOpenChangeComplete, open, placement = "bottomLeft", popupProps, portalProps, positionerProps, triggerProps, ...rest }) => {
const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen));
useEffect(() => {
if (open === void 0) return;
setUncontrolledOpen(open);
}, [open]);
const handleOpenChange = useCallback((nextOpen, details) => {
onOpenChange?.(nextOpen, details);
if (open === void 0) setUncontrolledOpen(nextOpen);
}, [onOpenChange, open]);
const menuItemsRef = useRef(null);
const isOpen = open ?? uncontrolledOpen;
const menuItems = useMemo(() => {
if (isOpen) {
const renderedItems = renderDropdownMenuItems(typeof items === "function" ? items() : items);
menuItemsRef.current = renderedItems;
return renderedItems;
}
return menuItemsRef.current;
}, [isOpen, items]);
const handleOpenChangeComplete = useCallback((nextOpen) => {
onOpenChangeComplete?.(nextOpen);
if (!nextOpen) menuItemsRef.current = null;
}, [onOpenChangeComplete]);
const portalContainer = usePortalContainer(DROPDOWN_MENU_CONTAINER_ATTR);
const placementConfig = placementMap[placement];
const hoverTrigger = Boolean(triggerProps?.openOnHover);
const { isNativeButtonTriggerElement, resolvedNativeButton } = useNativeButton({
children,
nativeButton,
triggerNativeButton: triggerProps?.nativeButton
});
const renderer = useCallback((props) => {
const resolvedProps = (() => {
if (isNativeButtonTriggerElement) return props;
const { type, ...restProps } = props;
return restProps;
})();
return cloneElement(children, mergeProps(children.props, resolvedProps));
}, [children, isNativeButtonTriggerElement]);
const trigger = isValidElement(children) ? /* @__PURE__ */ jsx(Menu.Trigger, {
...triggerProps,
className: clsx(CLASSNAMES.DropdownMenuTrigger, triggerProps?.className),
nativeButton: resolvedNativeButton,
render: renderer
}) : /* @__PURE__ */ jsx(Menu.Trigger, {
...triggerProps,
className: clsx(CLASSNAMES.DropdownMenuTrigger, triggerProps?.className),
children
});
const resolvedPositionerProps = {
...positionerProps,
align: positionerProps?.align ?? placementConfig?.align ?? "center",
side: positionerProps?.side ?? placementConfig?.side ?? "bottom",
sideOffset: positionerProps?.sideOffset ?? 6
};
return /* @__PURE__ */ jsxs(Menu.Root, {
...rest,
defaultOpen,
onOpenChange: handleOpenChange,
onOpenChangeComplete: handleOpenChangeComplete,
open,
children: [trigger, /* @__PURE__ */ jsx(Menu.Portal, {
container: portalProps?.container ?? portalContainer,
...portalProps,
children: /* @__PURE__ */ jsx(Menu.Positioner, {
...resolvedPositionerProps,
className: (state) => cx(styles.positioner, typeof positionerProps?.className === "function" ? positionerProps.className(state) : positionerProps?.className),
"data-hover-trigger": hoverTrigger || void 0,
"data-placement": placement,
children: /* @__PURE__ */ jsx(Menu.Popup, {
...popupProps,
className: (state) => cx(styles.popup, typeof popupProps?.className === "function" ? popupProps.className(state) : popupProps?.className),
children: menuItems
})
})
})]
});
});
DropdownMenu.displayName = "DropdownMenuV2";
var DropdownMenu_default = DropdownMenu;
//#endregion
export { DropdownMenu_default as default };
//# sourceMappingURL=DropdownMenu.mjs.map