UNPKG

@carbon/react

Version:

React components for the Carbon Design System

204 lines (192 loc) 7.91 kB
/** * Copyright IBM Corp. 2016, 2023 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHelpers.js'); var React = require('react'); var PropTypes = require('prop-types'); var cx = require('classnames'); var iconsReact = require('@carbon/icons-react'); var react = require('@floating-ui/react'); var index = require('../../FeatureFlags/index.js'); var index$1 = require('../../IconButton/index.js'); var Menu = require('../../Menu/Menu.js'); require('../../Menu/MenuItem.js'); var mergeRefs = require('../../../tools/mergeRefs.js'); var useId = require('../../../internal/useId.js'); var usePrefix = require('../../../internal/usePrefix.js'); var useAttachedMenu = require('../../../internal/useAttachedMenu.js'); var deprecateValuesWithin = require('../../../prop-types/deprecateValuesWithin.js'); var mapPopoverAlign = require('../../../tools/mapPopoverAlign.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes); var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx); const defaultSize = 'md'; const OverflowMenu = /*#__PURE__*/React__default["default"].forwardRef(function OverflowMenu({ autoAlign = false, children, className, label = 'Options', renderIcon: IconElement = iconsReact.OverflowMenuVertical, size = defaultSize, menuAlignment = 'bottom-start', tooltipAlignment, menuTarget, ...rest }, forwardRef) { const enableFloatingStyles = index.useFeatureFlag('enable-v12-dynamic-floating-styles') || autoAlign; const { refs, floatingStyles, placement, middlewareData } = react.useFloating(enableFloatingStyles ? { // Computing the position starts with initial positioning // via `placement`. placement: menuAlignment, // The floating element is positioned relative to its nearest // containing block (usually the viewport). It will in many cases // also “break” the floating element out of a clipping ancestor. // https://floating-ui.com/docs/misc#clipping strategy: 'fixed', // Middleware are executed as an in-between “middle” step of the // initial `placement` computation and eventual return of data for // rendering. Each middleware is executed in order. middleware: [autoAlign && react.flip({ // An explicit array of placements to try if the initial // `placement` doesn’t fit on the axes in which overflow // is checked. fallbackPlacements: menuAlignment.includes('bottom') ? ['bottom-start', 'bottom-end', 'top-start', 'top-end'] : ['top-start', 'top-end', 'bottom-start', 'bottom-end'] })], whileElementsMounted: react.autoUpdate } : {} // When autoAlign is turned off & the `enable-v12-dynamic-floating-styles` feature flag is not // enabled, floating-ui will not be used ); const id = useId.useId('overflowmenu'); const prefix = usePrefix.usePrefix(); const triggerRef = React.useRef(null); const { open, x, y, handleClick: hookOnClick, handleMousedown, handleClose } = useAttachedMenu.useAttachedMenu(triggerRef); React.useEffect(() => { if (enableFloatingStyles) { Object.keys(floatingStyles).forEach(style => { if (refs.floating.current) { refs.floating.current.style[style] = floatingStyles[style]; } }); } }, [floatingStyles, enableFloatingStyles, refs.floating, open, placement, middlewareData]); function handleTriggerClick() { if (triggerRef.current) { hookOnClick(); } } const containerClasses = cx__default["default"](className, `${prefix}--overflow-menu__container`, { [`${prefix}--autoalign`]: enableFloatingStyles }); const menuClasses = cx__default["default"](`${prefix}--overflow-menu__${menuAlignment}`); const triggerClasses = cx__default["default"](`${prefix}--overflow-menu`, { [`${prefix}--overflow-menu--open`]: open }, size !== defaultSize && `${prefix}--overflow-menu--${size}`); const floatingRef = mergeRefs["default"](triggerRef, refs.setReference); return /*#__PURE__*/React__default["default"].createElement("div", _rollupPluginBabelHelpers["extends"]({}, rest, { className: containerClasses, "aria-owns": open ? id : undefined, ref: forwardRef }), /*#__PURE__*/React__default["default"].createElement(index$1.IconButton, { "aria-controls": open ? id : undefined, "aria-haspopup": true, "aria-expanded": open, className: triggerClasses, onClick: handleTriggerClick, onMouseDown: handleMousedown, ref: floatingRef, label: label, align: tooltipAlignment, kind: "ghost" }, /*#__PURE__*/React__default["default"].createElement(IconElement, { className: `${prefix}--overflow-menu__icon` })), /*#__PURE__*/React__default["default"].createElement(Menu.Menu, { containerRef: triggerRef, ref: refs.setFloating, menuAlignment: menuAlignment, className: menuClasses, id: id, size: size, legacyAutoalign: !enableFloatingStyles, open: open, onClose: handleClose, x: x, y: y, label: label, target: menuTarget }, children)); }); OverflowMenu.propTypes = { /** * **Experimental**: Will attempt to automatically align the floating element to avoid collisions with the viewport and being clipped by ancestor elements. */ autoAlign: PropTypes__default["default"].bool, /** * A collection of MenuItems to be rendered within this OverflowMenu. */ children: PropTypes__default["default"].node, /** * Additional CSS class names for the trigger button. */ className: PropTypes__default["default"].string, /** * A label describing the options available. Is used in the trigger tooltip and as the menu's accessible label. */ label: PropTypes__default["default"].string, /** * Experimental property. Specify how the menu should align with the button element */ menuAlignment: PropTypes__default["default"].oneOf(['top-start', 'top-end', 'bottom-start', 'bottom-end']), /** * A component used to render an icon. */ renderIcon: PropTypes__default["default"].oneOfType([PropTypes__default["default"].func, PropTypes__default["default"].object]), /** * Specify the size of the menu, from a list of available sizes. */ size: PropTypes__default["default"].oneOf(['sm', 'md', 'lg']), /** * Specify how the trigger tooltip should be aligned. */ tooltipAlignment: deprecateValuesWithin["default"](PropTypes__default["default"].oneOf(['top', 'top-left', // deprecated use top-start instead 'top-right', // deprecated use top-end instead 'bottom', 'bottom-left', // deprecated use bottom-start instead 'bottom-right', // deprecated use bottom-end instead 'left', 'left-bottom', // deprecated use left-end instead 'left-top', // deprecated use left-start instead 'right', 'right-bottom', // deprecated use right-end instead 'right-top', // deprecated use right-start instead // new values to match floating-ui 'top-start', 'top-end', 'bottom-start', 'bottom-end', 'left-end', 'left-start', 'right-end', 'right-start']), ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end'], mapPopoverAlign.mapPopoverAlign), /** * Specify a DOM node where the Menu should be rendered in. Defaults to document.body. */ menuTarget: PropTypes__default["default"].instanceOf(typeof Element !== 'undefined' ? Element : Object) }; exports.OverflowMenu = OverflowMenu;