UNPKG

@mskcc/carbon-react

Version:

Carbon react components for the MSKCC DSM

170 lines (161 loc) 6.68 kB
/** * MSKCC 2021, 2024 */ '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 react = require('@floating-ui/react'); var IconButton = require('../../IconButton/IconButton.js'); var Menu = require('../../Menu/Menu.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'); 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(_ref, forwardRef) { var _IconElement; let { autoAlign = false, children, className, label = 'Options', renderIcon: IconElement = () => /*#__PURE__*/React__default["default"].createElement("span", { className: `msk-icon ${prefix}--overflow-menu__icon` }, "vert"), size = defaultSize, menuAlignment = 'bottom-start', tooltipAlignment, ...rest } = _ref; return function () { const { refs, floatingStyles, placement, middlewareData } = react.useFloating(autoAlign ? { // 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: [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, 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 (autoAlign) { Object.keys(floatingStyles).forEach(style => { if (refs.floating.current) { refs.floating.current.style[style] = floatingStyles[style]; } }); } }, [floatingStyles, autoAlign, refs.floating, open, placement, middlewareData]); function handleTriggerClick() { if (triggerRef.current) { hookOnClick(); } } const containerClasses = cx__default["default"](className, `${prefix}--overflow-menu__container`); 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(IconButton.IconButton, { "aria-controls": open ? id : undefined, "aria-haspopup": true, "aria-expanded": open, className: triggerClasses, onClick: handleTriggerClick, onMouseDown: event => handleMousedown(event), ref: floatingRef, label: label, align: tooltipAlignment, kind: "ghost" }, _IconElement || (_IconElement = /*#__PURE__*/React__default["default"].createElement(IconElement, null))), /*#__PURE__*/React__default["default"].createElement(Menu.Menu, { containerRef: triggerRef, ref: refs.setFloating, menuAlignment: menuAlignment, className: menuClasses, id: id, size: size, legacyAutoalign: !autoAlign, open: open, onClose: handleClose, x: x, y: y, label: label }, 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']), /** * Optionally provide a custom icon to be rendered on the trigger button. */ // @ts-expect-error: PropTypes are not expressive enough to cover this case 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: PropTypes__default["default"].oneOf(['top', 'top-left', 'top-right', 'bottom', 'bottom-left', 'bottom-right', 'left', 'right']) }; exports.OverflowMenu = OverflowMenu;