@carbon/ibm-products
Version:
Carbon for IBM Products
204 lines (193 loc) • 7.15 kB
JavaScript
/**
* 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.
*/
'use strict';
var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js');
var React = require('react');
var index = require('../../_virtual/index.js');
var cx = require('classnames');
var useResizeObserver = require('../../global/js/hooks/useResizeObserver.js');
var react = require('@carbon/react');
var settings = require('../../settings.js');
var propsHelper = require('../../global/js/utils/props-helper.js');
const blockClass = `${settings.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 = react.usePrefix();
const [showAsOverflow, setShowAsOverflow] = React.useState(false);
const spaceAvailableRef = React.useRef(null);
const sizingContainerRefSet = React.useRef(null);
const sizingContainerRefCombo = React.useRef(null);
const sizes = React.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);
};
React.useEffect(() => {
checkFullyVisibleItems();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [buttons]);
const AButtonSet = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
let {
isHidden = false,
buttons,
...rest
} = _ref2;
return /*#__PURE__*/React.createElement(react.ButtonSet, _rollupPluginBabelHelpers.extends({}, rest, {
ref: ref
}), buttons.map(_ref3 => {
let {
label,
key,
kind,
id,
...other
} = _ref3;
const kindFix = kind === 'default' ? 'primary' : kind;
return /*#__PURE__*/React.createElement(react.Button, _rollupPluginBabelHelpers.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: index.default.bool
};
const AButtonMenu = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
let {
buttons,
...rest
} = _ref4;
return /*#__PURE__*/React.createElement(react.MenuButton, _rollupPluginBabelHelpers.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.createElement(react.MenuItem, _rollupPluginBabelHelpers.extends({}, propsHelper.prepareProps(other, ['iconDescription', 'renderIcon']), {
key: key && `button-menu-${key}`,
kind: kindFix
}));
}).reverse());
});
useResizeObserver.useResizeObserver(sizingContainerRefSet, checkFullyVisibleItems);
useResizeObserver.useResizeObserver(sizingContainerRefCombo, checkFullyVisibleItems);
useResizeObserver.useResizeObserver(spaceAvailableRef, checkFullyVisibleItems);
return /*#__PURE__*/React.createElement("div", {
className: cx([blockClass, className, {
[`${blockClass}--right`]: rightAlign
}]),
ref: spaceAvailableRef
}, /*#__PURE__*/React.createElement("div", {
className: `${blockClass}__button-container ${blockClass}__button-container--hidden`
}, /*#__PURE__*/React.createElement(AButtonSet, {
"aria-hidden": true,
ref: sizingContainerRefSet,
size: buttonSize,
buttons: buttons,
isHidden: true
})), /*#__PURE__*/React.createElement("div", {
className: `${blockClass}__button-container ${blockClass}__button-container--hidden`,
"aria-hidden": true
}, /*#__PURE__*/React.createElement(AButtonMenu, {
className: menuOptionsClass,
ref: sizingContainerRefCombo,
buttons: buttons,
size: buttonSize
})), /*#__PURE__*/React.createElement("div", {
className: `${blockClass}__button-container ${blockClass}__button-container--visible`
}, showAsOverflow ? /*#__PURE__*/React.createElement(AButtonMenu, {
buttons: buttons,
size: buttonSize,
className: menuOptionsClass
}) : /*#__PURE__*/React.createElement(AButtonSet, {
className: `${blockClass}__button-container`,
size: buttonSize,
buttons: buttons
})));
};
ButtonSetWithOverflow.propTypes = {
/**
* buttonSetOverflowLabel - used when button set is shown as combo button
*/
buttonSetOverflowLabel: index.default.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: index.default.array.isRequired,
/**
* className
*/
className: index.default.string,
/**
* class name applied to the overflow options
*/
menuOptionsClass: index.default.string,
/**
* onResize reports maxSize on resize
*/
onWidthChange: index.default.func,
/**
* align buttons to right of available space
*/
rightAlign: index.default.bool
};
ButtonSetWithOverflow.displayName = componentName;
exports.ButtonSetWithOverflow = ButtonSetWithOverflow;