UNPKG

@carbon/ibm-products

Version:
198 lines (196 loc) 8.49 kB
/** * 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 pconsole_default from "../../global/js/utils/pconsole.js"; import { pkg } from "../../settings.js"; import { allPropTypes, prepareProps } from "../../global/js/utils/props-helper.js"; import React from "react"; import PropTypes from "prop-types"; import { Button, ButtonSet, InlineLoading } from "@carbon/react"; //#region src/components/ActionSet/ActionSet.tsx /** * Copyright IBM Corp. 2021, 2024 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ var import_classnames = /* @__PURE__ */ __toESM(require_classnames()); const blockClass = `${pkg.prefix}--action-set`; const componentName = "ActionSet"; const ActionSetButton = React.forwardRef(({ className, disabled, kind, label, loading, isExpressive = true, ...rest }, ref) => /* @__PURE__ */ React.createElement(Button, { ...rest, isExpressive, className: (0, import_classnames.default)(className, [`${blockClass}__action-button`, { [`${blockClass}__action-button--ghost`]: kind === "ghost" || kind === "danger--ghost", [`${blockClass}__action-button--expressive`]: isExpressive }]), disabled: disabled || loading || false, kind, ref }, label, loading && /* @__PURE__ */ React.createElement(InlineLoading, null))); ActionSetButton.displayName = "ActionSetButton"; ActionSetButton.propTypes = { /**@ts-ignore*/ ...Button.PropTypes, kind: PropTypes.oneOf([ "ghost", "danger--ghost", "tertiary", "secondary", "danger", "primary" ]), label: PropTypes.string, loading: PropTypes.bool }; const defaultKind = "primary"; const willStack = (size, numberOfActions) => size === "sm" || size === "md" && numberOfActions > 2; const defaults = { size: "md" }; const validateActionSetProps = ({ actions, size }) => { if (actions && actions.length) { const problems = []; const stacking = willStack(size, actions.length); const countActions = (kind) => actions.filter((action) => (action.kind || defaultKind) === kind).length; const primaryActions = countActions("primary"); const secondaryActions = countActions("secondary"); const tertiaryActions = countActions("tertiary"); const dangerActions = countActions("danger"); const ghostActions = countActions("ghost") + countActions("danger--ghost"); if (stacking && actions.length > 3) problems.push(`you cannot have more than three actions in this size of ${componentName}`); if (actions.length > 4) problems.push(`you cannot have more than four actions in a ${componentName}`); if (primaryActions > 1) problems.push(`you cannot have more than one 'primary' action in a ${componentName}`); if (ghostActions > 1) problems.push(`you cannot have more than one 'ghost' action in a ${componentName}`); if (stacking && actions.length > 1 && ghostActions > 0) problems.push(`you cannot have a 'ghost' button in conjunction with other action types in this size of ${componentName}`); if (actions.length > primaryActions + secondaryActions + tertiaryActions + dangerActions + ghostActions) problems.push(`you can only have 'primary', 'danger', 'secondary', 'tertiary', 'ghost' and 'danger--ghost' buttons in a ${componentName}`); return problems.length > 0 ? pconsole_default.error(`Invalid prop \`actions\` supplied to \`${componentName}\`: ${problems.join(", and ")}.`) : null; } }; /** * An ActionSet presents a set of action buttons, constructed from bundles * of prop values and applying some layout rules. When the size is 'sm' * the buttons are stacked, and should only include primary and secondary * kinds. When the size is 'md' the buttons are stacked if there are three or * more. When the size is 'md' or 'lg', two buttons share the horizontal space. * When the size is 'lg', three or more buttons use a quarter of the available * horizontal space, and if the size is 'xlg' or 'max' the buttons always use * a quarter of the available horizontal space. If there is a ghost button, * it appears at the left side. If there is a primary button it appears at the * right. */ const ActionSet = React.forwardRef((props, ref) => { const { actions, buttonSize, className, disableStacking = false, size = defaults.size, ...rest } = props; validateActionSetProps({ actions, size }); const buttons = actions && actions.slice?.(0) || []; const stacking = disableStacking ? false : willStack(size, buttons.length); const buttonOrder = (kind) => ({ ghost: 1, "danger--ghost": 2, tertiary: 3, danger: 5, primary: 6 })[kind] ?? 4; buttons.sort((action1, action2) => (buttonOrder(action1.kind || defaultKind) - buttonOrder(action2.kind || defaultKind)) * (stacking ? -1 : 1)); return /* @__PURE__ */ React.createElement(ButtonSet, { ...rest, className: (0, import_classnames.default)(blockClass, className, { [`${blockClass}--row-single`]: !stacking && buttons.length === 1, [`${blockClass}--row-double`]: !stacking && buttons.length === 2, [`${blockClass}--row-triple`]: !stacking && buttons.length === 3, [`${blockClass}--row-quadruple`]: !stacking && buttons.length >= 4, [`${blockClass}--stacking`]: stacking }, `${blockClass}--${size}`), ref, role: "presentation", stacked: stacking }, buttons.map((action, index) => { const actionProps = prepareProps(action, ["key"]); return /* @__PURE__ */ React.createElement(ActionSetButton, { key: action.key || index, ...actionProps, size: buttonSize }); })); }); ActionSet.displayName = componentName; /** * A validator function to help validate the actions supplied for a particular * size of component. When the size is sm, or md with three actions, the * buttons will be stacked and a maximum of three buttons is applied with no * ghosts unless the ghost is the only button. Otherwise a maximum of four * buttons with a maximum of one ghost is applied. In either case, a maximum * of one primary button is allowed. * @param sizeFn An optional function which will be passed all the props and * returns the size that the component should be treated as being: if not * provided, a 'size' prop is used to determine the size of the component. * @returns null if the actions meet the requirements, or an Error object with * an explanatory message. */ ActionSet.propTypes = { /** * The action buttons to show. Each action is specified as an * object with optional fields 'label' to supply the button label, 'kind' * to select the button kind (must be 'primary', 'secondary' or 'ghost'), * 'loading' to display a loading indicator, and 'onClick' to receive * notifications when the button is clicked. Additional fields in the object * will be passed to the Button component, and these can include 'disabled', * 'ref', 'className', and any other Button props. Any other fields in the * object will be passed through to the button element as HTML attributes. * * See https://react.carbondesignsystem.com/?path=/docs/components-button--default#component-api */ actions: allPropTypes([PropTypes.arrayOf(PropTypes.shape({ /**@ts-ignore*/ ...Button.propTypes, kind: PropTypes.oneOf([ "ghost", "danger--ghost", "tertiary", "secondary", "danger", "primary" ]), label: PropTypes.string, loading: PropTypes.bool, /**@ts-ignore*/ onClick: Button.propTypes.onClick }))]), /** * The size of buttons to use for the actions. The allowed values are * those for the size prop of carbon Button. If this prop is specified, all * the buttons will be set to this size, overriding any 'size' values (if any) * supplied in the actions array (if any). */ /**@ts-ignore*/ buttonSize: Button.propTypes.size, /** * An optional class or classes to be added to the outermost element. */ className: PropTypes.string, /** * When true, prevents automatic stacking of buttons even when size would * normally trigger stacking (e.g., 'sm' size or 'md' with 3+ actions). * Buttons will remain in a horizontal layout. */ disableStacking: PropTypes.bool, /** * The size of the action set. Different button arrangements are used at * different sizes, to make best use of the available space. */ size: PropTypes.oneOf([ "sm", "md", "lg", "xl", "2xl" ]) }; //#endregion export { ActionSet };