@carbon/ibm-products
Version:
Carbon for IBM Products
258 lines (256 loc) • 12.3 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.
*/
const require_runtime = require("../../_virtual/_rolldown/runtime.js");
const require_index = require("../../node_modules/classnames/index.js");
const require_settings = require("../../settings.js");
const require_CardHeader = require("./CardHeader.js");
const require_CardFooter = require("./CardFooter.js");
let react = require("react");
react = require_runtime.__toESM(react);
let prop_types = require("prop-types");
prop_types = require_runtime.__toESM(prop_types);
let _carbon_react = require("@carbon/react");
let _carbon_react_icons = require("@carbon/react/icons");
//#region src/components/Card/Card.tsx
var import_classnames = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
const componentName = "Card";
const Card = (0, react.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 = `${require_settings.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.default.createElement(_carbon_react.Layer, { level: 2 }, /* @__PURE__ */ react.default.createElement(_carbon_react.unstable_FeatureFlags, { enableV12Overflowmenu: true }, /* @__PURE__ */ react.default.createElement(_carbon_react.OverflowMenu, {
autoAlign: true,
menuAlignment: pos,
size,
label: overflowAriaLabel || iconDescription
}, overflowActions.map(({ id, itemText, onKeyDown, onClick, ...rest }) => /* @__PURE__ */ react.default.createElement(_carbon_react.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.default.createElement("span", {
key: id,
className: `${blockClass}__icon`
}, Icon && /* @__PURE__ */ react.default.createElement(Icon, { "aria-label": iconDescription }), iconDescription);
if (productive) return /* @__PURE__ */ react.default.createElement(_carbon_react.Button, {
...rest,
key: id,
renderIcon: Icon,
hasIconOnly: true,
onClick,
size: actionsPlacement === "top" ? "sm" : "md",
iconDescription,
kind: "ghost",
href,
...linkProps
});
if (href) return /* @__PURE__ */ react.default.createElement("a", {
key: id,
className: `${blockClass}__icon`,
href,
onClick,
...linkProps
}, Icon && /* @__PURE__ */ react.default.createElement(Icon, { "aria-label": iconDescription }));
return /* @__PURE__ */ react.default.createElement(_carbon_react.IconButton, {
key: id,
label: iconDescription,
className: `${blockClass}__icon`,
onClick,
kind: "ghost",
size: "sm"
}, Icon && /* @__PURE__ */ react.default.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.default.createElement(_carbon_react.Section, {
as: "div",
"aria-disabled": disabled,
...getCardProps()
}, !getStarted && media && /* @__PURE__ */ react.default.createElement("div", { className: `${blockClass}__media` }, media), Pictogram && /* @__PURE__ */ react.default.createElement("div", { className: `${blockClass}__pictogram` }, /* @__PURE__ */ react.default.createElement(Pictogram, null)), getStarted && sequence && /* @__PURE__ */ react.default.createElement("div", { className: `${blockClass}__sequence` }, sequence), getStarted && status && /* @__PURE__ */ react.default.createElement("div", { className: `${blockClass}__status` }, status === "incomplete" && /* @__PURE__ */ react.default.createElement(_carbon_react_icons.Incomplete, null), status === "complete" && /* @__PURE__ */ react.default.createElement(_carbon_react_icons.CheckmarkOutline, null)), /* @__PURE__ */ react.default.createElement("div", { className: `${blockClass}__content-container` }, /* @__PURE__ */ react.default.createElement("div", getHeaderBodyProps(), /* @__PURE__ */ react.default.createElement("div", { className: `${blockClass}__header-wrapper` }, /* @__PURE__ */ react.default.createElement(require_CardHeader.CardHeader, getHeaderProps()), /* @__PURE__ */ react.default.createElement("div", getBodyProps(), children)), getStarted && media && /* @__PURE__ */ react.default.createElement("div", { className: `${blockClass}__media` }, media)), hasBottomBar && /* @__PURE__ */ react.default.createElement(require_CardFooter.CardFooter, getFooterProps())));
});
Card.propTypes = {
/**@ts-ignore */
actionIcons: prop_types.default.arrayOf(prop_types.default.shape({
id: prop_types.default.string,
icon: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.object]),
onKeyDown: prop_types.default.func,
onClick: prop_types.default.func,
iconDescription: prop_types.default.string,
/**
* @deprecated please use the `link.href` instead
*/
href: prop_types.default.string,
link: prop_types.default.shape({ href: prop_types.default.string.isRequired })
})),
actionsPlacement: prop_types.default.oneOf(["top", "bottom"]),
children: prop_types.default.node,
className: prop_types.default.string,
clickZone: prop_types.default.oneOf([
"one",
"two",
"three"
]),
/**
* Optional prop that allows you to pass any component.
*/
decorator: prop_types.default.oneOfType([prop_types.default.node, prop_types.default.bool]),
/**@ts-ignore */
description: prop_types.default.oneOfType([prop_types.default.string, prop_types.default.node]),
disabled: prop_types.default.bool,
/**@ts-ignore */
footerActionIcon: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.object]),
getStarted: prop_types.default.bool,
label: prop_types.default.oneOfType([prop_types.default.string, prop_types.default.node]),
media: prop_types.default.node,
mediaPosition: prop_types.default.oneOf(["top", "left"]),
/**@ts-ignore */
metadata: prop_types.default.arrayOf(prop_types.default.shape({
id: prop_types.default.string,
icon: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.object]),
iconDescription: prop_types.default.string
})),
onClick: prop_types.default.func,
onKeyDown: prop_types.default.func,
onPrimaryButtonClick: prop_types.default.func,
onSecondaryButtonClick: prop_types.default.func,
/**@ts-ignore */
overflowActions: prop_types.default.arrayOf(prop_types.default.shape({
id: prop_types.default.string,
itemText: prop_types.default.string,
onClick: prop_types.default.func,
onKeyDown: prop_types.default.func
})),
overflowAriaLabel: prop_types.default.string,
/**@ts-ignore */
pictogram: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.object]),
primaryButtonDisabled: prop_types.default.bool,
primaryButtonHref: prop_types.default.string,
/**@ts-ignore */
primaryButtonIcon: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.object]),
primaryButtonKind: prop_types.default.oneOf(["primary", "ghost"]),
primaryButtonPlacement: prop_types.default.oneOf(["top", "bottom"]),
primaryButtonText: prop_types.default.string,
productive: prop_types.default.bool,
secondaryButtonDisabled: prop_types.default.bool,
secondaryButtonHref: prop_types.default.string,
/**@ts-ignore */
secondaryButtonIcon: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.object]),
secondaryButtonKind: prop_types.default.oneOf(["secondary", "ghost"]),
secondaryButtonPlacement: prop_types.default.oneOf(["top", "bottom"]),
secondaryButtonText: prop_types.default.string,
sequence: prop_types.default.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: prop_types.default.oneOfType([prop_types.default.node, prop_types.default.bool]),
status: prop_types.default.oneOf(["complete", "incomplete"]),
title: prop_types.default.oneOfType([prop_types.default.string, prop_types.default.node]),
titleSize: prop_types.default.oneOf(["default", "large"])
};
Card.displayName = componentName;
//#endregion
exports.Card = Card;