UNPKG

@carbon/react

Version:

React components for the Carbon Design System

144 lines (142 loc) 4.57 kB
/** * Copyright IBM Corp. 2016, 2026 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { usePrefix } from "../../internal/usePrefix.js"; import useIsomorphicEffect from "../../internal/useIsomorphicEffect.js"; import { useId } from "../../internal/useId.js"; import { useFeatureFlag } from "../FeatureFlags/index.js"; import Button_default from "../Button/index.js"; import { mergeRefs } from "../../tools/mergeRefs.js"; import { Menu as Menu$1 } from "../Menu/Menu.js"; import { useAttachedMenu } from "../../internal/useAttachedMenu.js"; import classNames from "classnames"; import { forwardRef, useRef } from "react"; import PropTypes from "prop-types"; import { jsx, jsxs } from "react/jsx-runtime"; import { ChevronDown } from "@carbon/icons-react"; import { autoUpdate, flip, size, useFloating } from "@floating-ui/react"; //#region src/components/MenuButton/index.tsx /** * Copyright IBM Corp. 2023, 2026 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ const validButtonKinds = [ "primary", "tertiary", "ghost" ]; const defaultButtonKind = "primary"; const MenuButton = forwardRef(({ children, className, disabled, kind = defaultButtonKind, label, menuBackgroundToken = "layer", menuBorder = false, size: size$1 = "lg", menuAlignment = "bottom", tabIndex = 0, menuTarget, ...rest }, forwardRef) => { const enableOnlyFloatingStyles = useFeatureFlag("enable-v12-dynamic-floating-styles"); const id = useId("MenuButton"); const prefix = usePrefix(); const triggerRef = useRef(null); let middlewares = []; if (!enableOnlyFloatingStyles) middlewares = [flip({ crossAxis: false })]; if (menuAlignment === "bottom" || menuAlignment === "top") middlewares.push(size({ apply({ rects, elements }) { Object.assign(elements.floating.style, { width: `${rects.reference.width}px` }); } })); const { refs, floatingStyles, placement, middlewareData } = useFloating({ placement: menuAlignment, strategy: "fixed", transform: false, middleware: middlewares, whileElementsMounted: autoUpdate }); const ref = mergeRefs(forwardRef, triggerRef); const { open, handleClick: hookOnClick, handleMousedown, handleClose } = useAttachedMenu(triggerRef); useIsomorphicEffect(() => { Object.keys(floatingStyles).forEach((style) => { if (refs.floating.current) { let value = floatingStyles[style]; if ([ "top", "right", "bottom", "left" ].includes(style) && Number(value)) value += "px"; refs.floating.current.style[style] = value; } }); }, [ floatingStyles, refs.floating, middlewareData, placement, open ]); function handleClick() { if (triggerRef.current) hookOnClick(); } const containerClasses = classNames(`${prefix}--menu-button__container`, className); const triggerClasses = classNames(`${prefix}--menu-button__trigger`, { [`${prefix}--menu-button__trigger--open`]: open }); const menuClasses = classNames(`${prefix}--menu-button__${menuAlignment}`); return /* @__PURE__ */ jsxs("div", { ...rest, ref, "aria-owns": open ? id : void 0, className: containerClasses, children: [/* @__PURE__ */ jsx(Button_default, { ref: refs.setReference, className: triggerClasses, size: size$1, tabIndex, kind, renderIcon: ChevronDown, disabled, "aria-haspopup": true, "aria-expanded": open, onClick: handleClick, onMouseDown: handleMousedown, "aria-controls": open ? id : void 0, children: label }), /* @__PURE__ */ jsx(Menu$1, { containerRef: triggerRef, menuAlignment, className: menuClasses, ref: refs.setFloating, id, legacyAutoalign: false, label, size: size$1, open, onClose: handleClose, target: menuTarget, backgroundToken: menuBackgroundToken, border: menuBorder, children })] }); }); MenuButton.propTypes = { children: PropTypes.node.isRequired, className: PropTypes.string, disabled: PropTypes.bool, kind: PropTypes.oneOf(validButtonKinds), label: PropTypes.string.isRequired, menuAlignment: PropTypes.oneOf([ "top", "top-start", "top-end", "bottom", "bottom-start", "bottom-end" ]), size: PropTypes.oneOf([ "xs", "sm", "md", "lg" ]), tabIndex: PropTypes.number, menuBackgroundToken: PropTypes.oneOf(["layer", "background"]), menuBorder: PropTypes.bool, menuTarget: PropTypes.instanceOf(typeof Element !== "undefined" ? Element : Object) }; //#endregion export { MenuButton };