UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

202 lines (192 loc) 7.05 kB
/** * Copyright IBM Corp. 2020, 2025 * * 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 { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js'; import React__default, { useState, useRef, useEffect } from 'react'; import PropTypes from '../../_virtual/index.js'; import cx from 'classnames'; import { useResizeObserver } from '../../global/js/hooks/useResizeObserver.js'; import { usePrefix, ButtonSet, Button, MenuButton, MenuItem } from '@carbon/react'; import { pkg } from '../../settings.js'; import { prepareProps } from '../../global/js/utils/props-helper.js'; const blockClass = `${pkg.prefix}--button-set-with-overflow`; const componentName = 'ButtonSetWithOverflow'; const buttonSize = 'md'; const ButtonSetWithOverflow = _ref => { let { buttons, className, onWidthChange, buttonSetOverflowLabel, menuOptionsClass, rightAlign } = _ref; const carbonPrefix = usePrefix(); const [showAsOverflow, setShowAsOverflow] = useState(false); const spaceAvailableRef = useRef(null); const sizingContainerRefSet = useRef(null); const sizingContainerRefCombo = useRef(null); const sizes = useRef({}); /** * checkFullyVisibleItems determines display count based on space available and width of pageActions * * ButtonSetWithOverflow switches between a Carbon ButtonSet and use of the ButtonMenu component depending * on the space available. While there is sufficient space to show all of the buttons side by side the * ButtonSet is used, once this is no longer the case it switches to a ButtonMenu. * */ const checkFullyVisibleItems = () => { const spaceAvailable = spaceAvailableRef.current?.offsetWidth; let newShowAsOverflow = true; // get all of the hidden sizing buttons const sizingSet = sizingContainerRefSet.current?.querySelectorAll(`.${carbonPrefix}--btn`); // calculate total width of button set let sizingSetTotalSize = 0; for (let item of sizingSet) { sizingSetTotalSize += item.offsetWidth; } // check ButtonMenu size const sizingComboSize = sizingContainerRefCombo.current?.offsetWidth; if (onWidthChange && (sizes.current.minWidth !== sizingComboSize || sizes.current.maxWidth !== sizingSetTotalSize)) { sizes.current.minWidth = sizingComboSize; sizes.current.maxWidth = sizingSetTotalSize; // report min and max width required to host onWidthChange({ ...sizes.current }); } // only if space available use ButtonSet. if (sizingSetTotalSize <= spaceAvailable) { newShowAsOverflow = false; } setShowAsOverflow(newShowAsOverflow); }; useEffect(() => { checkFullyVisibleItems(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [buttons]); const AButtonSet = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => { let { isHidden = false, buttons, ...rest } = _ref2; return /*#__PURE__*/React__default.createElement(ButtonSet, _extends({}, rest, { ref: ref }), buttons.map(_ref3 => { let { label, key, kind, id, ...other } = _ref3; const kindFix = kind === 'default' ? 'primary' : kind; return /*#__PURE__*/React__default.createElement(Button, _extends({}, other, { key: key && `button-set-${key}`, size: buttonSize, type: "button", kind: kindFix, id: id ? isHidden ? `${id}--hidden` : id : null }), label); })); }); AButtonSet.propTypes = { /** * isHidden - Used to conditionally change id if one is passed to the `AButtonSet` component * in order to avoid duplicate ids between the visible and hidden button set buttons */ isHidden: PropTypes.bool }; const AButtonMenu = /*#__PURE__*/React__default.forwardRef((_ref4, ref) => { let { buttons, ...rest } = _ref4; return /*#__PURE__*/React__default.createElement(MenuButton, _extends({}, rest, { ref: ref, label: buttonSetOverflowLabel }), buttons.map(_ref5 => { let { key, kind, ...other } = _ref5; // menu items only come in default and danger flavors const kindFix = kind === 'danger' ? 'danger' : 'default'; return /*#__PURE__*/React__default.createElement(MenuItem, _extends({}, prepareProps(other, ['iconDescription', 'renderIcon']), { key: key && `button-menu-${key}`, kind: kindFix })); }).reverse()); }); useResizeObserver(sizingContainerRefSet, checkFullyVisibleItems); useResizeObserver(sizingContainerRefCombo, checkFullyVisibleItems); useResizeObserver(spaceAvailableRef, checkFullyVisibleItems); return /*#__PURE__*/React__default.createElement("div", { className: cx([blockClass, className, { [`${blockClass}--right`]: rightAlign }]), ref: spaceAvailableRef }, /*#__PURE__*/React__default.createElement("div", { className: `${blockClass}__button-container ${blockClass}__button-container--hidden` }, /*#__PURE__*/React__default.createElement(AButtonSet, { "aria-hidden": true, ref: sizingContainerRefSet, size: buttonSize, buttons: buttons, isHidden: true })), /*#__PURE__*/React__default.createElement("div", { className: `${blockClass}__button-container ${blockClass}__button-container--hidden`, "aria-hidden": true }, /*#__PURE__*/React__default.createElement(AButtonMenu, { className: menuOptionsClass, ref: sizingContainerRefCombo, buttons: buttons, size: buttonSize })), /*#__PURE__*/React__default.createElement("div", { className: `${blockClass}__button-container ${blockClass}__button-container--visible` }, showAsOverflow ? /*#__PURE__*/React__default.createElement(AButtonMenu, { buttons: buttons, size: buttonSize, className: menuOptionsClass }) : /*#__PURE__*/React__default.createElement(AButtonSet, { className: `${blockClass}__button-container`, size: buttonSize, buttons: buttons }))); }; ButtonSetWithOverflow.propTypes = { /** * buttonSetOverflowLabel - used when button set is shown as combo button */ buttonSetOverflowLabel: PropTypes.node.isRequired, /** * Specifies the buttons for the ButtonSetWithOverflow. Each item is specified as an object * with the properties of a Carbon Button plus a label. * * Carbon Button API https://react.carbondesignsystem.com/?path=/docs/components-button--default#component-api */ buttons: PropTypes.array.isRequired, /** * className */ className: PropTypes.string, /** * class name applied to the overflow options */ menuOptionsClass: PropTypes.string, /** * onResize reports maxSize on resize */ onWidthChange: PropTypes.func, /** * align buttons to right of available space */ rightAlign: PropTypes.bool }; ButtonSetWithOverflow.displayName = componentName; export { ButtonSetWithOverflow };