UNPKG

@carbon/ibm-products

Version:
256 lines (254 loc) 11.2 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 { pkg } from "../../settings.js"; import { CardHeader } from "./CardHeader.js"; import { CardFooter } from "./CardFooter.js"; import React, { forwardRef } from "react"; import PropTypes from "prop-types"; import { Button, IconButton, Layer, MenuItem, OverflowMenu, Section, unstable_FeatureFlags } from "@carbon/react"; import { CheckmarkOutline, Incomplete } from "@carbon/react/icons"; //#region src/components/Card/Card.tsx var import_classnames = /* @__PURE__ */ __toESM(require_classnames()); const componentName = "Card"; const Card = forwardRef(({ actionIcons = Object.freeze([]), actionsPlacement = "bottom", decorator, metadata = Object.freeze([]), children, className, clickZone = "one", description, disabled, footerActionIcon, getStarted, label, media, mediaPosition = "top", onClick, onKeyDown, onPrimaryButtonClick, onSecondaryButtonClick, overflowActions = Object.freeze([]), overflowAriaLabel, pictogram: Pictogram, primaryButtonDisabled, primaryButtonHref, primaryButtonIcon, primaryButtonKind = "primary", primaryButtonPlacement = "bottom", primaryButtonText, productive = false, secondaryButtonDisabled, secondaryButtonHref, secondaryButtonIcon, secondaryButtonKind = "secondary", secondaryButtonPlacement = "bottom", secondaryButtonText, slug, status, sequence, title, titleSize = "default", iconDescription = "Options", ...rest }, ref) => { const getIcons = () => getStarted ? metadata : actionIcons; const blockClass = `${pkg.prefix}--card`; const hasActions = getIcons().length > 0 || overflowActions.length > 0 || !!primaryButtonText && primaryButtonPlacement === "top"; const hasHeaderActions = hasActions && actionsPlacement === "top"; const hasFooterActions = hasActions && actionsPlacement === "bottom"; const hasFooterButton = !!secondaryButtonText || !!primaryButtonText && primaryButtonPlacement === "bottom"; const hasBottomBar = hasFooterActions || hasFooterButton; const hasClickEvent = !!onClick || !!onKeyDown; const clickableProps = { onClick, onKeyDown, role: "button", tabIndex: 0 }; const getActions = () => { if (overflowActions.length > 0) { const pos = actionsPlacement === "top" ? "bottom-end" : "top-end"; const size = actionsPlacement === "top" ? "sm" : "md"; return /* @__PURE__ */ React.createElement(Layer, { level: 2 }, /* @__PURE__ */ React.createElement(unstable_FeatureFlags, { enableV12Overflowmenu: true }, /* @__PURE__ */ React.createElement(OverflowMenu, { autoAlign: true, menuAlignment: pos, size, label: overflowAriaLabel || iconDescription }, overflowActions.map(({ id, itemText, onKeyDown, onClick, ...rest }) => /* @__PURE__ */ React.createElement(MenuItem, { ...rest, key: id, label: itemText ?? "", onKeyDown, onClick }))))); } return getIcons().map(({ id, icon: Icon, onClick, iconDescription, href: deprecatedHref, link, ...rest }) => { const { href, ...linkProps } = link ?? { href: deprecatedHref }; if (getStarted) return /* @__PURE__ */ React.createElement("span", { key: id, className: `${blockClass}__icon` }, Icon && /* @__PURE__ */ React.createElement(Icon, { "aria-label": iconDescription }), iconDescription); if (productive) return /* @__PURE__ */ React.createElement(Button, { ...rest, key: id, renderIcon: Icon, hasIconOnly: true, onClick, size: actionsPlacement === "top" ? "sm" : "md", iconDescription, kind: "ghost", href, ...linkProps }); if (href) return /* @__PURE__ */ React.createElement("a", { key: id, className: `${blockClass}__icon`, href, onClick, ...linkProps }, Icon && /* @__PURE__ */ React.createElement(Icon, { "aria-label": iconDescription })); return /* @__PURE__ */ React.createElement(IconButton, { key: id, label: iconDescription, className: `${blockClass}__icon`, onClick, kind: "ghost", size: "sm" }, Icon && /* @__PURE__ */ React.createElement(Icon, { "aria-label": iconDescription })); }); }; const getCardProps = () => { const clickable = hasClickEvent && !productive || hasClickEvent && productive && clickZone === "one"; return { ...rest, ref, className: (0, import_classnames.default)(blockClass, { [`${blockClass}__disabled`]: disabled, [`${blockClass}__get-started`]: getStarted, [`${blockClass}__productive`]: productive, [`${blockClass}__clickable`]: clickable, [`${blockClass}__media-left`]: mediaPosition === "left", [`${blockClass}--has-slug`]: !!slug, [`${blockClass}--has-decorator`]: !!decorator && decorator["type"]?.displayName !== "AILabel", [`${blockClass}--has-ai-label`]: !!decorator && decorator["type"]?.displayName === "AILabel" }, className), ...clickable && clickableProps }; }; const getHeaderBodyProps = () => { const clickable = hasClickEvent && clickZone === "two"; return { className: (0, import_classnames.default)(`${blockClass}__header-body-container`, { [`${blockClass}__clickable`]: clickable }), ...clickable && clickableProps }; }; const getHeaderProps = () => ({ actions: actionsPlacement === "top" ? getActions() : "", decorator, noActionIcons: getIcons().length > 0 && actionsPlacement === "top" ? false : true, actionsPlacement, onPrimaryButtonClick, onSecondaryButtonClick, primaryButtonIcon, primaryButtonPlacement, primaryButtonText, primaryButtonDisabled, description, hasActions: hasHeaderActions, inClickableCard: hasClickEvent, label, secondaryButtonDisabled, secondaryButtonHref, secondaryButtonIcon, secondaryButtonPlacement, secondaryButtonText, slug, title, titleSize }); const getBodyProps = () => { const clickable = hasClickEvent && clickZone === "three"; return { className: (0, import_classnames.default)(`${blockClass}__body`, { [`${blockClass}__clickable`]: clickable }), ...clickable && clickableProps }; }; const getFooterProps = () => ({ actions: actionsPlacement === "bottom" ? getActions() : "", actionsPlacement, disabled, footerActionIcon, hasActions: hasFooterActions, hasButton: hasFooterButton, onPrimaryButtonClick, onSecondaryButtonClick, primaryButtonDisabled, primaryButtonHref, primaryButtonIcon, primaryButtonKind, primaryButtonPlacement, primaryButtonText, productive, secondaryButtonDisabled, secondaryButtonHref, secondaryButtonIcon, secondaryButtonKind, secondaryButtonPlacement, secondaryButtonText }); return /* @__PURE__ */ React.createElement(Section, { as: "div", "aria-disabled": disabled, ...getCardProps() }, !getStarted && media && /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__media` }, media), Pictogram && /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__pictogram` }, /* @__PURE__ */ React.createElement(Pictogram, null)), getStarted && sequence && /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__sequence` }, sequence), getStarted && status && /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__status` }, status === "incomplete" && /* @__PURE__ */ React.createElement(Incomplete, null), status === "complete" && /* @__PURE__ */ React.createElement(CheckmarkOutline, null)), /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__content-container` }, /* @__PURE__ */ React.createElement("div", getHeaderBodyProps(), /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__header-wrapper` }, /* @__PURE__ */ React.createElement(CardHeader, getHeaderProps()), /* @__PURE__ */ React.createElement("div", getBodyProps(), children)), getStarted && media && /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__media` }, media)), hasBottomBar && /* @__PURE__ */ React.createElement(CardFooter, getFooterProps()))); }); Card.propTypes = { /**@ts-ignore */ actionIcons: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string, icon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), onKeyDown: PropTypes.func, onClick: PropTypes.func, iconDescription: PropTypes.string, /** * @deprecated please use the `link.href` instead */ href: PropTypes.string, link: PropTypes.shape({ href: PropTypes.string.isRequired }) })), actionsPlacement: PropTypes.oneOf(["top", "bottom"]), children: PropTypes.node, className: PropTypes.string, clickZone: PropTypes.oneOf([ "one", "two", "three" ]), /** * Optional prop that allows you to pass any component. */ decorator: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]), /**@ts-ignore */ description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), disabled: PropTypes.bool, /**@ts-ignore */ footerActionIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), getStarted: PropTypes.bool, label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), media: PropTypes.node, mediaPosition: PropTypes.oneOf(["top", "left"]), /**@ts-ignore */ metadata: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string, icon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), iconDescription: PropTypes.string })), onClick: PropTypes.func, onKeyDown: PropTypes.func, onPrimaryButtonClick: PropTypes.func, onSecondaryButtonClick: PropTypes.func, /**@ts-ignore */ overflowActions: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string, itemText: PropTypes.string, onClick: PropTypes.func, onKeyDown: PropTypes.func })), overflowAriaLabel: PropTypes.string, /**@ts-ignore */ pictogram: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), primaryButtonDisabled: PropTypes.bool, primaryButtonHref: PropTypes.string, /**@ts-ignore */ primaryButtonIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), primaryButtonKind: PropTypes.oneOf(["primary", "ghost"]), primaryButtonPlacement: PropTypes.oneOf(["top", "bottom"]), primaryButtonText: PropTypes.string, productive: PropTypes.bool, secondaryButtonDisabled: PropTypes.bool, secondaryButtonHref: PropTypes.string, /**@ts-ignore */ secondaryButtonIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), secondaryButtonKind: PropTypes.oneOf(["secondary", "ghost"]), secondaryButtonPlacement: PropTypes.oneOf(["top", "bottom"]), secondaryButtonText: PropTypes.string, sequence: PropTypes.number, /** * **Experimental:** For all cases a `Slug` component can be provided. * Clickable tiles only accept a boolean value of true and display a hollow slug. * @deprecated please use the `decorator` prop */ slug: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]), status: PropTypes.oneOf(["complete", "incomplete"]), title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), titleSize: PropTypes.oneOf(["default", "large"]) }; Card.displayName = componentName; //#endregion export { Card };