@carbon/ibm-products
Version:
Carbon for IBM Products
156 lines (154 loc) • 6.17 kB
JavaScript
/**
* Copyright IBM Corp. 2020, 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 { __toESM } from "../../_virtual/_rolldown/runtime.js";
import { require_classnames } from "../../node_modules/classnames/index.js";
import { pkg } from "../../settings.js";
import { prepareProps } from "../../global/js/utils/props-helper.js";
import { useResizeObserver } from "../../global/js/hooks/useResizeObserver.js";
import React, { useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import { Button, ButtonSet, MenuButton, MenuItem, usePrefix } from "@carbon/react";
//#region src/components/ButtonSetWithOverflow/ButtonSetWithOverflow.jsx
var import_classnames = /* @__PURE__ */ __toESM(require_classnames());
const blockClass = `${pkg.prefix}--button-set-with-overflow`;
const componentName = "ButtonSetWithOverflow";
const buttonSize = "md";
const ButtonSetWithOverflow = ({ buttons, className, onWidthChange, buttonSetOverflowLabel, menuOptionsClass, rightAlign }) => {
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;
const sizingSet = sizingContainerRefSet.current?.querySelectorAll(`.${carbonPrefix}--btn`);
let sizingSetTotalSize = 0;
for (let item of sizingSet) sizingSetTotalSize += item.offsetWidth;
const sizingComboSize = sizingContainerRefCombo.current?.offsetWidth;
if (onWidthChange && (sizes.current.minWidth !== sizingComboSize || sizes.current.maxWidth !== sizingSetTotalSize)) {
sizes.current.minWidth = sizingComboSize;
sizes.current.maxWidth = sizingSetTotalSize;
onWidthChange({ ...sizes.current });
}
if (sizingSetTotalSize <= spaceAvailable) newShowAsOverflow = false;
setShowAsOverflow(newShowAsOverflow);
};
useEffect(() => {
checkFullyVisibleItems();
}, [buttons]);
const AButtonSet = React.forwardRef(({ isHidden = false, buttons, ...rest }, ref) => {
return /* @__PURE__ */ React.createElement(ButtonSet, {
...rest,
ref
}, buttons.map(({ label, key, kind, id, ...other }) => {
const kindFix = kind === "default" ? "primary" : kind;
return /* @__PURE__ */ React.createElement(Button, {
...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 = React.forwardRef(({ buttons, ...rest }, ref) => {
return /* @__PURE__ */ React.createElement(MenuButton, {
...rest,
ref,
label: buttonSetOverflowLabel
}, buttons.map(({ key, kind, ...other }) => {
const kindFix = kind === "danger" ? "danger" : "default";
return /* @__PURE__ */ React.createElement(MenuItem, {
...prepareProps(other, ["iconDescription", "renderIcon"]),
key: key && `button-menu-${key}`,
kind: kindFix
});
}).reverse());
});
useResizeObserver(sizingContainerRefSet, checkFullyVisibleItems);
useResizeObserver(sizingContainerRefCombo, checkFullyVisibleItems);
useResizeObserver(spaceAvailableRef, checkFullyVisibleItems);
return /* @__PURE__ */ React.createElement("div", {
className: (0, import_classnames.default)([
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,
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,
size: buttonSize
})), /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__button-container ${blockClass}__button-container--visible` }, showAsOverflow ? /* @__PURE__ */ React.createElement(AButtonMenu, {
buttons,
size: buttonSize,
className: menuOptionsClass
}) : /* @__PURE__ */ React.createElement(AButtonSet, {
className: `${blockClass}__button-container`,
size: buttonSize,
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;
//#endregion
export { ButtonSetWithOverflow };